World Domination Using Arduinos and Websockets

Arduino Websocket Client Arduino Pusher Client Pusher – Real Time Push Notifications Follow Me On Twitter: @kevinrohling

If you don’t have your very own Arduino yet you should get one. What exactly are you waiting for? Arduinos are tons of fun, especially once you hook up some servos and start driving them around. Add a few pieces of military grade weaponry and you have your very own deathbot!

One of the first things you have to figure out once you’ve decided to personally catalyze the robot apocalypse is how to remotely control your robots, i.e. tell them when to turn left, turn right, stop, and of course… fire rocket launchers. There’s a number of ways you could do this. One of the more common I’ve seen is to open up a server port on your Arduino, connect to it and send it messages directly. The problem I ran into doing this was two fold: 1) I have to know the IP address of the Arduino on my network to make the connection and 2) I have to be on the same network as the Arduino. None of these things are very convenient and IP addresses and networks can change.

The solution I came up with was to use Pusher, a real-time push notification service that runs over WebSockets. Since the native Arduino libraries come with support for TCP connections, making this work involved writing a WebSocket Client library, as one does not appear to have previously existed, and building a Pusher client on top of that. Because the Arduino is connected to a Pusher Channel I don’t have to know anything about the actual device to control it, I just send messages to the Channel. My mechanized Arduinos of destruction are free to roam around, switching networks and destroying civilization, meanwhile I can still control them with my iPad from a hammock in the Bahamas.

Building the WebSocket Client

WebSockets are an interesting hybrid between HTTP and raw TCP connections. They start life very much like a normal HTTP GET request. In the request the client sends a bit information asking for an “upgraded” connection. Once the server sees this, if WebSockets are supported it sends a response back with a status code of 101 indicating that the connection was successfully upgraded. Then, and here’s where things diverge from HTTP, nobody closes the connection. Both the client and the server remain connected to each other. Here’s what this looks like at the socket level:

Client Requests a WebSocket Connection

GET /app/yourpusherapikey?client=js&version=1.9.0 HTTP/1.1
Upgrade: WebSocket 
Connection: Upgrade 
Host: ws.pusherapp.com:80 
Origin: ArduinoWebSocketClient

Server responds indicating that the upgrade was successful

HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket 
Connection: Upgrade 
WebSocket-Origin: ArduinoWebSocketClient 
WebSocket-Location: ws://ws.pusherapp.com:80/app/yourpusherapikey?client=js&version=1.9.0 
Connected

Now that they’re connected both the client and the server can send each other messages any time they want using a process called Data Framing. Data Framing is a protocol for indicating the start and end of discrete messages on the socket. The Arduino Websocket Client Library currently only supports Text Frames, which use a 0x00 byte to indicate the start of a message, a 0xFF byte to indicate the end, and UTF-8 data in between. The WebSocket Specification also allows for Binary Frames, which use a length prefix followed by binary data. Here’s what the Arduino code looks like for sending a WebSocket message:

void  WebSocketClient::send (String data) {
     _client.print((char )0 ); 
     _client.print(data); 
     _client.print((char )255 ); 
}

Building the Pusher Client As I mentioned before Pusher is a real-time push notification service that operates over WebSockets. Like any WebSocket implementation they use Data Framing for passing messages back and forth but they’ve built their own messaging protocol on top of Data Framing. Each message sent to or received from Pusher is formatted as JSON. This is good for the Arduino because JSON is light weight and also easy to parse. Once the Arduino WebSocket Client was built developing the Arduino Pusher Client was a matter of implementing support for sending/receiving the right messages.

Sending a message is called “Triggering An Event”:

void PusherClient::triggerEvent(String eventName, String eventData) {
    _client.send("{\"event\": \"" + eventName + "\", \"data\": " + eventData + " }");
}

Receiving a message from Pusher:

 
void  PusherClient::dataArrived(WebSocketClient client, String data) {
     String eventNameStart = "event" ; 
     String eventName = parseMessageMember("event" , data); 

     if  (_bindAllDelegate != NULL ) { 
         _bindAllDelegate(data); 
     } 

     EventDelegate delegate = _bindMap.getValueOf(eventName); 
     if  (delegate != NULL ) { 
         delegate(data); 
     } 
}

Controlling our servos of destruction in response to events received from Pusher:

PusherClient client("your-api-key-here");

//Setup delegates for the Pusher Events
client.bind("forward", moveForward);
client.bind("backward", moveBackward);
client.bind("turn_left", turnLeft);
client.bind("turn_right", turnRight);
client.bind("stop", stopMoving);

//Subsribe to our Pusher Channel
client.subscribe("robot_channel");

void moveForward(String data) {
  leftServo.write(0);
  rightServo.write(180);
}

void moveBackward(String data) {
  leftServo.write(180);
  rightServo.write(0);
}

void turnLeft(String data) {
  leftServo.write(0);
  rightServo.write(0);
}

void turnRight(String data) {
  leftServo.write(180);
  rightServo.write(180);
}

void stopMoving(String data) {
  leftServo.write(95);
  rightServo.write(95);
}

All Wired Up

We’ve now solved what is likely the most challenging part of developing your Arduino army.

Pusher messages being received:



Tremble in fear of the Pusher Powered Arduino-Bot:



46 Comments on “World Domination Using Arduinos and Websockets”

    • Thanks, glad you liked the post. Didn’t even know what the Open Hardware Summit was before reading your comment. It looks awesome, wish I could be there, maybe next year!

      • HTTP is a request-response based protocol where exchanges always happen in isolation and always initiated by the client. Individual messages contains all necessary information for being processed by the next party, be it a user-agent (browser), server, proxy. There is no maintained context between exchanges and no direct way to get event notifications from the server.

        Websockets is bi-directional suitable for chat applications for example. Server Sent Events is a push from a server to a client in an HTTP context. It saves basically resources in terms of battery or long polling.

        Another protocol which is being worked on for the chrome of browsers is Web Notification.

  1. Nice. In WebSocketClient::send(String), you may want to throw in a loop that checks for (and drops or replaces) any 0xFF values in the String. Else someone’s going to try to transmit raw ADC readings or some such analog value and confuse the Websocket to no end.

  2. Great work, really cool idea.

    One thing the websocket API is changing to a new protocol and the handshake and data framing are significantly different. I believe iOS 5 will be using the new protocol, most latest release browsers are currently using the new protocol.

  3. Oh man, you should have been at OH Summit! There was a breakout I attended where we discussed the state of getting websocket to work with arduino. Someone already had a arduino websocket server, but no client. This is exactly what we needed (though it looks like you are using websocket v1, correct?).

    • It’s a very early rev on the Websockets spec. However, I’m currently working on refactoring and merging this with the Arduino Websocket Server library to form the Arduino Websocket SDK. What revision did you guys need? Also, it’s bery exciting to hear that this would have been helpful for what they were working on. What was the project by the way?

  4. any tips on getting this thing to connect? have properly set the mac address, ip and pusher api key.. still cannot get the pusher console to recognize my connection / arduino device to receive messages. using ethernet here. any tips?

    • update: will be using this to fly a gas powered rc helicopter through denver. will be outfitted with a wireless camera and sensors — all being fed back to my computer via my htc evo 🙂

    • Hi nparsons, sounds like a great application!
      First thing I’d try is seeing if your Arduino can do an http get from another site, say Google, just to verify that you’re connecting to the network. If that works then the most likely culprit is the code in the WebSocketClient.cpp that builds the server’s hostname before attempting a socket connection. Full disclosure, I haven’t tested with Ethernet, only WiFi, so there might be some kinks.

      Modify the following constructor by adding Serial.println(_hostname) at the bottom. When you run the code it should print out “174.129.22.121”. If it outputs anything different then that would explain why the connection isn’t working and I’ll throw together a quick fix for it.

      Constructor to modify:
      WebSocketClient::WebSocketClient(byte server[], String path, int port) :

      Email me here: kevin@kevinrohling.com and let’s set up a time to talk if you’re still having trouble.

  5. Hi!

    Great work and thanks for providing the ArduinoWebsocketClient library and the examples here!

    I ‘ve tried the library with the new Arduino v1.0 beta (http://code.google.com/p/arduino/wiki/Arduino1) and does not compile (like many of third libraries on the net). It looks like they have changed the way the Client.h is used. One has to use the EthernetClient instead. I’ve made the appropriate changes in the library though and it does.
    To save you some time I am reporting here my changes:
    1) Replaced WProgram.h with Arduino.h in WebSocketClient.cpp and WebSocketClient.h
    2) In the header file replace Client.h with EthernetClient.h
    3) On the same file the Client _client; has to become EthernetClient _client;
    4) I am using the WiFy constructor in the WebSocketClient.cpp like: WebSocketClient::WebSocketClient(const char *hostname, String path, int port) :
    _client()
    5) In WebSocketClient.cpp replaced in the _client.connect() with _client.connect(_hostname, _port)
    6) In WebSocketClient.h I have changed the port and hostname variables to:
    uint16_t _port;
    const char* _hostname;

    Also in sketch, the WebSocketClient is initialized using the const char, like: WebSocketClient client(“174.129.224.73”, “/”, 80);

    The sketch now compiles, I am running some tests to make sure functionality is the same 🙂

    Ch.

  6. Hey,

    Thanks alot for this code, exactly what I was looking for.. however I’m having a hard time to get it to work. Created a very basic sketch that subscribes to a channel and has a client.monitor in the loop.. In the pusher debug console it shows that my arduino is connects and subscribes, but then immediately disconnects, with lifecycle 0s.. Connecting through ethernet shield… Have any ideas?

    Thanks!

    • Hey wildfirewolf, I’ve had several people report compatibility issues with Arduino 1.0 as well as Ethernet (as opposed to WiFly) even in previous Arduino versions. The good news is I’ll be fixing these up shortly, just waiting on my new Ethernet shield to arrive.

      Thanks for reporting these issues and I hope to get a new release out by mid-January.

      http://bit.ly/visQKs
      http://bit.ly/vUaf4E

      • Thank you, that would be great! I managed to get it working with the older Arduino software. Just a note though for anyone wanting to trigger Pusher client events from the Arduino, once Pusher has enabled client events for your app, you need to subscribe to a private or presence channel, and that requires a sha256 hash of the socket-id sent by the connection event and the secret key, which means you will have to use a crypto library on the arduino, or make a get request to a php script or something to generate a key. In the end I decided to set up my own websocket server on nodejs, instead of using Pusher, which works perfectly with your Websocket library, I just changed the handshake a bit to work with the newer draft.

        Thanks again!

  7. Pingback: Arduino and pusher | things.iothings.io

  8. Kevin – thanks for doing this work. This code and pusher.com is a great solution for a problem that I have been trying to solve for a long time.

    The only problem I am having with the code is the size, with a skeletal sketch pushing over 20k. Whether I can get all the features of my project working remains to be seen, but I really appreciate work and the wealth of ideas that this has supplied to my project.

    • Brian,
      Thanks for the comment and I’m glad you have found it useful. I also appreciate the feedback regarding the library size and will think about ways to slim it down.

      Kevin.

      • Kevin – I noticed in your ‘bot, you were using a Mega and a WiFly board. Have you tried to run on boards with the lesser ATMega328 (32k) chips, and something like an Ether shield? I have a Blackwidow (‘duino clone + wifi) board, and am also looking at what it would take to get your libraries running on it. I’m guessing the Mega cures a host of ills in regard to available sketch memory, which is probably why it wasn’t a concern for you.

        The irony of the beauty and pain (wink) of your solution for me really revolves around the size issue, because that is what has been putting a continual kibosh on my key Arduino project. I had to expand it to two Arduinos working in master/slave roles to off-load functionality/memory use from the web-enabled board, because I was running out of memory due to the size of the web libraries, HTML, and NTP time management that I am trying to do (because I am doing some things around sunrise/sunset, daylight savings, etc). Because of that, I was already in the process of moving off of NTP in favor of a DS1307 real-time-clock – $9, which will save me a host of hassles, traded, of course, for the hassle of a button battery and manual initialization.

        Now I have an Ardweenie ($10/Solarbotics) doing the heavy lifting of all the physical I/O handling, sensors, LCD display, and I will have an Arduino+Ethernet board doing the back-end network hauling with Pusher and WebSockets, rather than trying to serve a web page and all the requisite functionality. Again the irony, this new path consumes just as much memory or more, with fewer actual moving parts, but the functionality potential has increased significantly. I am really liking this new direction!

        Thanks again!

      • Sorry man – don’t mean to spam/stalk 😉 I worked on getting your Pusher library coupled with my WiFi enabled Arduino (Asynclabs Blackwidow 1.0), and there is a real problem with the way that the WiFi is handled on these boards – I presume that the same is true of the Aysnclabs WiFi shield.

        Basically, the WiFi workload is queued and the inbound and outbound work is handled off its stack only when the WiFi.run() method is called.

        I tried a few different approaches to handle this in the sketch, but when we call client.connect() it just hangs because the WiFi stack is not being processed in the background. 😦

        Your code works fine on my Arduino+Ethernet board.

      • Hey Brian,
        No worries, I’m always happy to hear that this is something you’ve found useful, even if there are some challenges. It sounds like you’re saying that WiFi.run() needs to be called for this board to process incoming packets? If that’s the case, it might be possible to make a few modifications to the WebsocketClient to make this work:

        The first thing I’ve try to find out is what’s hanging inside the WebSocketClient. It’s most likely either the call to _client.connect or something inside the readHandshake() method. I can’t imagine it would be the call to connect, I feel like readHandshake() is more likely.

        bool WebSocketClient::connect(char hostname[], char path[], int port) {
        bool result = false;

        if (_client.connect(hostname, port)) {
        sendHandshake(hostname, path);
        result = readHandshake(); //I suspect this is where it’s hanging?
        }

        return result;
        }

        If it is the readHandshake() method you could try adding calls ti WiFi.run() inside these while loops.

        bool WebSocketClient::readHandshake() {
        bool result = false;
        char character;
        String handshake = “”, line;
        int maxAttempts = 300, attempts = 0;

        while(_client.available() == 0 && attempts < maxAttempts)
        {
        WiFi.run(); //Added call here
        delay(100);
        attempts++;
        }

        while((line = readLine()) != “”) {
        WiFi.run(); //And here
        handshake += line + ‘\n’;
        }

        This is probably far from a solution but it might be worth a try.

  9. Kevin – well, the bad news is that it’s still not working. The good news is that the board is responsive to pings while it’s trying to connect to Pusher, which is an improvement.

    I put in some debugging messages, and it’s hitting the maxAttempts threshold.

    Hmmmm.

  10. Pingback: Come/WebSocket? Introducing the Atmosphere framework | Nicolas Colomer

  11. Hello Kevin, I recently got authentication and Client events working from an Arduino, thanks to your libraries! I am a bit of a n00b when it comes to arduino, but I think there were serious memory issues once I started dealing with the long authentication strings. On the advice of another programmer, i tried to keep everything as char arrays instead of Strings, and removed a lot of stuff from the libraries that I didn’t need (this ended up working, I guess every little variable declaration counts.

    Blog is here:http://www.charmcitynetworks.com/news/sending-pusher-client-events-from-an-arduino/

  12. Hi Kevin, your Pusher client and WebSockets client are great.

    I’m playing around with a relay controlled by an event.

    The Arduino connects binds to events and subscribe to the channel, etc, everything works great for about 6 mins (~360 seconds) and the socket connection is closed. I’m not sure if is the Arduino or Pusher’s server who’s closing the connection.

    After that, the Arduino obviously does not receive nor respond to any events, until I push the reset button.

    I’m trying to figure out how to make a persistent connection and how to re-connect if the internet connection drops.

    Thanks in advance,
    Nicolas

Leave a reply to Brian B. Cancel reply