2012-09-25

Weather Station

Well, I've finished the 'big book' on iOS programming. My latest order from LittleBirdElectronics should arrive today.

1x Arduino Stackable Header Kit
1x WiFly Shield
1x Machine Screw Nut 2-56
1x Machine Screw: #2-56, 1" Length, Phillips (25-pack)
1x 0.100" (2.54 mm) Female Header: 1x2-Pin, Straight
1x 0.100" (2.54 mm) Breakaway Male Header: 1x40-Pin, Straight, Double-Sided
2x Jumper Wire - JST Black Red
1x Lipo Rider
1x Solar Cell Small - 0.45W
1x Polymer Lithium Ion Battery - 900mAh
1x LiPo Fuel Gauge



With this I plan to write a hardware-integration app for iOS that will allow me to do some basic things, like blink LEDs and read a DHT11 sensor, from my iPad. The end game here is to be able to present some fancy UI stuff on an iOS device and have my embedded 'minions' do all the work interacting with the real world. It requires WiFi and UDP network protocol - it seems Apple have slammed the door on every other option. Android anyone?

I'm also addicted to the idea of having self-powered 'nodes' (hence battery, solar panel, LiPo fuel gauge, etc.). I imagine having Arduinos with their own batteries, solar panels and XBee radios being stationed all around the house and cars. How cool would it be if the GPS data logger in the car tapped in to the mesh network at the house and pushed its data up to a server automatically? If the soil moisture probe in the yard could actuate a solenoid to water an area of garden, and do it while coordinating water usage with the rest of the house, and wirelessly?

Weather Station


Anyway, I've been behind on my blogging. I've built an outdoor weather station. You can see the data it logs here: https://cosm.com/feeds/60377

Parts I used:
  • EtherTen
  • DHT22 temperature and humidity sensor
  • A light dependent resistor (LDR) and 10kohm resistor
  • Power of Ethernet injector
  • Some nice hinged and clipped weatherproof enclosures from Jaycar
  • An old 9V DC plug-pack power adaptor I had lying around

Circuit

The basic circuit is simple. 5V and GND are taken from the EtherTen to feed the DHT22 and the LDR. The LDR is connected 5V -> 10kR -> Analog 0 Input -> LDR -> Ground. The DHT22 is connected to 5V, ground and the data leg is connected to pin 7.



Code

/*
  Pachube sensor client
 
 This sketch connects an analog sensor to Pachube (http://www.pachube.com)
 using a Wiznet Ethernet shield. You can use the Arduino Ethernet shield, or
 the Adafruit Ethernet shield, either one will work, as long as it's got
 a Wiznet Ethernet module on board.
 
 This example has been updated to use version 2.0 of the Pachube.com API. 
 To make it work, create a feed with a datastream, and give it the ID
 sensor1. Or change the code below to match your feed.
 
 
 Circuit:
 * Analog 0 has LDR input. 5V -> 10kR -> (A0) -> LDR -> Ground
 * DHT22 attached on pin 7
 * Etherten: Equivalent to Ethernet shield attached to pins 10, 11, 12, 13
 
 created 15 March 2010
 updated 16 Mar 2012
 by Tom Igoe with input from Usman Haque and Joe Saavedra
 
http://arduino.cc/en/Tutorial/PachubeClient
 This code is in the public domain.
 
 */

#include <SPI.h>
#include <Ethernet.h>
#include <DHT22.h>
#include 

#define APIKEY         "FBgi4n29h02x7sn2ghZbPIfr7vWSAKxPSktpbnlMdkJtYz0g" // replace your pachube api key here
#define FEEDID         60377 // replace your feed ID
#define USERAGENT      "Conditions at Ashtonfield" // user agent is the project name
#define DHT22_PIN 7

// assign a MAC address for the ethernet controller.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
// fill in your address here:
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

// fill in an available IP address on your network here,
// for manual configuration:
IPAddress ip(10,0,1,20);
// initialize the library instance:
EthernetClient client;

// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
IPAddress server(64,94,18,122);      // numeric IP for api.pachube.com
//IPAddress server(216,52,233,122);      // numeric IP for api.pachube.com
//char server[] = "api.pachube.com";   // name address for pachube API

unsigned long lastConnectionTime = 0;          // last time you connected to the server, in milliseconds
boolean lastConnected = false;                 // state of the connection last time through the main loop
const unsigned long postingInterval = 60000;   //milliseconds delay between updates to Pachube.com

// Setup a DHT22 instance
DHT22 myDHT22(DHT22_PIN);

void setup() {
  // start serial port:
  Serial.begin(115200);
  Serial.println("Cosm 3-sensor test");
 // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // DHCP failed, so use a fixed IP address:
    Ethernet.begin(mac, ip);
  }
}

void loop()
{
  // This is where HTML is returned from the server and errors are reported.
  
  // if there's incoming data from the net connection.
  // send it out the serial port.  This is for debugging
  // purposes only:
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }

  // if there's no net connection, but there was one last time
  // through the loop, then stop the client:
  if (!client.connected() && lastConnected) {
    //Serial.println();
    //Serial.println("disconnecting.");
    client.stop();
  }

  // if you're not connected, and ten seconds have passed since
  // your last connection, then connect again and send data:
  if(!client.connected() && ((millis() - lastConnectionTime) > postingInterval)) {
    // read sensors
    int light = analogRead(A0);   
    int humidity = 0; //myDHT22.getHumidityInt()/10;
    int temperature = 0; //myDHT22.getTemperatureCInt()/10;
    if (getEnviron(temperature, humidity)) sendData(light, humidity, temperature);
    else Serial.println("Digital sensor read failed");
  }
  // store the state of the connection for next time through
  // the loop:
  lastConnected = client.connected();
}

boolean getEnviron(int& temp, int& rh)
{
  DHT22_ERROR_t errorCode;
  delay(2000);
  //Serial.print("Requesting data...");
  errorCode = myDHT22.readData();
  switch(errorCode)
  {
    case DHT_ERROR_NONE:
      Serial.println("   ***   getEnviron() got data ...");
      //Serial.print(myDHT22.getTemperatureC());
      //Serial.print("C ");
      //Serial.print(myDHT22.getHumidity());
      //Serial.println("%");
      // Alternately, with integer formatting which is clumsier but more compact to store and
   // can be compared reliably for equality:
   //   
      char buf[128];
      sprintf(buf, "Integer-only reading: Temperature %hi.%01hi C, Humidity %i.%01i %%RH",
                   myDHT22.getTemperatureCInt()/10, abs(myDHT22.getTemperatureCInt()%10),
                   myDHT22.getHumidityInt()/10, myDHT22.getHumidityInt()%10);
      //Serial.println(buf);
      rh = myDHT22.getHumidityInt();
      temp = myDHT22.getTemperatureCInt();
      return true;
    case DHT_ERROR_CHECKSUM:
      Serial.print("check sum error ");
      Serial.print(myDHT22.getTemperatureC());
      Serial.print("C ");
      Serial.print(myDHT22.getHumidity());
      Serial.println("%");
      break;
    case DHT_BUS_HUNG:
      Serial.println("BUS Hung ");
      break;
    case DHT_ERROR_NOT_PRESENT:
      Serial.println("Not Present ");
      break;
    case DHT_ERROR_ACK_TOO_LONG:
      Serial.println("ACK time out ");
      break;
    case DHT_ERROR_SYNC_TIMEOUT:
      Serial.println("Sync Timeout ");
      break;
    case DHT_ERROR_DATA_TIMEOUT:
      Serial.println("Data Timeout ");
      break;
    case DHT_ERROR_TOOQUICK:
      Serial.println("Polled to quick ");
      break;
  }
  return false;
}

// this method makes a HTTP connection to the server:
void sendData(int light1, int rh1, int temp1)
{
//  Serial.println("*** ENVIRON");
//  Serial.println(light1);
//  Serial.println(temp1);
//  Serial.println(rh1);
  client.stop();
  delay(500);
  // if there's a successful connection:
  if (client.connect(server, 80)) {
    Serial.println("   ***   sendData() connecting...");
    // send the HTTP PUT request:
    client.print("PUT /v2/feeds/");
    client.print(FEEDID);
    client.println(".csv HTTP/1.1");
    client.println("Host: api.pachube.com");
    client.print("X-PachubeApiKey: ");
    client.println(APIKEY);
    client.print("User-Agent: ");
    client.println(USERAGENT);
    client.print("Content-Length: ");

    char data[128];
    sprintf(data, "light1,%hi\ntemp1,%hi.%01hi\nrh1,%i.%01i", light1, temp1/10, temp1%10, rh1/10, rh1%10);
    
    Serial.println("   ***   sendData() data...");
    Serial.println(data);
    
    int thisLength = -1;
    char* length = data;
    while(*(length++)) thisLength++;
    client.println(thisLength);
    
    // last pieces of the HTTP PUT request:
    client.println("Content-Type: text/csv");
    client.println("Connection: close");
    client.println();

    // here's the actual content of the PUT request:
    client.print(data);
  } 
  else
  {
    // if you couldn't make a connection:
    Serial.println("connection failed");
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
  }
   // note the time that the connection was made or attempted:
  lastConnectionTime = millis();
}


// This method calculates the number of digits in the
// sensor reading.  Since each digit of the ASCII decimal
// representation is a byte, the number of digits equals
// the number of bytes:

int getLength(int someValue)
{
  // there's at least one byte:
  int digits = 1;
  // continually divide the value by ten, 
  // adding one to the digit count for each
  // time you divide, until you're at 0:
  int dividend = someValue /10;
  while (dividend > 0) {
    dividend = dividend /10;
    digits++;
  }
  // return the number of digits:
  return digits;
}

Packaging

With the help of a 'Dremel' tool, I built the project into a weather-poof box from Jaycar.


The DHT22 I cut into the lid and Araldited it in.


Then, with the help of my six year-old daughter, I ran a Cat5 cable through the roof and out under the eave of my house on the southern (shady) side.


Problems

Then the weather turned warm. Really warm. Impossibly warm.

What I hadn't accounted for was the heating effect of the EtherTen which got surprisingly warm. Enough to add 10 degrees to the measured air temperature outside. So it was back to the drawing board. I had to quickly reorganise to separate the sensor from the brain.


Once weather-poofed into separate enclosures, all was well.

That is all.

2 comments:

  1. I had your exact part list in my project bin, so I thought I'd try your code. When I try to compile, I get:

    Humidity_and_Temperature_Sensor_RHT03:131: error: 'class DHT22' has no member named 'getTemperatureCInt'
    Humidity_and_Temperature_Sensor_RHT03:132: error: 'class DHT22' has no member named 'getHumidityInt'

    I am using DHT22 library from here https://github.com/ringerc/Arduino-DHT22/blob/master/README with some updates to support Arduino 1.0. This lib has myDHT22.getTemperatureC rather thant myDHT22.getTemperatureCInt.

    What DHT22 library are you using, and did you add the getTemperatureCInt function?

    Thanks, Jordan

    ReplyDelete
  2. You may want to revisit your external sensor packaging. The DHT/RHT datahsheets are fairly explicit about the sensor not being recommended for outdoor use and the sticking point it seems is that if it gets wet, it will be very unhappy, perhaps forever.

    So my approach is a vented case with baffles so that there's no direct path for water to "splash" the sensor. Also using some flywire to keep the critters from nesting in it.

    Cheers.

    ReplyDelete