EmonTH with multiple DS18B20

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,

Series530's picture

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

 

Schism's picture

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.

charliemic's picture

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!

Schism's picture

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...

glyn.hudson's picture

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

Schism's picture

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?

Simonk695's picture

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.

Simonk695's picture

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);
  
 }
*/ 
}

Simonk695's picture

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);
}

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.