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:-
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)
/* 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:
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.
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.
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
Post a Comment