I use Processing to control my Arduino — an easy and working solution. However if you need to send alot data to the Arduino, you’ll notice a 20ms delay between sending (120 bytes) and receieving (16 bytes) a serial message. After playing around with the rxtx Library (changed some timeouts, JNI version…) I guess the bottleneck is the Java JNI call.
While the serial interface is not a part of the JRE (anymore), the network stack is. This means we can send network packets at full speed. And there should be some Network to Serial tools available for my Mac OSX. Nope! I found plenty of such tools, but not a single one was working:
First I checked the Arduino Playground about SerialIP. A Serial IP library for the Arduino exist — but I couldn’t find a SLIP utility for Mac OSX 10.6.6. There was once slattach — but only for pre OSX 10.6. I compiled the source myself without success.
The next step was another Arduino Playground wiki entry, about SerialNet. To make the long story short, here are the tools I tried without success:
- nc –l –p 4443 –D < /dev/tty.usbmodem621 > /dev/tty.usbmodem621
- ./ser2net –C “3001:raw:0:/dev/tty.usbmodem621:115200 NONE 1STOPBIT 8DATABITS –XONXOFF –LOCAL –RTSCTS”
- socat –d –d –d –x –s –t 30 TCP-LISTEN:4441 GOPEN:/dev/tty.usbmodem621,raw,ispeed=115200,ospeed=115200,ignoreeof
- Tinkerproxy v2.0
Socat v1.7.1.3 hints: the “nonblock” option for the serial device resulted in a “tcsetattr(3, TCSADRAIN, 0x7fff5fbfefd0): Invalid argument” error, I guess this operation is not supported by the OS. Gerhard gave me another hint, that I should use the GOPEN address type (I was using the PTY address type — this *should* create a new PTY and not open an existing one. This is a bug in the v1.7.1.3 version).
Disclaimer: Maybe I was just too stupid to use those tools correct…
The solution was a bit funky — I created a PureData patch. Whats PureData?
PureData is a real-time graphical programming environment for audio, video, and graphical processing.
But you can use it as network-to-serial relay too. Here is my patch as image:
You may download the patch here. Features of the v0.1 release:
- Autoconnect Serial Port
- Autoconnect back to your listening Server
- TCP and UDP supported (replace tcpreceive with udpreceive and tcpsend with udpsend)
Here is the processing sketch I used to test my solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import processing.net.*; Client c; Server myServer; void setup() { frameRate(200); myServer = new Server(this, 7777); c = new Client(this, “127.0.0.1″, 7778); // Connect to server on port 80 } void draw() { long l1 = System.currentTimeMillis(); byte buf[] = new byte[70]; buf[0]=0x01; buf[1]=0; buf[2]=64; buf[3]=0x03; buf[4]=0x10; for (int j=5; j<70; j++) buf[j]=0x20; c.write(buf); print(“SEND “); Client thisClient=null; boolean gotData=false; int i=0; while (!gotData) { thisClient = myServer.available(); if (thisClient!=null) { if (thisClient.available()>2) { gotData = true; } } else { i++; delay(2); if (i==30) { println(“noreply!”); return; } } } println(“incomming serial data “+thisClient.available()+” bytes after “+(System.currentTimeMillis()-l1)+“ms”); while (thisClient!=null && thisClient.available()>0) { int b = thisClient.read(); //print(“0x”+Integer.toHexString(b)+”, ”); } // println(); } |
And this is the Arduino Sketch:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | #include <TimerOne.h> #define BAUD_RATE 115200 //— protocol data start #define CMD_START_BYTE 0x01 #define CMD_SENDFRAME 0x03 #define START_OF_DATA 0x10 #define END_OF_DATA 0x20 //frame size for specific color resolution #define COLOR_5BIT_FRAME_SIZE 64 #define SERIAL_HEADER_SIZE 5 //— protocol data end #define SERIAL_DELAY_LOOP 3 #define SERIAL_WAIT_DELAY 3 //this should match RX_BUFFER_SIZE from HardwareSerial.cpp byte serInStr[COLOR_5BIT_FRAME_SIZE+SERIAL_HEADER_SIZE]; // array that will hold the serial input string #define SERIALBUFFERSIZE 16 byte serialResonse[SERIALBUFFERSIZE]; byte g_errorCounter; //send status back to library static void sendAck() { serialResonse[0] = ‘A’; serialResonse[1] = ‘K’; serialResonse[2] = Serial.available(); serialResonse[3] = g_errorCounter; Serial.write(serialResonse, SERIALBUFFERSIZE); } void setup() { pinMode(13, OUTPUT); memset(serialResonse, 0, SERIALBUFFERSIZE); Serial.begin(BAUD_RATE); //Setup high speed Serial Serial.flush(); } void loop() { //read the serial port and create a string out of what you read g_errorCounter=0; // digitalWrite(13, LOW); // see if we got a proper command string yet if (readCommand(serInStr) == 0) { return; } digitalWrite(13, HIGH); //i2c addres of device byte addr = serInStr[1]; //how many bytes we’re sending byte sendlen = serInStr[2]; //what kind of command we send byte type = serInStr[3]; //parameter byte* cmd = serInStr+5; switch (type) { case CMD_SENDFRAME: if (sendlen == COLOR_5BIT_FRAME_SIZE) { } else { g_errorCounter=100; } break; case CMD_PING: //just send the ack! break; default: //invalid command g_errorCounter=130; break; } sendAck(); digitalWrite(13, LOW); } byte readCommand(byte *str) { byte b,i,sendlen; //wait until we get a CMD_START_BYTE or queue is empty i=0; while (Serial.available()>0 && i==0) { b = Serial.read(); if (b == CMD_START_BYTE) { i=1; } } if (i==0) { //failed to get data ignore it return 0; } //read header i = SERIAL_DELAY_LOOP; while (Serial.available() < SERIAL_HEADER_SIZE-1) { // wait for the rest delay(SERIAL_WAIT_DELAY); if (i– == 0) { return 0; //no data available! } } for (i=1; i<SERIAL_HEADER_SIZE; i++) { str[i] = Serial.read(); // fill it up } // — START HEADER CHECK //check if data is correct, 0x10 = START_OF_DATA if (str[4] != START_OF_DATA) { return 0; } //check sendlen, its possible that sendlen is 0! sendlen = str[2]; // — END HEADER CHECK //read data i = SERIAL_DELAY_LOOP; // wait for the final part, +1 for END_OF_DATA while (Serial.available() < sendlen+1) { delay(SERIAL_WAIT_DELAY); if( i– == 0 ) { return 0; } } for (i=SERIAL_HEADER_SIZE; i<SERIAL_HEADER_SIZE+sendlen+1; i++) { str[i] = Serial.read(); // fill it up } //check if data is correct, 0x20 = END_OF_DATA if (str[SERIAL_HEADER_SIZE+sendlen] != END_OF_DATA) { return 0; } //return data size (without meta data) return sendlen; } |
Here are some performance results on Mac OSX v10.6.6, PureData v0.41.4-extended, Arduino v0022, Processing v1.2.1 — using an Arduino UNO:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | SEND incomming serial data 16 bytes after 31ms SEND incomming serial data 16 bytes after 30ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 18ms SEND incomming serial data 16 bytes after 19ms SEND incomming serial data 16 bytes after 31ms SEND incomming serial data 14 bytes after 19ms SEND incomming serial data 3 bytes after 20ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 19ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 20ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 19ms SEND incomming serial data 16 bytes after 32ms SEND incomming serial data 16 bytes after 30ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 18ms SEND incomming serial data 3 bytes after 29ms SEND incomming serial data 16 bytes after 31ms SEND incomming serial data 3 bytes after 21ms SEND incomming serial data 13 bytes after 8ms SEND incomming serial data 32 bytes after 21ms SEND incomming serial data 16 bytes after 30ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 15 bytes after 20ms SEND incomming serial data 6 bytes after 25ms SEND incomming serial data 16 bytes after 30ms SEND incomming serial data 14 bytes after 22ms SEND incomming serial data 3 bytes after 31ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 9 bytes after 19ms SEND incomming serial data 7 bytes after 11ms SEND incomming serial data 16 bytes after 8ms SEND incomming serial data 9 bytes after 12ms SEND incomming serial data 23 bytes after 11ms SEND incomming serial data 16 bytes after 11ms SEND incomming serial data 3 bytes after 17ms SEND incomming serial data 3 bytes after 10ms SEND incomming serial data 25 bytes after 21ms SEND incomming serial data 7 bytes after 9ms SEND incomming serial data 32 bytes after 20ms SEND incomming serial data 16 bytes after 19ms SEND incomming serial data 16 bytes after 32ms SEND incomming serial data 9 bytes after 19ms SEND incomming serial data 7 bytes after 11ms SEND incomming serial data 16 bytes after 9ms SEND incomming serial data 32 bytes after 21ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 19ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 19ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 19ms SEND incomming serial data 16 bytes after 21ms SEND incomming serial data 16 bytes after 19ms SEND incomming serial data 16 bytes after 31ms SEND incomming serial data 11 bytes after 19ms SEND incomming serial data 5 bytes after 11ms SEND incomming serial data 16 bytes after 11ms SEND incomming serial data 22 bytes after 20ms SEND incomming serial data 10 bytes after 18ms SEND incomming serial data 16 bytes after 3ms SEND incomming serial data 16 bytes after 17ms SEND incomming serial data 4 bytes after 8ms SEND incomming serial data 16 bytes after 11ms SEND incomming serial data 16 bytes after 11ms SEND incomming serial data 5 bytes after 8ms |
Conclusion: Compared to using the rxtx JNI libraries I cannot see a huge performance boost! Sad but true. So my assumption was wrong — JNI is NOT the serial bottleneck using Arduino with the rxtx libraries. Again:
JNI is NOT the serial bottleneck using Arduino with the rxtx libraries
Side note: I read on several forum threads (mainly on the Arduino forum) that the baudrate should not matter is you use the USB version. This conclusion is just wrong! You can use my code above and run the tests with 9600 bps and 115200 bps — you’ll notice a HUGE difference!

One Trackback