Friday 27 May 2011

First project with the Jeenode and GLCD

 Having had my Jeenodes for a while now and eventually got some code to actually work, reading DS18B20s and displaying the results on a 16x2 line display, I fancied getting to grips with the GLCD, also from Jeelabs.
This is just a project to help me learn about the hardware and software, it may be useful in to someone out there.  I've tried to put plenty of comments in the code, mainly so I remember what I did when I look at it in two weeks time.

My target project is to create a portable (small) display that I can control the central heating from, i.e. room temp., heating on/off and hot water temp. control.  This project is the prototype phase.

At first it was a bit of a slog trying to understand the glcd library and how to send float variables to the screen (C++ is a new language for me), after that came the RTC coding to get the time on the screen, especially with leading zeros.

What I eventually came up with was a display showing the time, with leading zeros (thanks to 'sprintf'), two temperature inputs and a trend line showing just over two hours history for the current temperature.

The temperatures are derived from two DS18B20s connected to port2, adding extra sensors should be easy enough.  Each sensor has a Max. and a Min. point displayed.

The trend line is generated by sampling the temp. every minute, based on the now.minute() value from the RTC and then plotted between rows 40 to 60 using the glcd.setpixel command and the map function to map from 10 to 30 degrees C.  This time could be altered to be shorter or longer, as desired.

The hardware is basically a Jeenode V6, GLCD PCB and display, RTC plug (Port 3) and a DS18B20 soldered to a small prototyping board plugged into port 2.
DS18B20 on the Left and the RTC plug on the Right
 
On the DS81B20 PCB there is also a three pin right angle connector for the exernal DS18B20 probe, bought from Ebay.

The sofware is based on the Jeelabs demo sketches from Jeelabs rtcplug and the GLCD demo sketches.

The temperatures are also sent out over the serial link to the PC, formatted as they are seen on the GLCD. 

There are still some issues and some improvements to be made:-
  • The DS18B20 disconnected detection doesn't seem to work.
  • If the DS18B20 is disconnected and then, when reconnected, the Max temp. reads 85 Deg.
 Future improvements
  • To set the time I used another sketch but I plan to add the ability to set the time using my VB software from the PC.
  • Automatically alter the trend line scale based on the readings.
  • Round up the temp. for the pixel to the nearest degree.
  • Make the trend line update in seconds and put it in a variable. 

Next steps
  • Learn how to send/receive remote data from other Jeenode/Uno
  • Find out if I can use different size fonts on the GLCD (not the really big ones)
Sketch below: -
/* Based on Demo display for the Graphics Board and the rtcplug.pde from Jeelabs
 2010-11-14 <jcw@equi4.com> http://opensource.org/licenses/mit-license.php
 
 Added DS18B20 sensors and RTC plug
 Displays time, internal and external temp with min and max readings there is also a trend
 line drawn at the bottom of the LCD.  It samples the current temp. every minute, using RTC,
 and draws a pixel at a point between row 40 to 60, using the map command for temp's between
 10 and 30 degrees.  It takes 126 min's to draw the line then it clears the line and
 starts again.
*/
#include <GLCD_ST7565.h>
#include <Ports.h>
#include <RF12.h> // needed to avoid a linker error :(
#include <avr/pgmspace.h>
#include <OneWire.h>
#include "Wire.h"
#include <RTClib.h>
#include <DallasTemperature.h> //Version 3.6
#define ONE_WIRE_BUS 5  // DS18S20 Temperature chip i/o on pin 5 - Port 2

GLCD_ST7565 glcd;

EMPTY_INTERRUPT(WDT_vect);

OneWire oneWire(ONE_WIRE_BUS);// Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire);// Pass our oneWire reference to Dallas Temperature. 
// Global variables
char outBuf [25];
int MaxTempIn=0; //hold max temp value
int MinTempIn=500; //hold min temp value, pre load with 500 because the first test is against the temp val * 10  
int MaxTempOut=0; //hold max temp value
int MinTempOut=500; //hold min temp value, pre load with 500 because the first test is against the temp val * 10 
int LastMin; //Hold last min value
int x=1; //counter for trend line
int Ypixel; // Y value for trend line
// Insert the ID of your temp sensor here, for the sketch, visit here
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html
DeviceAddress insideThermometer = { 0x28, 0x2F, 0x8A, 0xEE, 0x02, 0x00, 0x00, 0xE9 }; // none probe
DeviceAddress outsideThermometer = { 0x28, 0x60, 0x3A, 0x10, 0x03, 0x00, 0x00, 0x38 };
DeviceAddress WaterThermometer = { 0x28, 0xB3, 0x58, 0x18, 0x03, 0x00, 0x00, 0x2B };
class Sleepy;

// RTC based on the DS1307 chip connected via the Ports library
class RTC_Plug : public DeviceI2C {
    // shorthand
    static uint8_t bcd2bin (uint8_t val) { return RTC_DS1307::bcd2bin(val); }
    static uint8_t bin2bcd (uint8_t val) { return RTC_DS1307::bin2bcd(val); }
public:
    RTC_Plug (const PortI2C& port) : DeviceI2C (port, 0x68) {}

    void begin() {}
    
    void adjust(const DateTime& dt) {
        send();
        write(0);
        write(bin2bcd(dt.second()));
        write(bin2bcd(dt.minute()));
        write(bin2bcd(dt.hour()));
        write(bin2bcd(0));
        write(bin2bcd(dt.day()));
        write(bin2bcd(dt.month()));
        write(bin2bcd(dt.year() - 2000));
        write(0);
        stop();
    }

    DateTime now() {
       send();
       write(0); 
        stop();

        receive();
        uint8_t ss = bcd2bin(read(0));
        uint8_t mm = bcd2bin(read(0));
        uint8_t hh = bcd2bin(read(0));
        read(0);
        uint8_t d = bcd2bin(read(0));
        uint8_t m = bcd2bin(read(0));
        uint16_t y = bcd2bin(read(1)) + 2000;
    
        return DateTime (y, m, d, hh, mm, ss);
    }
};

PortI2C i2cBus (3); //RTC on port 3
RTC_Plug RTC (i2cBus);

void setup () {
DateTime now = RTC.now();
LastMin = now.minute(); //Store minute value for use in Draw Graph

// Set serial speed
  Serial.begin(57600);
  // Start RTC
 RTC.begin();
// Power down the Transceiver
    rf12_initialize(1, RF12_868MHZ);
    rf12_sleep(0);
// Start the temp sensors    
    sensors.begin();

// set the resolution to 12 bit
    sensors.setResolution(outsideThermometer, 12);
    sensors.setResolution(insideThermometer, 12);
 //sensors.setResolution(WaterThermometer, 12);
    Wire.begin();

 // Initialise the GLCD
    glcd.begin();
    glcd.backLight(255);
    glcd.drawString_P(0,  10, PSTR("       Now  Max  Min")); //Set the column titles
    //glcd.drawLine(0, 8, 120, 8, WHITE);
    glcd.drawRect(0, 8, 128, 56, WHITE);
    glcd.refresh(); // Update the display
}

void loop () {
 DateTime now = RTC.now();
  sensors.requestTemperatures();// Trigger all the temp sensors to carry out a temp. conversion 
  Sleepy::loseSomeTime(2000); // Do nothing for n seconds, slow update OK
 
  printTemperature(insideThermometer); // Read in the temp. data from the inside sensor
  printTemperature(outsideThermometer); // Read in the temp. data from the outside sensor
  UpdateTime();
  //Trigger a pixel to be drawn on the history every minute
  if(now.minute() > LastMin) {
    DrwGraph();
    LastMin = now.minute();
  }
  if(LastMin > now.minute()) {
  LastMin=1; //reset to 1 as rollover to next hour.
  }
  glcd.refresh();
 }
 
void UpdateTime(){ 
 DateTime now = RTC.now();
 sprintf(outBuf, "Time %02d:%02d",now.hour(), now.minute()); //Format the time without seconds
 glcd.drawString(3, 0, outBuf); //Print to Screen RAM
 Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.print(" - ");
    Serial.print(now.day(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.year(), DEC);
    Serial.print("\n");
  }

void printTemperature(DeviceAddress deviceAddress) {
int temp2; // Create variable to hold the int value of the float without the decimal 
float tempC = sensors.getTempC(deviceAddress); // read the specific sensor called from loop.

if (deviceAddress == insideThermometer){ //Display Internal Temp

if (tempC == -127) {
      glcd.drawString(10,  20, PSTR("Error"));
} else {
  temp2 =tempC *10 + 0.5; // make now temp decimal value into an integer. remove decimal point
  Ypixel = temp2/10; // Store Y pixel value temp for trend line
  if (MaxTempIn < temp2){ //Set MaxTempIn to temp2 if higher than old MaxtempIn
    MaxTempIn=temp2;
}
if (MinTempIn > temp2) { //Set MinTempIn to temp2 if Lower than old MinTempIn
 MinTempIn = temp2;
}
  // Print Inside Temps, Now, Max and Min.
  //Format the integer back into integer and decimal 
  sprintf(outBuf, "In    %d.%d %d.%d %d.%d",temp2/10, temp2%10,MaxTempIn/10, MaxTempIn%10,MinTempIn/10, MinTempIn%10); 
  glcd.drawString(3, 20, outBuf); //Print to Screen RAM
  Serial.print("      Now  Max  Min");
  Serial.print("\n");
  Serial.print(outBuf); //send data to host 
  Serial.print("\n");
}
}v 
if (deviceAddress == outsideThermometer){ //Display Outside Temp
if (tempC == -127) {
      glcd.drawString(10,  30, PSTR("Error"));
} else {
  temp2 =tempC *10 + 0.5; // make now temp decimal value into an integer. remove decimal point
  if (MaxTempOut < temp2){ //Set Max temp2 if higher than old Maxtemp
    MaxTempOut=temp2;
}
if (MinTempOut > temp2) { //Set Min temp2 if Lower than old Mintemp
 MinTempOut = temp2;
}
// Print Outside Temps, Now, Max and Min.
//Format the integer back into integer and decimal 
  sprintf(outBuf, "Out   %d.%d %d.%d %d.%d",temp2/10, temp2%10,MaxTempOut/10, MaxTempOut%10,MinTempOut/10, MinTempOut%10);
  glcd.drawString(3, 30, outBuf); //Print to Screen RAM
  Serial.print(outBuf); //send data to host 
  Serial.print("\n");
}
}
}
void DrwGraph(){ //Plot next pixel of temp history
if (x < 126){ //Check if the end of the visible screen is reached
x++; //Increment x for next pixel
// draw pixel, Y value mapped from temp value to Y pixel range, reversed.
glcd.setPixel(x,map(Ypixel,10,30,60,40),WHITE); 
}
else{
glcd.fillRect(1,40,126,20, BLACK); //Draw rectangle in Black to erase previous line
x=1; // Start at left hand of visible screen again.
}
}

4 comments:

Tankslappa said...

Nice write up Gadget, sorry the gLCD library was a bit of a struggle, it does all make sense in the end, honest! Although I might be biased as I wrote it with JC over last Christmas. If you have any questions give me a shout, I might be able to explain the logic behind how things work.

I've noticed the 85degree not connected issue on the DS18B20 too, I really must sit down an look at the code as opposed to just taking snippets from about the net and accepting them at their face value.

Gadjet said...

Thanks for the feedback Tankslappa, it's not the fault of the library, without it I'd still be figuring out what to do with the display, it's just that C++ has some strange ways of writing things that I'm not yet familiar with, sprintf took me a couple of hours on the net to get it to do what I wanted.

erectile dysfunction natural remedies said...
This comment has been removed by a blog administrator.
Anonymous said...

Amazing blog! Is your theme custom made or did you download it from somewhere? A design like yours with a few simple adjustements would really make my blog shine. Please let me know where you got your theme. Bless you

 Time for another post. A while ago I bought a new Oscilloscope, a RIGOL DHO914, but this scope doesn't have WiFi built in just a LAN co...

Popular Posts