// 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 );
}