This page's comments/discussion on Arduino Forum: Sdfrethsh (Arduino shell) discussion
This is yet another shell for the Arduino, based on fruitshell.
Edit: there is now version 1.1.0 on svn (see below), which can embed one of three unofficial DHCP libraries for Arduino: Arduino DHCP Library: Version 0.4 (jordanterrell); ASocket library (google code) (kegger); ArduinoEthernet (gkaindl). For more about the library installation procedure, see the dhcp test sketch ardtestdhcp.pde. Note that with (gkaindl), compiled Binary sketch size is: 14918 bytes.
I recently got into both ArduinoEthernetShield and emulino: arduino cpu emulator (also here), so I thought it would be nice to have a shell as a test program that could be ran either via usb-serial or ethernet; and which could be emulated with emulino.
There seem to be plenty of Arduino shells out there:
fruitshell (fruitshell forum post) was taken as a start, as it is relatively simple - however, it had problems being simulated with emulino. So the shell engine was rewritten, and then the command parser was copied from fruitshell, communication made switchable between ethernel and serial, and few functions added ... (and so, the sdfrethsh would stand for sd's 'fruit' ethernet shell :) ).
The shell functions in 'character mode' (that is, with characters, not buffered lines) - so the access tools should be appropriately set.
The code can be found on SourceForge.net Repository - (sdaaubckp) Index of /sdfrethsh, and v.1.0.0 is also included below for reference. sdfruiteth.pde builds to around 8K, and can be put on either ATmega168 or ATmega328 (most of development was done on emulino, tested with Duemillanove w/ATmega328 w/ Ethernet Shield).
Note that the instructions below refer to the process in respect to Ubuntu Linux as PC OS.
Build the sdfruiteth.pde in serial mode. Then, note that during a "Verify" the .hex file is written to /tmp, find the built hex file, and run
stty -icanon ; ./emulino /tmp/buildXXXXXXXXXXXXXXXX.tmp/sdfruiteth.cpp.hex
A shell prompt should be displayed - click TAB for help, Backspace to cancel an entry. Below is an example session:
$ stty -icanon 115200 ; ./emulino /tmp/build3918414324196097168.tmp/sdfruiteth.cpp.hex emulino: Loading hex image: /tmp/build3918414324196097168.tmp/sdfruiteth.cpp.hex sdfrethsh # read 1 > read 1 /1530 (6) Pin 1: 0 sdfrethsh # output 5 > output 5 /1819 (8) Output Pin 5 sdfrethsh # read 5 > read 5 /2142 (6) Pin 5: 0 sdfrethsh # write 1 5 > write 1 5 /2340 (9) pin 21 1 pins 00000000 00000000 00000100 Writen to Pin 5: 1 sdfrethsh # read 5 > read 5 /2558 (6) Pin 5: 1 sdfrethsh # exit > exit /2664 (4) cycles: 555078429 $
Note that the exit command should not "truly" exit if either ethernet mode, or echo mode (as likely for a real serial connection) is active (if by chance a real board "truly" exits, it needs to be rebooted to have the program start again).
Build the sdfruiteth.pde in serial mode. Then, upload to your Arduino board, and in terminal, execute:
screen /dev/ttyUSB0 115200
The shell will most likely not be displayed at first, so click Del or Tab to force the prompt to display.
Modify the IP settings to suit your needs, and build the sdfruiteth.pde in ethernet mode. Then, upload to your Arduino board - and in terminal, first see if ping responds:
$ ping 192.168.1.123 PING 192.168.1.123 (192.168.1.123) 56(84) bytes of data. 64 bytes from 192.168.1.123: icmp_seq=1 ttl=128 time=6.17 ms 64 bytes from 192.168.1.123: icmp_seq=2 ttl=128 time=2.18 ms ^C
Then, try to establish a telnet connection to the board:
$ telnet 192.168.1.123 23 Trying 192.168.1.123... Connected to 192.168.1.123. Escape character is '^]'. ^] telnet> mode character ^? sdfrethsh # Echo: ON sdfrethsh # hello > hello /5790 (6) Version 1.0.0
Note that as soon as connection is obtained, we should put telnet in "character mode", so the shell responds the same as via emulator or serial access. So, in the above telnet connection:
/* * sdfruiteth.pde * Shell-like environment for Arduino, * that can be simulated in emulino. * Based on minifruit (Fruit One) by Halid Ziya Yerebakan. * This one should be working with Ethernet. * * The code reports its results through serial or ethernet * * For sim, build in serial mode, and: * stty -icanon 115200 ; ./emulino /path/to/sdfruiteth.cpp.hex * in order to get sim of tab, del and echo keypresses * * sdfruiteth.pde * ------------ * Latest update: May 07 2010 * ------------ * Copyleft: use as you like * sd [] imi.aau.dk * */ // Commands byte shellstate = 3; //start from show prompt state /* state = 0 : prompt has been shown, no in data yet state = 1 : first incoming character came in, data still incoming state = 2 : EOL has been reach, incoming data completed state = 3 : data has been processed, ready to show prompt. */ int timestep=0; #define MAXCHARS 100 char line[MAXCHARS]; int linept=0; char rch; #define LF 10 //LineFeed #define CR 13 //CarrRet #define TAB 9 #define DEL 127 #define ECHR '^' //echo char byte echomode=0; // from minifruit shell - commands #define READ 1 #define WRITE 2 #define OUT 3 #define IN 4 #define DELAY 5 #define PULSE 6 #define EXIT 7 #define VERSION 8 /* * COMM FUNCTIONS **************************************************************** */ // keep ETHERNET commented (undefined) for USB/serial mode build //#define ETHERNET /* * in ETHERNET case, the shell should be * Server class - listening for * incoming connections */ /* * Note for ethernet: * calling as: * * stty -icanon ; telnet 192.168.1.77 23 * * once connected, escape the telnet with ^] (Ctrl+]) and to into character mode: * * ^] * telnet> mode character * * press enter - no message will be printed, but one is now in character mode. * Then press Tab to show the help screen of the shell. * No characters will be echoed, so you may want activate echo mode; * unfortunately '^' may get garbled by telnet, but you can copypaste it from * gnome-terminal from the help text shown on tab... * */ // commmode #ifdef ETHERNET static byte commmode = 1; // ethernet #else static byte commmode = 0; // serial #endif #include <Ethernet.h> // network configuration. gateway and subnet are optional. // Ethernet shield should be pingable at the address specified below, // its leds should blink at ping too byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; byte ip[] = { 192, 168, 1, 123 }; byte gateway[] = { 192, 168, 1, 1 }; byte subnet[] = { 255, 255, 255, 0 }; // telnet defaults to port 23 Server server = Server(23); void commBegin() { #ifdef ETHERNET // initialize the ethernet device Ethernet.begin(mac, ip, gateway, subnet); // start listening for clients server.begin(); #else Serial.begin(115200); #endif } int commAvailable() { #ifdef ETHERNET return server.available(); #else return Serial.available(); #endif } byte commRead() { #ifdef ETHERNET //the client read seems to block until enter is received // use telnet's character mode to get char by char response Client tclient = server.available(); return tclient.read(); #else // serial will block in emulation until enter is received, unless // stty -icanon is ran before emulating.. return Serial.read(); #endif } // Variadic macros are C99-only #ifdef ETHERNET #define commPrint(...) do { server.print(__VA_ARGS__); } while (0) #define commPrintln(...) do { server.println(__VA_ARGS__); } while (0) #else #define commPrint(...) do { Serial.print(__VA_ARGS__); } while (0) #define commPrintln(...) do { Serial.println(__VA_ARGS__); } while (0) #endif /* * MAIN FUNCTIONS **************************************************************** */ void setup() { commBegin(); // initialize the communication } void loop() { if (shellstate == 3) { printPrompt(); shellstate = 0; } checkForInData(); processInData(); //commPrintln(shellstate); timestep++; delay(20); } /* * SHELL FUNCTIONS **************************************************************** */ void printPrompt() { commPrint("sdfrethsh # "); } void checkForInData() { while(commAvailable()) // does not block if no data available { rch = commRead(); line[linept] = rch; linept++; if (rch == TAB){ // usage and reset - for tab printUsage(); shellstate = 3; linept = 0; return; } if (rch == DEL){ // immediate reset - for delete commPrintln(); shellstate = 3; linept = 0; return; } if (rch == ECHR){ // switch echo mode and reset commPrintln(); commPrint("Echo: "); if (echomode == 0) { echomode = 1; commPrintln("ON"); } else { echomode = 0; commPrintln("off"); } shellstate = 3; linept = 0; return; } if (echomode == 1) commPrint(rch); if (shellstate == 0) { shellstate = 1; //commPrintln(); //commPrintln("checkForInData "); } line[linept] = 0; if ((rch == CR) || (rch == LF)) { shellstate = 2; } } } void processInData() { if (shellstate == 2) { if (echomode == 1) commPrintln(); //add newline for echo mode linept--; //ignore last EOL line[linept] = 0; // and shorten string // printout command, timestep and number of chars in command commPrint("> "); commPrint(line); commPrint("\t\t/"); commPrint(timestep); commPrint("\t ("); commPrint(linept); commPrintln(")"); // parse'n'execute command execCommand(); // add empty line commPrintln(); shellstate = 3; linept = 0; } } // orig: showConsole() in minifruit shell void execCommand() { //Do command int command=parseLine(line); int param1,param2,param3; switch(command) { case READ : // Parse param as pin number param1 = line[5] - '0'; if (line[6]>='0' && line[6]<='9') param1=10*param1+line[6]-'0'; commPrint("Pin "); commPrint(param1); commPrint(": "); commPrintln(digitalRead(param1)); break; case WRITE : param1 = line[6] - '0'; // Digital value param2 = line[8] - '0'; // Line number if (line[9]>='0' && line[9]<='9') param2=10*param2+line[9]-'0'; digitalWrite(param2,param1); commPrint("Writen to Pin "); commPrint(param2); commPrint(": "); commPrintln(param1); break; case OUT: param1 = line[7] - '0'; if (line[8]>='0' && line[8]<='9') param1=10*param1+line[8]-'0'; pinMode(param1, OUTPUT); commPrint("Output Pin "); commPrintln(param1); break; case IN: param1 = line[6] - '0'; if (line[7]>='0' && line[7]<='9') param1=10*param1+line[7]-'0'; pinMode(param1, OUTPUT); commPrint("Input Pin "); commPrint(param1); break; case DELAY: param1 = line[6] - '0'; // Digital value param2 = line[8] - '0'; param3 = line[10] - '0'; // Line number if (line[11]>='0' && line[11]<='9') param3=10*param3+line[11]-'0'; commPrint("Active pin "); commPrint(param3); commPrint(" by "); commPrint(param1); commPrint(" seconds value:"); commPrintln(param2); digitalWrite(param3,param2); delay(1000*param1); digitalWrite(param3,!param2); // Not of parameter break; case PULSE: param1 = line[6] - '0'; // Voltage Level param2 = line[8] - '0'; // Line Number if (line[11]>='0' && line[11]<='9') param2=10*param2+line[11]-'0'; commPrint("Wave to pin "); commPrint(param2); commPrint(" by "); commPrint(param1); commPrintln(" V"); analogWrite(param2,param1*50); break; case VERSION: commPrintln("Version 1.0.0"); break; case EXIT: if ((echomode == 0) && (commmode ==0)) exit(0); break; // only useful for sim!! default: commPrintln("Unknown command"); break; } } void printUsage() { commPrintln("Console Usage:"); commPrint(" "); commPrint(ECHR); commPrintln(" : Switch echo mode "); commPrintln(" read <pin> : Read target pin "); commPrintln(" write <value> <pin> : Write value to desired pin "); commPrintln(" output <pin> : Configure pin as output "); commPrintln(" input <pin> : Configure pin as input "); commPrintln(" delay <second> <value> <pin> : Writes value to pin for given delay "); commPrintln(" pulse <value> <pin> : Arrages voltage value to given pin by PWM "); commPrintln(" exit : Exit"); commPrintln(" hello : Version"); } int stringCompare(char* line1,char* line2,int length) /*Returns 1 when they are equal*/ { int i=0; int result=1; for(i=0;i<length;i++) result = result && (line1[i]==line2[i]); return result; } int stringLength(char* line) { int i; for(i=0;line[i]!=0;i++); return i; } int parseLine(char* line) { //if (stringCompare(line,"exit",4) || (stringLength(line) < 4 )) return EXIT; if (stringCompare(line,"exit",4)) return EXIT; if (stringCompare(line,"read",4)) return READ; if (stringCompare(line,"write",5)) return WRITE; if (stringCompare(line,"output",5)) return OUT; if (stringCompare(line,"input",5)) return IN; if (stringCompare(line,"delay",5)) return DELAY; if (stringCompare(line,"pulse",5)) return PULSE; if (stringCompare(line,"hello",5)) return VERSION; }
This page's comments/discussion on Arduino Forum: Sdfrethsh (Arduino shell) discussion