Hi,
I have searched, but very new to this, so apologies if I've missed something obvious.
I'd like to have an EmonTH reporting three temperature figures: bottom of tank, top of tank (both with DS18B20) and ambient (on board sensor).
I've had a look at the EmonTH firmware and see that it doesn't support multiple DS18B20 sensors out of the box - we check if at least one is attached, then read from the first.
I think can manage to get the readings from multiple sensors - getTempC(allAddress[i]) for i in 0..j ?
Do I then just need to modify the Payload struct as follows and it will automagically appear on emoncms.org as an extra input?
typedef struct { int temp; int temp_external1; int temp_external2; int humidity; int battery; } Payload;
If not, I could use a hint on how to update the payload.
Thinking as I type, is it safe to assume that oneWire.search() returns deterministically based on some ordering of the sensor serial numbers? If so, the sensors will retain ordering after a reset, but if not, I need another steer on how to make sure that each physical sensor stays tied to their respective inputs.
Thanks in anticipation,
Re: EmonTH with multiple DS18B20
A few things to consider... or things that I struggled with anyway ... :
best to fix the addresses as I found that the software can behave in a random way otherwise:
Here's my payload:
typedef struct { int channel, TriacFires , Loops;
int vrms, irms, real_power, apparent_power, power_factor,joules,solar_real_power, temperature0, temperature1, temperature2 ;
} PayloadTX; // create structure - a neat way of packaging data for RF comms
PayloadTX emontx;
Three are listed below as examples:
DeviceAddress OutsideTemp={0x28, 0xB0, 0x0E, 0x55, 0x04, 0x00, 0x00, 0x7C};
DeviceAddress TankTop ={0x28, 0x28, 0xF5, 0x4D, 0x04, 0x00, 0x00, 0x9E};
DeviceAddress TankBottom ={0x28, 0xF0, 0x1E, 0x4E, 0x04, 0x00, 0x00, 0xD9};
This checks how many sensors are on the bus:
numberOfDevices= sensors.getDeviceCount();
Serial.print("Temperature sensors found : ");Serial.println(numberOfDevices);
This checks that the sensors are as expected:
if (!sensors.getAddress(OutsideTemp, 0)) Serial.println("Unable to find address for Outside temperature device ... check hard coded address");
if (!sensors.getAddress(TankTop, 2)) Serial.println("Unable to find address for Top of tank temperature device ... check hard coded address");
if (!sensors.getAddress(TankBottom , 1)) Serial.println("Unable to find address for Bottom of tank temperature device ... check hard coded address");
This sets the resolution of each sensor based upon the stated address:
sensors.setResolution(OutsideTemp, 11);
sensors.setResolution(TankTop, 11);
sensors.setResolution(TankBottom, 11);
This verifies that the resolution has been set:
Serial.print("Sensor resolution is set to : ");
Serial.print(sensors.getResolution(OutsideTemp), DEC); Serial.print(" ");
Serial.print(sensors.getResolution(TankTop), DEC); Serial.print(" ");
Serial.println(sensors.getResolution(TankBottom), DEC);
This requests the sensor temperatures:
sensors.requestTemperatures();
This reads the Outside temperature sensor and stores it in a temporary variable:
float temp=(sensors.getTempC(OutsideTemp)); // 3 currently supported. Add or remove units as you see fit
This writes the data into a payload structure multiplying the result by 1, 10 or 100 as there is no way to send fractional data in a payload (you will need to scale it down again at the receiver)
emontx.temperature0=(temp)*ScaleBy;
temp=(sensors.getTempC(TankTop));
emontx.temperature1=(temp)*ScaleBy;
temp=(sensors.getTempC(TankBottom));
emontx.temperature2=(temp)*ScaleBy;
Hope this helps
Ian
Re: EmonTH with multiple DS18B20
Excellent, thanks. I'd missed the whole hardcoding of addresses completely.
Time to get busy with the soldering iron so I can give this a try.
Re: EmonTH with multiple DS18B20
To add to this, I found it useful to keep a log of all my temperature sensors and their respective addresses. So I have labelled them all and have a notepad file with the list of addresses on it. Saves confusion later or having to find their addresses everytime!
Re: EmonTH with multiple DS18B20
Thanks, that's a good tip. I've got a grab bag of ten to play with so it's probably quite a wise idea to churn through them now and document...
I managed to modify the EmonTH default firmware to report two external sensors without too much trouble. I was worried that embedded would be a major departure from my day job but it's all nicely abstracted. The fact that the Pi just passes the extra payload straight through to emoncms which picks it up automatically was the icing on the cake :)
Although it's almost trivial in retrospect, I'm going to attempt a wiki update for this anyway, as I suspect it's a common 'recipe' and I think it's a nice illustration for people like me (technical but no hardware background) that the emon kit is a great way to go...
Re: EmonTH with multiple DS18B20
Please let me know when you have some working code, if you're happy it would be great to push an emonTH multi DS18B20 example to the emonTH github
Re: EmonTH with multiple DS18B20
My emonTH has the DHT22. I'm trying to work out what would happen if my sketch ran on a module with the DS18B20 on board.
Does that come wired to the same OneWire pin as the external sensors, so it would mainly be a question of establishing the address?
Re: EmonTH with multiple DS18B20
I came across this thread while doing a search prior to uploading my own hot water tank monitor solution. It is currently running on two Jeenodes, the Rx has an GLCd type display, but will be ported to a Jeenode Tx and EmonGLCD Rx \ display over the next week or so. I will upload the current code over the weekend.
Re: EmonTH with multiple DS18B20
The plan is to transmit the remote battery voltage back to the display and integrate this into a new screen on the GLCD code.
Likewise the Template code shown in the next post.
/*
Tank Temperature Display
Written by Simon K - 16/11/2014
Last updated: - See footer.
Based on wireless emonGLCD by: Glyn Hudson, Trystan Lea
Builds upon JeeLabs RF12 library and Arduino.
Note that although the physical module is called 'RFM12B',
the library is 'RF12'
*/
//------------------------------------------------------------------------------------------------------------------------------------------------
// emonGLCD Home Energy Monitor example
// emonGLCD documentation http://openEnergyMonitor.org/emon/emonglcd
// Correct time is updated via NanodeRF which gets time from internet
// GLCD library by Jean-Claude Wippler: JeeLabs.org
// 2010-05-28 <jcw@equi4.com> http://opensource.org/licenses/mit-license.php
// THIS SKETCH REQUIRES:
// - OneWire library http://www.pjrc.com/teensy/td_libs_OneWire.html
// - DallasTemperature http://download.milesburton.com/Arduino/MaximTemperature
// or https://github.com/milesburton/Arduino-Temperature-Control-Library
// - JeeLib https://github.com/jcw/jeelib
// - RTClib https://github.com/jcw/rtclib
// - GLCD_ST7565 https://github.com/jcw/glcdlib
//
// Other files in project directory (should appear in the arduino tabs above)
// - icons.ino
// - temp_templates.ino
//
//-------------------------------------------------------------------------------------------------------------------------------------------------
#include <JeeLib.h>
#include <GLCD_ST7565.h>
#include <avr/pgmspace.h>
GLCD_ST7565 glcd;
#include <RTClib.h>
// Real time clock (RTC) - used for software RTC to reset kWh counters at midnight
#include <Wire.h>
// Part of Arduino libraries - needed for RTClib
RTC_Millis RTC;
//--------------------------------------------------------------------------------------------
// RFM12B Settings
//--------------------------------------------------------------------------------------------
#define MYNODE 15
// Should be unique on network, node ID 30 reserved for base station
#define freq RF12_868MHZ
// frequency - match to same frequency as RFM12B module (change to 868Mhz or 915Mhz if appropriate)
#define group 200
unsigned long fast_update;
unsigned long slow_update;
double temp,maxtemp,mintemp;
//---------------------------------------------------
// Data structures for transferring data between units
//---------------------------------------------------
typedef struct { int temp1, temp2, temp3; } PayloadTX;
// neat way of packaging data for RF comms
PayloadTX emontx;
// Kept for when I transmit data up to the emoncms site.
// typedef struct { int temp1, temp2, temp3, battery; } PayloadTemp;
// PayloadTemp emontemp;
int hour = 12, minute = 0;
double usekwh = 0;
//int cval_use; Replace with 'top_val' etc.
int top_val;
int mid_val;
int low_val;
int volts =5;
//--------------------------------------------------------------------------------------------
// Flow control
//--------------------------------------------------------------------------------------------
unsigned long last_emontx;
// Used to count time from last emontx update
unsigned long last_emonbase;
// Used to count time from last emontx update
void setup()
{
Serial.begin(9600);
delay(500);
//wait for power to settle before firing up the RF
rf12_initialize(MYNODE, freq, group);
delay(100);
//wait for RF to settle before turning on display
glcd.begin(0x18);
glcd.backLight(200);
}
void loop()
{
if (rf12_recvDone())
{
if (rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0) // and no rf errors
{
int node_id = (rf12_hdr & 0x1F);
if (node_id == 10) {emontx = *(PayloadTX*) rf12_data; last_emontx = millis();}
//Assuming 10 is the emonTx NodeID
/*
if (node_id == 12) //Assuming 12 is the emonBase node ID
{
RTC.adjust(DateTime(2012, 1, 1, rf12_data[1], rf12_data[2], rf12_data[3]));
last_emonbase = millis();
}
*/
}
}
//--------------------------------------------------------------------------------------------
// Display update every 200ms
//--------------------------------------------------------------------------------------------
if ((millis()-fast_update)>200)
{
fast_update = millis();
DateTime now = RTC.now();
int last_hour = hour;
hour = now.hour();
minute = now.minute();
top_val = (emontx.temp1);
mid_val = (emontx.temp2);
low_val = (emontx.temp3);
//volts = (emontx.battery);
Serial.print(emontx.temp1);
Serial.println( );
draw_temp_page( "Top" ,top_val, "Mid", mid_val, "Low", low_val);
draw_temperature_volts_footer(temp, volts);
glcd.refresh();
// int LDR = analogRead(LDRpin); (Not used as no LDR fitted).
// Read the LDR Value so we can work out the light level in the room.
// int LDRbacklight = map(LDR, 0, 1023, 50, 250);
// Map the data from the LDR from 0-1023 (Max seen 1000) to var GLCDbrightness min/max
//int LDRbacklight = (50);
// Set back light level as no LDR fitted. Must be between 0 and 255.
//LDRbacklight = constrain(LDRbacklight, 0, 255);
// Constrain the value to make sure its a PWM value 0-255
//if ((hour > 22) || (hour < 5)) glcd.backLight(0); else glcd.backLight(LDRbacklight);
// Turn the back-light off at night.
}
/*
if ((millis()-slow_update)>5000)
{
slow_update = millis();
Temperature sensing removed for now;
sensors.requestTemperatures();
temp = (sensors.getTempCByIndex(0));
if (temp > maxtemp) maxtemp = temp;
if (temp < mintemp) mintemp = temp;
// Temperature sending remove for now.
// emonglcd.temperature = (int) (temp * 100);
// set emonglcd payload
// rf12_sendNow(0, &emonglcd, sizeof emonglcd);
// send temperature data via RFM12B using new rf12_sendNow wrapper -glynhudson
rf12_sendWait(2);
}
*/
}
Re: EmonTH with multiple DS18B20
Requires modifying to add the battery voltage collection and an outside air temperature sensor.
Work in progress. All credit to the original authors, without who's talents I would not have been able to get my head around all this and produce a branch of THEIR code.
#include "utility/font_helvB24.h"
#include "utility/font_helvB14.h"
#include "utility/font_helvB12.h"
#include "utility/font_clR4x6.h"
#include "utility/font_clR6x8.h"
//------------------------------------------------------------------
// Draws a page showing a single power and energy value in big font
//------------------------------------------------------------------
void draw_temp_page(char* upper, double top_val, char* middle, double mid_val, char* lower, double low_val)
{
glcd.clear();
glcd.fillRect(0,0,128,64,0);
char str[50]; //variable to store conversion
glcd.setFont(font_clR6x8);
strcpy(str,upper); // Draw the value of temp1
strcat(str," of Tank:");
glcd.drawString(0,0,str);
strcpy(str,middle);
strcat(str," of Tank:");
glcd.drawString(0,16,str); //was 38
strcpy(str,lower);
strcat(str," of Tank:");
glcd.drawString(0,32,str);
// Top of tank
glcd.setFont(font_helvB12);
itoa((int)top_val,str,10);
strcat(str,"c");
glcd.drawString(80,0,str);
// Middle of tank.
glcd.setFont(font_helvB12);
itoa((int)mid_val,str,10);
strcat(str,"c");
glcd.drawString(80,16,str);
// Bottom of tank.
glcd.setFont(font_helvB12);
itoa((int)low_val,str,10);
strcat(str,"c");
glcd.drawString(80,32,str);
}
//------------------------------------------------------------------
// Draws a footer showing outside ait temperature and the remote battery voltage
//------------------------------------------------------------------
// void draw_temperature_time_footer(double temp, double mintemp, double maxtemp, double hour, double minute)
void draw_temperature_volts_footer(double temp, double volts)
{
glcd.drawLine(0, 47, 128, 47, WHITE); //middle horizontal line
char str[50];
// Draw Temperature
glcd.setFont(font_helvB12);
dtostrf(temp,0,1,str);
strcat(str,"C");
glcd.drawString(0,50,str);
// Draw Volts
glcd.setFont(font_helvB12);
itoa((int)volts,str,10);
strcat(str,"V");
glcd.drawString(80,50,str);
}