Arduino as a wireless HTTP client

There are many aspects one must take into consideration when it comes to picking the right board for their project – from performance, memory, battery life, available slots for SD cards or bee modules all the way to the connectivity, namely network interface. Whether it is wired or wireless network interface, it plays crucial role in the way the board is deployed, controlled and it influences the memory and power consumption. In this post I will be discussing the way to setup your Arduino-compatible board as an HTTP client. I have been using my Seeedstudio Stalker v2.3 for all the tests I am describing in this post.

Wifi shield

In order to act as an HTTP client, we need to augment the Arduino using a wifi shield. I decided to use Wifi shield Fi250 from Seeedstudio, but you can get more modern one nowadays. These shields are easy to attach just stack it on top of your Arduino board. This particular shield allows us to do much more than just simple HTTP call execution, but for now that is all I will be focusing on. Before we take a look at the code, I would advice to check out the appropriate documentation, which in this case is the wiki page.

wifi-shield-Fi250-bottom
wifi-shield-Fi250-top

There are several libraries that are supposed to work with this model, but I kinda settled on the following one with small edits to the provided sample code in order to make it work. I will describe the individual quirks of working with this shield in the following chapters. You can download the library from this repository or from my Seeeduino_WizFi250 zip backup.

Programming

Before you start programming, it is a good idea to prepare the environment you want to issue your requests to. I created a simple Spring Boot application that exposes couple of endpoints for the testing purposes. All the details will be provided along the code samples.

Dependencies and constants

First things first, make sure you added the library for your wifi shield to your sketch. Once that’s done, you can start adding include statements and defining the constants that will be used by the sketch to set up the connection and target of the requests.

#include <Wire.h>
#include <SoftwareSerial.h>
#include "WizFi250.h"

#define SSID "MY_SSID" // replace with the SSID of your network
#define KEY "MY_PASS"  // replace with the password to your network
#define AUTH "WPA2"    // replace with the appropriate authentication method

#define spi_CS  8

#define SERVER_IP "192.168.0.187" // replace with your own server's IPv4 address
#define SERVER_PORT 8080          // replace with your own server's port
#define LOCAL_PORT 9000

WizFi250 wizfi250(Serial);

As it turns out, the library I chose to use was developed for Arduino Mega that supports the use of more than one Serial. I experienced some issues when I tried to log the values passed to the library, so be ware of this strange behavior. And since we are talking about the strange behavior, in case you are pushing the code to your Arduino with the shield plugged in, you may experience issues with the upload of the code. I found a simple workaround in my case – just remove the shield from the board, upload the code, attach the shield back on and restart the board. This process proved very successful and I was able to program the shield in any way I wanted without any errors related to the code upload.

Setup method

Setup method is fairly simple. We just need to initialize the libraries, turn on the shield via SPI interface and try to join to the network using the constants defined earlier.

void setup() {
  Serial.begin(115200);
  Wire.begin();  
  
  pinMode(spi_CS, OUTPUT);
  digitalWrite(spi_CS, HIGH);

  delay(1000);
  Serial.println("Join " SSID);

  delay(10);
  if (wizfi250.join(SSID, KEY, AUTH)) {
    Serial.println("Successfully joined  "  SSID);
    wizfi250.clear();
  } else {
     Serial.println("Failed to join " SSID);
  }
}

In case you specified your configuration correctly, you should see an output similar to the following:

Join MY_SSID
CMD: AT

AT
CMD: AT+WSET=0,MY_SSID

AT+WSET=0,MY_SSID
CMD: AT+WSEC=0,WPA2,MY_PASS

AT+WSEC=0,WPA2,MY_PASS
CMD: AT+WNET=1

AT+WNET=1
CMD: AT+WJOIN

AT+WJOIN
Successfully joined MY_SSID

GET request

Now, that the setup is done, let’s take a look at how to send a simple GET request. I will be using my Spring Boot application to monitor the outcomes of my Arduino sketch. The code looks fairly simple, breaking the logic apart into two methods: loop that sends the request and getHeaders that builds the required headers for the host server to be able to process the request. In order to make the request successful, one must specify the Host header to comply with the HTTP standard. Once the request is built, the code instructs the wifi shield library to issue the request to a specific server IP address and port. Since the Serial is being used by the wifi shield library, make sure that your debugging messages are commented out when running the code. Failing to do so will cause your code to fail and not issue any requests.

void loop() {
  String path = "GET / HTTP/1.1\r\n";
  path += getHeaders();

  wizfi250.clear(); 
  wizfi250.connect(SERVER_IP,SERVER_PORT,LOCAL_PORT);
  delay(10);

  //Serial.println("Path = " + path); 

  wizfi250.send(path.c_str());    
  delay(2000);
}

String getHeaders() {
  String headers = "";
  headers += "Host: ";
  headers += SERVER_IP;
  headers += ":";
  headers += SERVER_PORT;
  headers += "\r\n\r\n";
  
  //Serial.println("Headers = " + headers); 
  
  return headers;
}

If you followed the instructions above, you should be able to see an output similar to the one below.

CMD: AT+SCON=SO,TCN,192.168.0.187,8080,9000,1

AT+SCON=SO,TCN,192.168.0.187,8080,9000,1
GET / HTTP/1.1
Host: 192.168.0.187:8080

At this point, the local server should be receiving GET requests from your Arduino.

[2016-04-11 00:21:17.766] boot - 519  WARN [http-nio-8080-exec-5] --- ArduinoController: GET called
[2016-04-11 00:21:22.147] boot - 519  WARN [http-nio-8080-exec-7] --- ArduinoController: GET called
[2016-04-11 00:21:26.448] boot - 519  WARN [http-nio-8080-exec-9] --- ArduinoController: GET called
[2016-04-11 00:21:30.749] boot - 519  WARN [http-nio-8080-exec-1] --- ArduinoController: GET called
[2016-04-11 00:21:35.018] boot - 519  WARN [http-nio-8080-exec-3] --- ArduinoController: GET called

POST request

GET requests are great way to test your setup, but usually you want to be able to use the full capabilities of REST. Let’s focus on POST and sending the data to your backend server. Once again, the code consists of two methods mentioned earlier and works in the same way. Please notice that the endpoint used in this example is defined as /{number} accepting 123 value.

void loop() {
  String path = "POST /123 HTTP/1.1\r\n";
  path += getHeaders();
 
  wizfi250.clear();
  wizfi250.connect(SERVER_IP,SERVER_PORT,LOCAL_PORT);
  delay(10);
  
  //Serial.println("Path = " + path);

  wizfi250.send(path.c_str());
  delay(2000);
}

String getHeaders() {
  String headers = "";
  headers += "Host: ";
  headers += SERVER_IP;
  headers += ":";
  headers += SERVER_PORT;
  headers += "\r\n\r\n";
  
  //Serial.println("Headers = " + headers);
  
  return headers;
}

Following is the output you should be able to see once the loop method finished the execution.

CMD: AT+SCON=SO,TCN,192.168.0.187,8080,9000,1

AT+SCON=SO,TCN,192.168.0.187,8080,9000,1
POST /123 HTTP/1.1
Host: 192.168.0.187:8080

At this point, the local server should be receiving POST requests from your Arduino.

[2016-04-11 00:19:39.236] boot - 519  WARN [http-nio-8080-exec-3] --- ArduinoController: POST called: 123
[2016-04-11 00:19:43.741] boot - 519  WARN [http-nio-8080-exec-5] --- ArduinoController: POST called: 123
[2016-04-11 00:19:48.906] boot - 519  WARN [http-nio-8080-exec-7] --- ArduinoController: POST called: 123
[2016-04-11 00:19:54.105] boot - 519  WARN [http-nio-8080-exec-9] --- ArduinoController: POST called: 123
[2016-04-11 00:19:58.487] boot - 519  WARN [http-nio-8080-exec-1] --- ArduinoController: POST called: 123

POST request with JSON body

Finally, let’s consider a setup where the Arduino collects data from multiple sensors and posts them to the backend server using JSON format for the body of the request. While the methods remain the same, their code required several updates. The method building the headers needs to account for this and adds two new headers: Content-Type and Content-Length. A new method was added called getBody to build the body of the request. The rest of the code just accounted for these changes.

void loop() {
  String body = getBody();

  String path = "POST / HTTP/1.1\r\n";
  path += getHeaders(body.length());
  path += body;

  //Serial.println("Path = " + path);
 
  wizfi250.connect(SERVER_IP,SERVER_PORT,LOCAL_PORT);
  delay(10);

  wizfi250.send(path.c_str());
  delay(1000);
  
  wizfi250.clear();
  delay(1000);
}

String getHeaders(int contentLength) {
  String headers = "";
  headers += "Content-Type: application/json\r\n";
  headers += "Content-Length: ";
  headers += contentLength;
  headers += "\r\n";
  headers += "Host: ";
  headers += SERVER_IP;
  headers += ":";
  headers += SERVER_PORT;
  headers += "\r\n\r\n";
  
  //Serial.println("Headers = " + headers);
  
  return headers;
}

String getBody() {
  String body = "{";
  body += "\"value\":";
  body += "\"body\"";
  body += "}\r\n\r\n";
  
  //Serial.println("Body = " + body);
  
  return body;
}

This sketch produces following output.

CMD: AT+SCON=SO,TCN,192.168.0.187,8080,9000,1

AT+SCON=SO,TCN,192.168.0.187,8080,9000,1
POST / HTTP/1.1
Content-Type: application/json
Content-Length: 20
Host: 192.168.0.187:8080

{"value":"body"}

The local server receives the request and prints out the body of the request.

[2016-04-11 00:16:42.902] boot - 32641  WARN [http-nio-8080-exec-7] --- ArduinoController: POST called: Body{value='body'}
[2016-04-11 00:16:48.053] boot - 32641  WARN [http-nio-8080-exec-9] --- ArduinoController: POST called: Body{value='body'}
[2016-04-11 00:16:53.347] boot - 32641  WARN [http-nio-8080-exec-1] --- ArduinoController: POST called: Body{value='body'}
[2016-04-11 00:16:57.648] boot - 32641  WARN [http-nio-8080-exec-3] --- ArduinoController: POST called: Body{value='body'}
[2016-04-11 00:17:02.871] boot - 32641  WARN [http-nio-8080-exec-5] --- ArduinoController: POST called: Body{value='body'}

Conclusion

And that’s it. Admittedly, it was fun to play with wireless and Arduino and it showed me a great potential for any future project that might benefit from wireless connectivity. The best thing is that using the shield as an HTTP client is only one of the possible uses – other might be a TCP client. I would encourage you to try this out and take your projects to a new level. In case you decide to proceed, make sure to drop a comment here to share your experience.

Leave a Reply

Your email address will not be published. Required fields are marked *