Sunday, July 15, 2012

Working Prototypes

Inside Station plugged into the computer.

Outside on the front porch running on battery.. Under cover so the rain doesn't get to it too much.  Two days on the Li-ion battery and it still shows ~4 volts. Almost fully charged.

Since the radio is using SPI, I set the display up as I2C, I follow the two rules of micro controllers - Unused memory is wasted memory, and Unused pins are wasted pins.

The data is being sent to  https://thingspeak.com/channels/2624 - I am going to try and let it run for a few days and not monkey with the code too much.  I have code to measure the battery voltage - but I am going to resist it for the time being, and just let it run...




Friday, July 13, 2012

DIY Remote Temperature Logging Software



I bread boarded all the parts together using the default pin numbers mentioned in each part's documentation and started investing time in getting the software working together.

Once I get a schematic editor working - I'll get the schematic here. Sorry for any delay

The software and Libraries that it took to make it all work.

RF24 - This is used to drive the radios. The PingPair_Sleepy example sketch is the basis of the project.  Getting two Arduinos working on the same computer is a bit of a challenge.  It helps that with this sketch the two Arduino are running the same code.  Who is talking and who is listening is controlled by pin7.  If it is high - you are the talker and if pin7 is at ground - that Arduino is listening, a nice touch. The pin7 is tested and routes the program flow to the proper code to talk or listen. It helps that you actually run a jumper from pin7 to VCC+. It gets somewhat iffy if it is just left floating and assumed to be high.

I got stuck at first by changing the code to send an array of floats over the radio.  I got confused by declaring the array length and actually accessing its members.  I originally used ht[1] as the two value array.  I needed to have declared it as ht[2] and then reference it as ht[0], and ht[1].  Even though it worked in the sketch declared as ht[1] - when it came to sending the values to the radio using radio.send (&ht,sizeof(ht)) the size of returns the declared array size in bytes which is  4 for 1 member array of float and sent only one value of the array.  The compiler must have stuck a little extra space in memory that allowed a second value in a 1 character array.  Watch out for this.

DHT22 - Adafruit does such a nice job with their libraries and examples.  This was used to get the temperature and humidity from the DHT22 sensor to send back and forth.
Very straight forward - not any problems with library at all.

NueWire's Seriot - Currently I am using this application to send the data to ThingSpeak for display.  It was a bit of a challenge to get it set up and having it send all three values I wanted to graph.  It listens to the serial port and I rotate between the three different values sent 15 seconds apart.  Thing Speak will not allow updates closer than 15 seconds apart.


The Thing Speak Setup is


The operative part of the Seriot config file is


add key="tsSensor-1" value="APIKEY,field1,OutsideTempF"
add key="tsSensor-2" value="APIKEY,field2,OutsideHumidity"
add key="tsSensor-3" value="APIKEY,field3,OutsideTempC"

The data is sent from the sketch as:

###BOD
OutsideTempF,88.52
###EOD


How it all hooks together is:

  • Seriot matches up the first string in the csv line with its matching value in the config file (case counts)
  • Seriot then constructs a call to ThinkSpeak using the field# on the matching line in the file - evidently it only sends one value each call.  I had to put a 15sec wait in the sketch and then rotate through the 3 values to get them all posted using the same APIKEY.
Eventually I will have an Ethernet shield doing this, but the breadboard and the parts on the expansion shield doesn't give enough clearance to plug one in.  I was impatient to get something working so I went this route while I still had a change to move wires around.

Current Arduino Sketch
------------------------------------------------------


#include <SPI.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include "nRF24L01.h"
#include "RF24.h"
#include "printf.h"
#include "DHT.h"

//DHT specific declarations
#define DHTPIN 2     // what pin we're connected to
#define DHTTYPE DHT22   // DHT 22  (AM2302)
DHT dht(DHTPIN, DHTTYPE);

//store the vales to transmit 0= humidity, 1=temp
float ht[2];

//pins used by the nRF24L01 radio chip module
RF24 radio(9,10);

// sets the role of this unit in hardware.  Connect to GND to be the 'pong' listener
// Connect to 5v to be the ping talker
const int role_pin = 7;

// Radio pipe addresses for the 2 nodes to communicate.
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };
typedef enum { role_ping_out = 1, role_pong_back } role_e;
const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"};

// The role of the current running sketch
role_e role;

// Sleep declarations
typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e;
void setup_watchdog(uint8_t prescalar);
void do_sleep(void);
const short sleep_cycles_per_transmission = 4;
volatile short sleep_cycles_remaining = sleep_cycles_per_transmission;
  int counter=0;
void setup(void)
{
   //used to rotate through the three data values sent to ThingSpeak
   counter=0;
  // set up the role pin
  pinMode(role_pin, INPUT);
  digitalWrite(role_pin,HIGH);
  delay(20); // Just to get a solid reading on the role pin
  // read the address pin, establish our role
  if ( digitalRead(role_pin) )
    role = role_ping_out;
  else
    role = role_pong_back;

  Serial.begin(9600);
  //initialize the DHT22 library
  dht.begin();
  printf_begin();
  printf("\n\rRF24/examples/pingpair_sleepy/\n\r");
  printf("ROLE: %s\n\r",role_friendly_name[role]);
  // Only the ping out role sleeps.  Wake up every 4s to send a ping
  if ( role == role_ping_out )
    setup_watchdog(wdt_1s);
  // Setup and configure rf radio
  radio.begin();
  // Open pipes to other nodes for communication
  if ( role == role_ping_out )
  {
    radio.openWritingPipe(pipes[0]);
    radio.openReadingPipe(1,pipes[1]);
  }
  else
  {
    radio.openWritingPipe(pipes[1]);
    radio.openReadingPipe(1,pipes[0]);
  }
  // Start listening
  radio.startListening();
  // Dump the configuration of the rf unit for debugging
  //radio.printDetails();
}

void loop(void)
{
  // Ping out role.  Repeatedly send the current time
  if (role == role_ping_out)
  {
    //get the data from the dht22
    printtemp();
    // First, stop listening so we can talk.
    radio.stopListening();
    // Take the time, and send it.  This will block until complete
    printf("Now sending ");
    Serial.print("sizeog(ht) ");
    Serial.println(sizeof(ht));
    radio.write( &ht, sizeof(ht) );
    // Now, continue listening
    radio.startListening();
    // Wait here until we get a response, or timeout (250ms)
    unsigned long started_waiting_at = millis();
    bool timeout = false;
    while ( ! radio.available() && ! timeout )
      if (millis() - started_waiting_at > 250 )
        timeout = true;
    // Describe the results
    if ( timeout )
    {
      printf("response timed out.\n\r");
    }else{
      // Grab the response, compare, and send to debugging spew
        // Spew it
      radio.read( &ht, sizeof(ht) );
      printf("Got response \r\n");
      Serial.println (ht[1]);
    }
    // Shut down the system
    // Experiment with some delay here to see if it has an effect
    delay(500);
    // Power down the radio.  Note that the radio will get powered back up
    // on the next write() call.
    radio.powerDown();
    // Sleep the MCU.  The watchdog timer will awaken in a short while, and
    // continue execution here.
    while( sleep_cycles_remaining )
      do_sleep();

 sleep_cycles_remaining = sleep_cycles_per_transmission;
  }
  // Pong back role.  Receive each packet, dump it out, and send it back
  if ( role == role_pong_back )
  {
    // if there is data ready
    if ( radio.available() )
    {
      // Dump the payloads until we've gotten everything
      bool done = false;
      while (!done)
      {
        // Fetch the payload, and see if this was the last one.
        done = radio.read( &ht, sizeof(ht) );
        // Spew it.
        //printf("Got payload %lu @ %lu...",got_time,millis());
        //printf("Got payload ...");
//if (isnan(ht[0]) || isnan(ht[1])) {
if (isnan(ht[0]))
        {
          Serial.println("Failed to read from DHT");
        } else {
//convert the temperature to F
float f=(ht[1] * 9.0 / 5.0) + 32.0;
          if (counter==0){
          Serial.println("###BOD");
          Serial.print("OutsideTempF,");
          Serial.println(f);
          Serial.println("###EOD");
          }
          else if(counter==1){
          Serial.println("###BOD");
          Serial.print("OutsideHumidity,");
          Serial.println(ht[0]);
          Serial.println("###EOD");
          }else if (counter==2){
          Serial.println("###BOD");
          Serial.print("OutsideTempC,");
          Serial.println(ht[1]);
          Serial.println("###EOD");
        }
          if(++counter>2) {
          counter=0;
        }
        }//endif isnan
      }//endif radio.available()

      // First, stop listening so we can talk
      radio.stopListening();
      // Send the final one back.
      radio.write( &ht, sizeof(ht) );
      //printf("Sent response.\n\r");
      // Now, resume listening so we catch the next packets.
          delay(15000);//need to put the delay in the ping out code block to sleep longer (later)
      radio.startListening();
    }
  }
}

//gets the data from the DHT22 sensor and sends it to the serial port (only on the sender)
void printtemp()
{
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  ht[0]=h;
  ht[1]=t;
  // check if returns are valid, if they are NaN (not a number) then something went wrong!
  if (isnan(t) || isnan(h)) {
    Serial.println("Failed to read from DHT");
  } else {
    Serial.print("Humidity: ");
    Serial.print(ht[0]);
    Serial.print(" %\t");
    Serial.print("Temperature: ");
    Serial.print(ht[1]);
    Serial.print(" *C\t");
    t = (t * 9.0 / 5.0) + 32.0;
    Serial.print("Temperature: ");
    Serial.print(t);
    Serial.println(" *F");
 
  }
}
// Sleep helpers
// 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(uint8_t prescalar)
{
  prescalar = min(9,prescalar);
  uint8_t wdtcsr = prescalar & 7;
  if ( prescalar & 8 )
    wdtcsr |= _BV(WDP3);

  MCUSR &= ~_BV(WDRF);
  WDTCSR = _BV(WDCE) | _BV(WDE);
  WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE);
}

ISR(WDT_vect)
{
  --sleep_cycles_remaining;
}

void do_sleep(void)
{
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();
  sleep_mode();                        // System sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out
}

// vim:ai:cin:sts=2 sw=2 ft=cpp


Saturday, July 7, 2012

DIY remote temperature Logger - Bits and Pieces

Ever since my weather station console fell off the computer table and broke the LCD, I have wanted to DIY a replacement.  Since we have the heat wave - I am inspired again.
Goal -

  • Outside arduino battery (solar?) radio sender
  • Inside Arduino console - linked to a site that graphs the data
Plan -

  • Document each sub system in separate Blog posts.


This post outlines the Hardware I accumulated for this project.

You will notice that I have a lot of different breakout board modules - For the first go round it is always better, for me, to have an already built module that has been debugged, than to start from scratch.  It might be cheaper to start with all the parts and build it out, but it certainly takes a lot more time. After it all works, I might, and that is a remote might, build a custom board.  But it is highly unlikely.


3.7V/670mAh Rechargeable Lithium-Ion
The battery was a close out - had internal protection circuitry and 650mah - more than enough to drive a sleepy Arduino for a long time,

Lithium rechargeable batterys are dangerious to charge - so I found this on eBay  A bit pricey but it suited me.  It's default charge current was a healthy one amp so I had to replarce the current set resistor to limit the current to ~35% of C for the battery (~4.7K for 350ma).   Little tiny surface mount resistors are out of the question for someone with my eyes. I used a 1/8w resistor on one and a trimmer pot on the other.

Item image


NRF24L01 radio  They are all over eBay for all prices - I got mine from Digole Digital Solution in Canada and they arrived in just a few days.  I have done business with them in the past and liked them.  The Library I found to work the best for this board on the Arduino is the Maniacbug RF24


Item image

Serial Display Adapter - I had a 16X2 display and never managed to include it into any project - simply because it took too many pins and too much code. Since I was on  Digole - got a couple.  A little disappointed that it did not include a command to turn the back light on and off - but that was a very minor point.

Diavolino
Diavilino - My favorite Arduino clone.  The Evil Mad Scientist basic kit costs around  around  $12 -  The biggest plus is that they take all the shields. I have a BUNCH of them.
 I never included the voltage regulator on any board - have always run them from batteries. Mostly 3 AA's. But for this project I thought I would need the Li-Ion - just because I wanted too.  They run @ 16mhz just fine on batteries.

DHT22  temperature-humidity sensor + extras


DHT22 temperature-humidity sensor From Adafruit  Something I had from a while back.



Proto Shield from Adafruit   Eventually when I get enough time, everything will be soldered onto one of these. I can and have created custom circuit boards in the past - but for one offs - nothing beats prototyping with  this shield - usually with a mini solderless breadboard on top. The after all the kinks are worked out - solder it down and call it done.