// Buspirate app. // http://techref.massmind.org/techref/language/DroidScript/BusPirate/index.htm // Note: this application only works on devices that support // OTG and allow access to external serial devices. // // Known to work: Nexus7, GalaxyS3/S4, ExperiaZUltra, TescoHudl, Asus MemoPad 8 // Don't work: Nexus4, GalaxyS1, AsusMemo //Global variables. var usb=null, reply=""; var log="", maxLines; var txts="", descs=[], cmds=[], ary; var desc=""; var modes=" HiZ>1-WIRE>UART>I2C>SPI>2WIRE>3WIRE>KEYB>LCD>"; var mode=""; var scaleADC=1; var aryParm=[]; //Called when application is started. function OnStart() { txts = app.LoadText("BusPirateCommands"); if (!txts) { //load default values if none saved. txts = "[select commands]\t\r"; txts +="Help\t?\r"; txts +="Reset\t#\r"; txts +="Show Pin State\tv\r"; txts +="ADC In\td\r"; txts +="ADC Loop (Voltimeter)\tD\r"; txts +="Delay 1uS\t&\r"; txts +="Delay 1mS\t%\r"; txts +="Frequency Counter\tf\r"; txts +="Generate #Khz %Duty\tg\r"; txts +="Control AUX\tc\r"; txts +="Control /CS\tC\r"; txts +="AUX/CS low\ta\r"; txts +="AUX/CS HI\tA\r"; txts +="AUX/CS In\t@\r"; txts +="Power off\tw\r"; txts +="Power ON\tW\r"; txts +="Pullup Off\tp\r"; txts +="Pull Bus to VPullup\tP\r"; txts +="VPullup Pin\te1\r"; txts +="VPullup 3.3v\te2\r"; txts +="VPullup 5v\te3\r"; txts +="HiZ Mode\tm1\r"; txts +="UART Mode\tm3\r"; txts +="I2C Mode"; txts +=" #KHz 1=50 2=100 3=400"; txts +=" \tm4\r"; txts +="SPI Mode"; txts +=" #KHz 1=30 2=125 3=250 4=1000"; txts +=" #Clock_idle 1=low 2=high"; txts +=" #Clock_edge 1=idle->active 2=active->ide"; txts +=" #Phase 1=middle 2=end"; txts +=" #CS 1=high 2=low"; txts +=" #Output 1=open_collecter 2=normal"; txts +=" \tm5\r"; txts +="1-Wire Mode\tm2\r"; txts +="2-Wire Mode"; txts +=" #KHz 1=50 2=50 3=100 4=400"; txts +=" #Output 1=open_collecter 2=normal"; txts +=" \tm6\r"; txts +="3-Wire Mode"; txts +=" #KHz 1=50 2=50 3=100 4=400"; txts +=" #CS 1=high 2=low"; txts +=" #Output 1=open_collecter 2=normal"; txts +=" \tm7\r"; txts +="LCD Mode\tm8\r"; txts +="Bitorder MSB\tl\r"; txts +="Bitorder LSB\tL\r"; txts +="HEX Display\to1\r"; txts +="Decimal Display\to2\r"; txts +="Binary Display\to3\r"; txts +="Raw Data\to4\r"; txts +="Version/Status\ti\r"; txts +="Clock HI\t/\r"; txts +="Clock low\t\\\r"; txts +="Clock Tick\t^\r"; txts +="Data HI\t-\r"; txts +="Data low\t_\r"; txts +="Read bit\t.\r"; txts +="Clock Tick & Read bit\t!\r"; txts +="Read Byte\t\r"; txts +="Bus Start\t[\r"; txts +="Bus Stop\t]\r"; txts +="Bus Start w/Read\t{\r"; txts +="Bus Stop w/Read\t}\r"; txts +="Repeat #times\t:\r"; txts +="Search 7-bit I2C addresses\t(1)\r"; txts +="I2C Sniffer\t(2)\r"; txts +="List User Macros\t<0>\r"; txts +="Record As Macro 1\tMACRO\r"; txts +="Record As Macro 2\tMACRO\r"; txts +="Record As Macro 3\tMACRO\r"; txts +="Record As Macro 4\tMACRO\r"; txts +="Record As Macro 5\tMACRO\r"; txts +="\t\r"; } ary = txts.split("\r"); for (var i = 0; i<ary.length; i++) { txts = ary[i].split("\t"); //seperate description from command cmds[txts[0]]=txts[1]; //associate description with command descs[i]=txts[0]; //build ordered array of descriptions } //Create a layout with objects vertically centered. lay = app.CreateLayout( "linear", "VCenter,FillXY" ); //Create title text. txt = app.CreateText("BusPirate"); txt.SetTextSize( 22 ); txt.SetMargins( 0,0,0,0.01 ); lay.AddChild( txt ); //Create a read-only edit box to show responses. edtReply = app.CreateText( "", 0.96, 0.6, "MultiLine,Left,Monospace" ); edtReply.SetMargins( 0,0,0,0.01 ); edtReply.SetBackColor( "#333333" ); edtReply.SetTextSize( 8 ); // must be 8 to fit pin status // could be larger for most things. but changing size changes line count. lay.AddChild( edtReply ); //Create an edit box containing the constructed commands edt = app.CreateTextEdit( "", 0.96, 0.2, "NoSpell" ); edt.SetOnChange( edt_OnChange ); lay.AddChild( edt ); //Create program spinner. spin = app.CreateSpinner( descs.join(","), 0.8 ); spin.SetOnTouch( spin_OnTouch ); lay.AddChild( spin ); //Create a horizontal layout for buttons. layBut = app.CreateLayout("Linear", "Horizontal"); lay.AddChild( layBut ); //Create an connect button. btnConnect = app.CreateButton( "Connect", 0.23, 0.1 ); btnConnect.SetOnTouch( btnConnect_OnTouch ); layBut.AddChild( btnConnect ); //Create an send button. btnSend = app.CreateButton( "Send", 0.23, 0.1 ); btnSend.SetOnTouch( btnSend_OnTouch ); layBut.AddChild( btnSend ); //Create a reset button. btnReset = app.CreateButton( "Reset", 0.23, 0.1 ); btnReset.SetOnTouch( btnReset_OnTouch ); //btnReset.SetOnLongTouch( btnReset_OnLongTouch ); layBut.AddChild( btnReset ); //Create an save button. btnSave = app.CreateButton( "Save", 0.23, 0.1 ); btnSave.SetOnTouch( btnSave_OnTouch ); layBut.AddChild( btnSave); //Add layout to app. app.AddLayout( lay ); } //Called when user touches connect button. function btnConnect_OnTouch() { //Create USB serial object. usb = app.CreateUSBSerial(); if( !usb ) { app.ShowPopup( "Please connect a USB device" ); return; } usb.SetOnReceive( usb_OnReceive ); Send ("i"); //get version / status to start. app.ShowPopup( "Connected" ); } //Called when user touches send button. function btnSend_OnTouch() { //Get rid of blank lines, spaces etc that cause //a problem for Espruino. var s = edt.GetText(); s = s.replace( RegExp("\n\n+","gim"), "\n" ); s = s.replace( RegExp("\n +","gim"), "\n" ); s = s.replace( RegExp("\\)\\s*\\{","gim"), "\)\{" ); s = s.replace( RegExp(", +","gim"), "," ); s = s.replace( RegExp("\\( +","gim"), "\(" ); s = s.replace( RegExp(" +\\)","gim"), "\)" ); edt.SetText(s); //Send program to Espruino. Send( s ); } //Called when user touches reset button. function btnReset_OnTouch() { edt.SetText( "" ); //clear text edit. spin.SelectItem(descs[0]); //clear pull down so next selection works } function btnReset_OnLongTouch() { app.SaveText("BusPirateCommands",""); //clear the previously stored data } //Called when user touches save button. function btnSave_OnTouch(item) { //need to ask the user for a description for the current command //Create dialog window. dlgTxt = app.CreateDialog( "Name?" ); //Create a layout for dialog. layDlg = app.CreateLayout( "linear", "VCenter,FillXY" ); layDlg.SetPadding( 0.02, 0, 0.02, 0.02 ); dlgTxt.AddLayout( layDlg ); //Create an text edit box. edtDesc = app.CreateTextEdit( "Do: "+edt.GetText(), 0.8, 0.1 ); edtDesc.SetMargins( 0, 0.02, 0, 0 ); layDlg.AddChild( edtDesc ); //Create an ok button. btnOk = app.CreateButton( "Ok", 0.23, 0.1 ); btnOk.SetOnTouch( btnOk_OnTouch ); layDlg.AddChild( btnOk ); //Show dialog. dlgTxt.Show(); } //called when user closes save name dialog function btnOk_OnTouch(item) { var txts=""; desc = edtDesc.GetText(); descs.push(desc); //add new description to array spin.SetList(descs.join(",")); //and to the displayed list cmds[desc]=edt.GetText(); //associate command with description for (var i=0; i<descs.length; i++) { //build a file to save list txts += descs[i]+"\t"+cmds[descs[i]]+"\r"; } //app.SaveText("BusPirateCommands",txts); //and store it. dlgTxt.Hide(); app.ShowPopup("Saved "+desc); } //Called when text entered directly in edit area function edt_OnChange() { if ("\n"==edt.GetText().slice(-1)) { //if they just pressed return edt.SetText( edt.GetText().slice(0,-1) ); //remove the return. btnSend_OnTouch(); //pretend the user pressed send edt.SetCursorPos( edt.GetText().length ); //move cursor to end } } //Called when user touches program spinner. //http://dangerousprototypes.com/docs/Bus_Pirate_menu_options_guide function spin_OnTouch( item ) { num = item.slice( -1 ); var s = item; var parm = findNextParm(s,0); if (parm) { //if there was a parameter marker var name = s.slice(0, parm-1); //text before first parm var parms=""; //units for the parm var aryLay=[]; //an array to hold the lines dlgTxt = app.CreateDialog( name ); //make a dialog layDlg = app.CreateLayout( "linear", "Vertical,Left" ); //overall dlgTxt.AddLayout( layDlg ); while (parm>0) { //for each parmameter s = s.slice(parm); //cut off the prior string parm = findNextParm(s,0); //go for the next one parms = s.slice(0,parm?parm-1:s.length); //to next or end //start a new line aryLay.push(app.CreateLayout( "linear", "Horizontal,Left") ); layDlg.AddChild(aryLay[aryLay.length-1]); if (parms.indexOf("=")>0) {//list of #=option? //Create a spinner to get the unit aryParm.push(app.CreateSpinner(parms.split(" ").slice(1), 0.4, 0.07, "Right" )); } else { //Create an edit box to get the unit aryParm.push(app.CreateTextEdit("", 0.4, 0.07, "Number, Right, SingleLine" )); } aryLay[aryLay.length-1].AddChild(aryParm[aryParm.length-1]); //with the name of the unit aryLay[aryLay.length-1].AddChild(app.CreateText(parms,0.4,0.07,"Left, Multiline")); } //Create an ok button. btnParmOk = app.CreateButton( "Ok", 0.23, 0.1 ); btnParmOk.SetOnTouch( btnParmOk_OnTouch ); layDlg.AddChild( btnParmOk ); //Show dialog. dlgTxt.Show(); } // Done with parameterized items s = edt.GetText() + cmds[item]; if ("MACRO" == cmds[item]) { s = "<"+num+"="+edt.GetText()+">"; } if ("m3" == cmds[item]) { //UART needs baud, data/parity, stop, polarity, out btnUart_OnTouch(); } if ("D" == cmds[item]) { s = ""; btnMultimeter_OnTouch(); } edt.SetText( s ); edt.SetCursorPos( s.length ); //move cursor to end of string } //Called when user presses Ok on parameter entry dialog function btnParmOk_OnTouch() { for(var i=0; i<aryParm.length; i++) { //for each parameter //get it's value and append it to what we will send edt.SetText(edt.GetText()+" "+aryParm[i].GetText().split("=")[0]); aryParm[i] = null; } aryParm = []; //clear it for next time dlgTxt.Hide(); //hope that garbage collects... nope } //helper to find either # or % in the parameter description function findNextParm(item, start) { var lb=item.indexOf("#",start)+1, per = item.indexOf("%",start)+1; if ( (lb) + (per) > 0 ) { //if we found any parameter markers if (lb) {if (per && (per<lb)) return per; else return lb; } if (per) {if (lb && (lb<per)) return lb; else return per; } } return 0; } //called when the user selects UART mode function btnUart_OnTouch() { var txt=""; dlgTxt = app.CreateDialog( "UART mode" ); //Create a layout for dialog. layDlg = app.CreateLayout( "linear", "Vertical,Left" ); //layDlg.SetPadding( 0.02, 0, 0.02, 0.02 ); dlgTxt.AddLayout( layDlg ); layDlgL1 = app.CreateLayout( "linear", "Horizontal,Left" ); layDlg.AddChild(layDlgL1); //Create baud spinner. txt ="1. 300,2. 1200,3. 2400,4. 4800,5. 9600,6. 19200,7. 38400,"; txt+="8. 57600,9. 115200" spinBaud = app.CreateSpinner(txt , 0.4 ); layDlgL1.AddChild( spinBaud ); //Create data/parity spinner UartDP =["None 8","Even 8","Odd 8","None 9"]; spinDP = app.CreateSpinner(UartDP.join(",") , 0.3 ); layDlgL1.AddChild( spinDP ); //Create stop bits spinner spinSB = app.CreateSpinner("1 ,2 " , 0.2 ); layDlgL1.AddChild( spinSB ); //Create polarity spinner spinP = app.CreateSpinner("1 Idle 1,2 Idle 0" , 0.4 ); layDlg.AddChild( spinP ); //Create output spinner spinOut = app.CreateSpinner("2 Normal (3.3/GND),1 OC (HiZ/GND)" , 0.6 ); layDlg.AddChild( spinOut ); //Create an ok button. btnUartOk = app.CreateButton( "Ok", 0.23, 0.1 ); btnUartOk.SetOnTouch( btnUartOk_OnTouch ); layDlg.AddChild( btnUartOk ); //Show dialog. dlgTxt.Show(); } //called when user closes UART setup dialog function btnUartOk_OnTouch(item) { var baud=spinBaud.GetText(); baud = baud.slice(0,baud.indexOf("."))+" "; var DP = spinDP.GetText(); for (var i=UartDP.length;i>=0;i--) { if (DP == UartDP[i]) { DP=(i+1)+" "; break; } } edt.SetText( "m3 " +baud +DP +spinSB.GetText().slice(0,2) +spinP.GetText().slice(0,2) +spinOut.GetText().slice(0,2) ); dlgTxt.Hide(); } //called when user selects ADC loop mode (D) function btnMultimeter_OnTouch() { dlgTxt = app.CreateDialog( "Multimeter mode" ); //Create a layout for dialog. layDlg = app.CreateLayout( "linear", "Vertical,Center" ); dlgTxt.AddLayout( layDlg ); //Create a read-only edit box to show responses. edtVolts = app.CreateText( "0.00", 0.95, 0.15, "Right, Monospace" ); edtVolts.SetMargins( 0,0,0,0.01 ); edtVolts.SetBackColor( "#333333" ); edtVolts.SetTextSize( 56 ); layDlg.AddChild( edtVolts ); layDlgHoriz = app.CreateLayout( "Linear", "Horizontal" ); layDlg.AddChild( layDlgHoriz ); layDlgHoriz.AddChild( app.CreateText( "Scale:" ) ); spinVolts = app.CreateSpinner( "x1,x5,x10,x25,x50,x100", 0.2 ); spinVolts.SetOnChange( spinVolts_onTouch ); layDlgHoriz.AddChild( spinVolts ); btnScaleCal = app.CreateButton( "Calibrate", 0.3, 0.1 ); btnScaleCal.SetOnTouch( btnScaleCal_OnTouch ); layDlgHoriz.AddChild( btnScaleCal ); layDlg.AddChild( app.CreateText( "(Calibrate will turn on power supplies!)" ) ); txtScaleCal = null; //Create an ok button. btnMultimeterOk = app.CreateButton( "Exit", 0.23, 0.1 ); btnMultimeterOk.SetOnTouch( btnMultimeterOk_OnTouch ); layDlg.AddChild( btnMultimeterOk ); //Show dialog. dlgTxt.Show(); Send("d"); } function btnScaleCal_OnTouch() { if (!txtScaleCal) { //if we haven't already entered this mode layDlg.mode=mode; //save the starting mode locally if ("HiZ>"==mode) { Send("m2"); app.ShowPopup("No power in HiZ, switching to 1-WIRE"); } Send("W"); //Power UP! txtScaleCal = app.CreateText( //teach user to connect scale pot "1. Connect +5 volt power and ground across potentiometer\n" +"2. Connect ADC to potentiometer wiper\n" +"3. Select desired scale\n" +"4. Trim potentiometer to show power supply voltage (5 V)\n" +"5. Disconnect +5 volt power from potentiometer\n" +"6. Connect voltage to be measured\n" +"(click Calibrate to close)\n" ,0.8, 0.4, "Left, Multiline"); layDlg.AddChild( txtScaleCal ); } else { //if we are already in calibrate mode, get out if ("HiZ>"==layDlg.mode) { Send("m1"); app.ShowPopup("Switching back to HiZ"); } Send("w"); //Power Down layDlg.DestroyChild(txtScaleCal); txtScaleCal = null; } } function spinVolts_onTouch( item ) { scaleADC = item.substring(1); //scale is after the "x" } function btnMultimeterOk_OnTouch() { if (txtScaleCal) { //if we are still in calibration mode btnScaleCal_OnTouch(); //pretend the user closed it } layDlg.DestroyChild(edtVolts); //destroy the edit box edtVolts = undefined; //and forget it so we don't funnel responses there. dlgTxt.Hide(); //hide the dialog Send("d"); //one last reading so the user as a record of it. } //Check connection and send data. function Send( s ) { if( usb ) usb.Write( s+"\r" ); else app.ShowPopup( "Please connect" ); } //Called when we get data from the BusPirate function usb_OnReceive( data ) { //if we are in meter mode, display the data if (typeof(edtVolts)!='undefined') { data += ":V"; //ensure we will have delimeters data = parseFloat(data.split(":")[1] .split("V")[0]); data = scaleADC*data; edtVolts.SetText(data.toPrecision(3)+" V"); Send("d"); //and repeat return; } //Need to translate tabs into spaces. var lines = data.split("\n"); var line; //line outside for scope so it is retained after loop ends for (var l = 0; l < lines.length; l++) { line=""; tabs = lines[l].split("\t"); for (var i = 0; i < tabs.length-1; i++) { line += tabs[i]+(" ").slice(0,8-(tabs[i].length % 8)); } line += tabs[i]; //don't expand the last bit of text. if ( l < lines.length - 1 ) line += "\n"; log += line; } //last line of returned data could be our mode. if (modes.indexOf(line)>0 && mode!=line) { mode=line; txt.SetText("BusPirate "+mode); } //scroll extra lines off the top var logLines = log.split("\n"); //calculate the longest line and set font size accordingly var maxChars = 0; for (var line in logLines) { maxChars = Math.max(maxChars, logLines[line].length); } //we want 8 point when the line is 80(?ish) and up to 14 point at 10. edtReply.SetTextSize(Math.min(16-maxChars/10,14)); maxLines = edtReply.GetMaxLines()-1; logLines = logLines.slice( -maxLines ); log = logLines.join("\n").toString(); edtReply.SetText( log ); }