arduino mega web server with wifly and temperature sensor

Sun, Apr 27, 2014

I’ve had a pile of electronics lying around for a while, waiting for me to tackle an interesting project. How to get an Arduino on the wifi network, broadcasting the temperature. Well, the parts list is fairly simple to get hold of:

  • Arduino (I used a Mega 2560)
  • WiFly shield
  • Stackable headers
  • BMP180 temperature/pressure sensor. I got one from eBay (I think!)
  • A load of jumper wires and a wee breadboard (for the BMP180)
  • Soldering iron and lead free solder
  • Some AC/DC albums (optional)

Now I haven’t soldered anything in a very long time but it turned out to be easy (once I’d worked round the tip on the brand new soldering iron not working after 5mins). But first things first, assemble the parts into a photogenic arrangement:

I then bunged the headers into the WiFly shield and carefully flipped it upside down and made sure the whole thing was flat:

Then it was a case of soldering one pin on each header, flipping it the right way up:

and very carefully inserting the header pins in the arduino. This makes sure they’re straight. Slowly out again, flip over and solder the rest. The result was a nice mating of the shield and the arduino:

Almost there but for the mega, you need to wire up these pins:

  • WiFly 10 -> Mega 53
  • WiFly 11 -> Mega 51
  • WiFly 12 -> Mega 50
  • WiFly 13 -> Mega 52

and that’s the finished assembly:

At this point you can plug it into the computer and the shield lights should start flashing but to actually get it on the network you need to do some more work.

Download the WiFly shield library from github and install it in the Arduino IDE:


unzip WiFly-Shield-master.zip
cp -r WiFly-Shield-master/Libraries/WiFly_Shield /Users/alistair/Documents/Arduino/libraries/WiFly

then start up the Arduino IDE and choose:

File -> Examples -> WiFly -> SpiUartTerminal

compile and upload the above sketch. It will let you talk to the WiFly over the serial connection and you can then configure it:


# Make sure Serial Monitor is 9600 baud and No line ending

# enter command mode:
$$$

# Now make sure Serial Monitor is 9600 baud and Carriage return

scan # don't worry if this doesn't spot your router. It didn't spot mine

# no wifi authentication needed
set wlan auth 0

# get your SSID from your router and join the network
join YOUR_SSID

# that's you on the network! Now tell the shield to always join this network
set wlan ssid YOUR_SSID
set wlan join 1

# and listen on port 80
set ip local 80

# and save to its defauls
save

# see what your IP is
get ip

# or feeling verbose today?
get everything

Just out of curiosity I checked on the router to see what had connected:

WiFly-GSX 00-06-66-30-14-FE 192.168.1.5

when you join from the serial monitor you’ll see a load of stuff like:

Auto-Assoc YOUR_SSID chan=6 mode=OPEN SCAN OK
Joining YOUR_SSID now..
<2.31> Associated!
DHCP: Start
DHCP in 7ms, lease=259200s
IF=UP
DHCP=ON
IP=192.168.1.5:80
NM=255.255.255.0
GW=192.168.1.1
Listen on 80

the IF=UP line is very important as we’ll see later. You should be able to ping your router from the serial monitor now:

ping 192.168.1.1

So you now have the arduino on the wifi network. What now? Temperature perhaps?

For this you’ll need something to take the temperature. I used a BMP180 sensor that also does atmospheric pressure. A dinky wee thing which I soldered pins to. It meant having to cut the 5th pin of a set as it only has 4 holes but it was easy enough. I then popped it into a lovely wee breadboard and wired it up as per the spec:

Again, you need to download a library:


# Download zip from https://github.com/sparkfun/BMP180_Breakout
cd BMP180_Breakout-master/software/Arduino/libraries
cp -r SFE_BMP180 /Users/alistair/Documents/Arduino/libraries

restart the IDE and choose:

File -> Examples -> SFE_BMP180 -> SFE_BMP180_example

This lets you see the BMP180 in action. Compile and upload the above sketch then connect on the serial monitor and it will display temperature, pressure and altitude readings.

So far, you’ve got a networked arduino sampling the temperature but only accessible really over the serial connection. Let’s bung it on the web!

This is the code I wrote which basically just takes the best bits of the WiFly and BMP180 libraries and ties them together into a web server that shows the temperature, pressure and so on.

Note that the files are called WeatherStation.h and WeatherStation.ino. The .h.c and .ino.c extensions are just to fool Octopress into displaying them as it doesn’t know what .h and .ino files are. Perhaps I’ll sort that sometime.


#ifndef WeatherStationTypes_h
#define WeatherStationTypes_h

#include <WString.h>

typedef struct {
  double temperatureDegreesC;
  double temperatureDegreesF;
  double absolutePressureMb;
  double absolutePressureHg;
  double relativeSeaLevelPressureMb;
  double relativeSeaLevelPressureHg;
  double altitudeM;
  double altitudeF;
} WeatherReading_t;

#endif

/*
 * Web Server
 *
 * (Based on Ethernet's WebServer Example)
 *
 * A simple web server that shows the value of the analog input pins.
 */

#include <Stdio.h>
#include <SPI.h>
#include <WiFly.h>
#include <SFE_BMP180.h>
#include <Wire.h>
#include "WeatherStation.h"

#define ALTITUDE 1655.0

SFE_BMP180 pressureSensor;
WiFlyServer server(80);

void setup() {
  WiFly.begin();

  char ssid[] = "YOUR_SSID";
  if (!WiFly.join(ssid)) {
    Serial.println("can't join network");
    while (1) {
    }
  }

  Serial.begin(9600);
  Serial.print("IP: ");
  Serial.println(WiFly.ip());
  
  server.begin();
  
  // We're on the network, set up the temperature sensor
  if (pressureSensor.begin()) {
    Serial.println("BMP180 init success");
  }
  else {
    Serial.println("BMP180 init fail\n\n");
  }
}

void loop() {
  WeatherReading_t weatherReading;
  WiFlyClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        // if we've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so we can send a reply
        if (c == '\n' && current_line_is_blank) {

          getTemperature(&weatherReading);
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          client.println("<html>");
          client.println("<head><title>codeBrane Weather Station 0.0.1</title></head>");
          client.println("<body>");
          
          client.print("<p>Temperature C : ");
          client.print(weatherReading.temperatureDegreesC);
          client.print("</p>");
          
          client.print("<p>Temperature F : ");
          client.print(weatherReading.temperatureDegreesF);
          client.print("</p>");
          
          client.print("<p>Pressure Mb : ");
          client.print(weatherReading.absolutePressureMb);
          client.print("</p>");
          
          client.print("<p>Pressure Hg : ");
          client.print(weatherReading.absolutePressureHg);
          client.print("</p>");
          
          client.print("<p>Relative Sea Level Pressure Mb : ");
          client.print(weatherReading.relativeSeaLevelPressureMb);
          client.print("</p>");
          
          client.print("<p>Relative Sea Level Pressure Hg : ");
          client.print(weatherReading.relativeSeaLevelPressureHg);
          client.print("</p>");
          
          client.print("<p>Altitude metres : ");
          client.print(weatherReading.altitudeM);
          client.print("</p>");
          
          client.print("<p>Altitude feet : ");
          client.print(weatherReading.altitudeF);
          client.print("</p>");
          
          client.println("</body>");
          client.println("</html>");
          
          break;
        }
        if (c == '\n') {
          // we're starting a new line
          current_line_is_blank = true;
        } else if (c != '\r') {
          // we've gotten a character on the current line
          current_line_is_blank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(100);
    client.stop();
  }
}

void getTemperature(WeatherReading_t* weatherReading) {
  char status;
  double temperature, pressure, p0, altitude;
  
  status = pressureSensor.startTemperature();
  if (status != 0) {
    // Wait for the measurement to complete:
    delay(status);

    // Retrieve the completed temperature measurement:
    // Note that the measurement is stored in the variable T.
    // Function returns 1 if successful, 0 if failure.

    status = pressureSensor.getTemperature(temperature);
    if (status != 0) {
      // Print out the measurement:
      Serial.print("temperature: ");
      Serial.print(temperature,2);
      weatherReading->temperatureDegreesC = temperature;
      Serial.print(" deg C, ");
      weatherReading->temperatureDegreesF = (9.0/5.0) * temperature + 32.0;
      Serial.print((9.0/5.0)*temperature+32.0,2);
      Serial.println(" deg F");
      
      // Start a pressure measurement:
      // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
      // If request is successful, the number of ms to wait is returned.
      // If request is unsuccessful, 0 is returned.

      status = pressureSensor.startPressure(3);
      if (status != 0) {
        // Wait for the measurement to complete:
        delay(status);

        // Retrieve the completed pressure measurement:
        // Note that the measurement is stored in the variable P.
        // Note also that the function requires the previous temperature measurement (T).
        // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
        // Function returns 1 if successful, 0 if failure.

        status = pressureSensor.getPressure(pressure, temperature);
        if (status != 0) {
          // Print out the measurement:
          Serial.print("absolute pressure: ");
          Serial.print(pressure, 2);
          Serial.print(" mb, ");
          Serial.print(pressure*0.0295333727,2);
          Serial.println(" inHg");
          weatherReading->absolutePressureMb = pressure;
          weatherReading->absolutePressureHg = pressure * 0.0295333727;

          // The pressure sensor returns absolute pressure, which varies with altitude.
          // To remove the effects of altitude, use the sealevel function and your current altitude.
          // This number is commonly used in weather reports.
          // Parameters: P = absolute pressure in mb, ALTITUDE = current altitude in m.
          // Result: p0 = sea-level compensated pressure in mb

          p0 = pressureSensor.sealevel(pressure,ALTITUDE);
          Serial.print("relative (sea-level) pressure: ");
          Serial.print(p0,2);
          Serial.print(" mb, ");
          Serial.print(p0*0.0295333727,2);
          Serial.println(" inHg");
          weatherReading->relativeSeaLevelPressureMb = p0;
          weatherReading->relativeSeaLevelPressureHg = p0 * 0.0295333727;

          // On the other hand, if you want to determine your altitude from the pressure reading,
          // use the altitude function along with a baseline pressure (sea-level or other).
          // Parameters: P = absolute pressure in mb, p0 = baseline pressure in mb.
          // Result: a = altitude in m.

          altitude = pressureSensor.altitude(pressure, p0);
          Serial.print("computed altitude: ");
          Serial.print(altitude,0);
          Serial.print(" meters, ");
          Serial.print(altitude*3.28084,0);
          Serial.println(" feet");
          weatherReading->altitudeM = altitude;
          weatherReading->altitudeF = altitude * 3.28084;
        }
        else Serial.println("error retrieving pressure measurement\n");
      }
      else Serial.println("error starting pressure measurement\n");
    }
    else Serial.println("error retrieving temperature measurement\n");
  }
  else Serial.println("error starting temperature measurement\n");

  delay(5000);  // Pause for 5 seconds.
}

The really important bit in WeatherStation.ino is this line:

WiFly.join(ssid)

if that’s not there, for some reason, the mega doesn’t connect to the network after the sketch is uploaded. Might be timing, might be dropping, not sure yet but that line fixes it. If it’s not there it won’t serve and you can’t connect to it and if you upload the WiFly config sketch and ping your router it’ll fail with:

ERR: Net IF is down

and you’ll have to manually join again from the config sketch. Incidentally, you can also do:

telnet 192.168.1.5 80

and issue WiFly config commands over telnet. Nice. Of course, your WiFly IP will vary.

When you upload the WeatherStation sketch again though, telnet to 80 will produce the web page. Or just open it in a browser:

http://192.168.1.1

or even better, add the mega to your /etc/hosts file:

192.168.1.1 mega

then in the browser:

http://mega

sweeeeeet!

References:

comments powered by Disqus