Hi All,
I would like to share my script i am using as an expansion shield for my domoticz home automation system. On this module i connect up to 10 DS18B20 (hardwired over the whole house on a star configuration using the Onewire protocol) as well as 3 DHT11 modules (2 bathrooms and a indoor garden) as well as 4 Finder brand relay modules to control 4 lights (2 for each bathroom). The choice of finde relays was done so that in a bathroom all the lights are interupted on 2 poles, hence the special relays from finder that switch a 5V PSU by using 4 2N2222A NGate transistors.
This sketch is basically a combo from various other posts and scripts made by other users so all credit goes to them. The DS18B20 script qlso stores all chip IDs in the eeprom because i got sick and tired of how they would interchange inside of domoticz and thus jump from one sensor to the other.
NOW my problem i am facing and also my cry for help:
1)Is there anyone that can help me on the timing part of this script as i want to minimize delays caused by the data transfer of all the sensors. currently already miliseconds but still...
2) MY biggest problem is the famous groupID issue within domoticz that i have found numerous posts about but cant seem to figure out or get working... All my DS18B20 show up seperatly now but all are grouped together even with the DHT sensors making them all show up as temp/humidity sensor (NOT OK). Also the DHT sensors are grouped wrong so the temperature and humidity are coupled wrong. In previous version of the script i got i working correctly but i lost it due to PC crash and am pretty stuck right now. I have tried stuff i found in other posts like spacing them apart in presentation (0-16 is for DS18B20 and 50-55 is for DHT with relays in between in 40 range) as well as making everything V_HUM but nothing seems to work....Any help or explaination on how to fix this would be greatly appreciated.
Thx in advance
/**
The MySensors Arduino library handles the wireless radio link and protocol
between your home built sensors/actuators and HA controller of choice.
The sensors forms a self healing radio network with optional repeaters. Each
repeater and gateway builds a routing tables in EEPROM which keeps track of the
network topology allowing messages to be routed to nodes.
Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
Copyright (C) 2013-2015 Sensnology AB
Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
Documentation: http://www.mysensors.org
Support Forum: http://forum.mysensors.org
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*******************************
DESCRIPTION
Example sketch showing how to send in DS1820B OneWire temperature readings back to the controller
http://www.mysensors.org/build/temp
Enhanced Version to keep SensorID icluding a lot of ideas from leodesigner and hauswart (forum.fhem.de)
Improvements:
- it will remember a sensor index in EEPROM (2 bytes per sensor)
in case if you need to replace or add new sensor to the 1Wire bus - all other sensors will keep own sensor index#
Last modifications:
- Revert back to standard Mysensor EEPROM storage (in line with leodesigner code base)
- First ID for DS has to be defined
- Bug fixed with not changed "spot_used" value
- Present Dallas-Hardware-ID as comment (still not working!!!)
- Improve reading of code by opting for some more subroutines
*/
// Enable debug prints to serial monitor
#define MY_DEBUG
// Enable and select radio type attached
//#define MY_RADIO_NRF24
//#define MY_RF24_PA_LEVEL RF24_PA_MIN // This sets a low-power mode for the radio. Useful if you use the version with the bigger antenna, but don' want to power that from a separate source. It can also fix problems with fake Chinese versions of the radio.
//#define MY_RADIO_RFM69
// Enable serial gateway
#define MY_GATEWAY_SERIAL
#include <SPI.h>
#include <MySensors.h>
#include <DallasTemperature.h>
#include <OneWire.h>
#include <DHT.h>
/////////////////*CODE FOR DS18B20*/////////////////////////////
#define COMPARE_TEMP 1 // Send temperature only if changed?
#define ERASE_HASH // Clear EEPROM, if no 1w-device is present?
#define SEND_ID // Send also Dallas-Addresses?
#define ONE_WIRE_BUS 11 // Pin where dallase sensor is connected
#define MAX_ATTACHED_DS18B20 16
#define EEPROM_DEVICE_ADDR_START 64 // start byte in eeprom for remembering our sensors
#define EEPROM_DEVICE_ADDR_END EEPROM_DEVICE_ADDR_START+MAX_ATTACHED_DS18B20*2
uint8_t DS_First_Child_ID = 0; //First Child-ID to be used by Dallas Bus; set this to be higher than other Child-ID's who need EEPROM storage to avoid conflicts
uint16_t SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
DallasTemperature sensors(&oneWire); // Pass the oneWire reference to Dallas Temperature.
float lastTemperature[MAX_ATTACHED_DS18B20];
uint8_t numSensors = 0;
DeviceAddress tempDeviceAddress; // We'll use this variable to store a found device address
int resolution = 12;
int conversionTime = 0;
char* charAddr = "Check for faults";
uint8_t ts_spot[MAX_ATTACHED_DS18B20]; // array for matching bus-id to EEPROM-index
bool spot_used[MAX_ATTACHED_DS18B20]; // used spot array
// Initialize temperature message
MyMessage msgTemp(0, V_TEMP);
#ifdef SEND_ID
MyMessage msgId(0, V_ID);
#endif
boolean receivedConfig = false;
boolean metric = true;
/////////////////*CODE FOR DHT*/////////////////////////////
#define CHILD_ID_HUM1 51
#define CHILD_ID_TEMP1 50
#define DHT1_DATA_PIN 12
#define SENSOR_TEMP1_OFFSET 0
#define CHILD_ID_HUM2 53
#define CHILD_ID_TEMP2 52
#define DHT2_DATA_PIN 5
#define SENSOR_TEMP2_OFFSET 0
#define CHILD_ID_HUM3 55
#define CHILD_ID_TEMP3 54
#define DHT3_DATA_PIN 6
#define SENSOR_TEMP3_OFFSET 0
// Force sending an update of the temperature after n sensor reads, so a controller showing the
static const uint8_t FORCE_UPDATE_N_READS = 2;
// Must be >1000ms for DHT22 and >2000ms for DHT11
static const uint64_t UPDATE_INTERVAL = 30000;
DHT dht1;
DHT dht2;
DHT dht3;
float lastTemp1;
float lastHum1;
float lastTemp2;
float lastHum2;
float lastTemp3;
float lastHum3;
uint8_t nNoUpdatesTemp1;
uint8_t nNoUpdatesHum1;
uint8_t nNoUpdatesTemp2;
uint8_t nNoUpdatesHum2;
uint8_t nNoUpdatesTemp3;
uint8_t nNoUpdatesHum3;
MyMessage msgTemp1(CHILD_ID_TEMP1, V_TEMP);
MyMessage msgHum1(CHILD_ID_HUM1, V_HUM);
MyMessage msgTemp2(CHILD_ID_TEMP2, V_TEMP);
MyMessage msgHum2(CHILD_ID_HUM2, V_HUM);
MyMessage msgTemp3(CHILD_ID_TEMP3, V_TEMP);
MyMessage msgHum3(CHILD_ID_HUM3, V_HUM);
/////////////////*CODE FOR FINDER RELAYS*/////////////////////////////
#define RELAY_PIN 7 // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
#define NUMBER_OF_RELAYS 4 // Total number of attached relays
#define RELAY_ON 1 // GPIO value to write to turn on attached relay
#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
void before() {
conversionTime = 750 / (1 << (12 - resolution));
// Startup up the OneWire library
sensors.begin();
// requestTemperatures() will not block current thread
sensors.setWaitForConversion(false);
// Fetch the number of attached temperature sensors
numSensors = sensors.getDeviceCount();
// use the 1.1 V internal reference
initialiseIdArray();
for (int sensor=41, pin=RELAY_PIN; sensor<=NUMBER_OF_RELAYS+40; sensor++, pin++) {
// Then set relay pins in output mode
pinMode(pin, OUTPUT);
// Set relay to last known state (using eeprom storage)
digitalWrite(pin, loadState(sensor)?RELAY_ON:RELAY_OFF);
}
}
void presentation() {
// Send the sketch version information to the gateway and Controller
sendSketchInfo("1WIRE SHIELD", "V2");
// Present all sensors to controller
for (int i = 0; i < numSensors && i < MAX_ATTACHED_DS18B20; i++) {
sensors.getAddress(tempDeviceAddress, i);
Serial.print("Hardware presented: ");
charAddr = addrToChar(tempDeviceAddress);
Serial.println(charAddr);
present(ts_spot[i], S_TEMP, charAddr);
sensors.setResolution(tempDeviceAddress, resolution);
}
// Register all sensors to gw (they will be created as child devices)
present(CHILD_ID_TEMP1, S_TEMP);
present(CHILD_ID_HUM1, S_HUM);
present(CHILD_ID_TEMP2, S_TEMP);
present(CHILD_ID_HUM2, S_HUM);
present(CHILD_ID_TEMP3, S_TEMP);
present(CHILD_ID_HUM3, S_HUM);
metric = getControllerConfig().isMetric;
for (int sensor=41, pin=RELAY_PIN; sensor<=NUMBER_OF_RELAYS+40; sensor++, pin++) {
// Register all sensors to gw (they will be created as child devices)
present(sensor, S_BINARY);
}
}
void setup() {
#ifdef SEND_ID
for (int i = 0; i < numSensors && i < MAX_ATTACHED_DS18B20; i++) {
sensors.getAddress(tempDeviceAddress, i);
//charAddr = addrToChar(tempDeviceAddress);
//8 sorgt dafür, dass alle 16 Stellen übermittelt werden
send(msgId.setSensor(ts_spot[i]).set(tempDeviceAddress, 8));
}
#endif
dht1.setup(DHT1_DATA_PIN);
dht2.setup(DHT2_DATA_PIN);
dht3.setup(DHT3_DATA_PIN);
// Sleep for the time of the minimum sampling period to give the sensor time to power up
// (otherwise, timeout errors might occure for the first reading)
sleep(dht1.getMinimumSamplingPeriod());
sleep(dht2.getMinimumSamplingPeriod());
sleep(dht3.getMinimumSamplingPeriod());
}
void loop() {
// Force reading sensor, so it works also after sleep()
dht1.readSensor(true);
dht2.readSensor(true);
dht3.readSensor(true);
//Get Temp DHT1
float temperature1 = dht1.getTemperature();
if (isnan(temperature1)) {
Serial.println("Failed reading temperature from DHT1!");
} else if (temperature1 != lastTemp1 || nNoUpdatesTemp1 == FORCE_UPDATE_N_READS) {
// Only send temperature if it changed since the last measurement or if we didn't send an update for n times
lastTemp1 = temperature1;
if (!metric) {
temperature1 = dht1.toFahrenheit(temperature1);
}
// Reset no updates counter
nNoUpdatesTemp1 = 0;
temperature1 += SENSOR_TEMP1_OFFSET;
send(msgTemp1.set(temperature1, 1));
#ifdef MY_DEBUG
Serial.print("T: ");
Serial.println(temperature1);
#endif
} else {
// Increase no update counter if the temperature stayed the same
nNoUpdatesTemp2++;
}
// Get humidity from DHT library
float humidity1 = dht1.getHumidity();
if (isnan(humidity1)) {
Serial.println("Failed reading humidity from DHT1");
} else if (humidity1 != lastHum1 || nNoUpdatesHum1 == FORCE_UPDATE_N_READS) {
// Only send humidity if it changed since the last measurement or if we didn't send an update for n times
lastHum1 = humidity1;
// Reset no updates counter
nNoUpdatesHum1 = 0;
send(msgHum1.set(humidity1, 1));
#ifdef MY_DEBUG
Serial.print("H: ");
Serial.println(humidity1);
#endif
} else {
// Increase no update counter if the humidity stayed the same
nNoUpdatesHum1++;
}
float temperature2 = dht2.getTemperature();
if (isnan(temperature2)) {
Serial.println("Failed reading temperature from DHT2!");
} else if (temperature2 != lastTemp2 || nNoUpdatesTemp2 == FORCE_UPDATE_N_READS) {
// Only send temperature if it changed since the last measurement or if we didn't send an update for n times
lastTemp2 = temperature2;
if (!metric) {
temperature2 = dht2.toFahrenheit(temperature2);
}
// Reset no updates counter
nNoUpdatesTemp2 = 0;
temperature2 += SENSOR_TEMP2_OFFSET;
send(msgTemp2.set(temperature2, 1));
#ifdef MY_DEBUG
Serial.print("T: ");
Serial.println(temperature2);
#endif
} else {
// Increase no update counter if the temperature stayed the same
nNoUpdatesTemp2++;
}
// Get humidity from DHT library
float humidity2 = dht2.getHumidity();
if (isnan(humidity2)) {
Serial.println("Failed reading humidity from DHT2");
} else if (humidity2 != lastHum2 || nNoUpdatesHum2 == FORCE_UPDATE_N_READS) {
// Only send humidity if it changed since the last measurement or if we didn't send an update for n times
lastHum2 = humidity2;
// Reset no updates counter
nNoUpdatesHum2 = 0;
send(msgHum2.set(humidity2, 1));
#ifdef MY_DEBUG
Serial.print("H: ");
Serial.println(humidity2);
#endif
} else {
// Increase no update counter if the humidity stayed the same
nNoUpdatesHum2++;
}
float temperature3 = dht3.getTemperature();
if (isnan(temperature3)) {
Serial.println("Failed reading temperature from DHT3!");
} else if (temperature3 != lastTemp3 || nNoUpdatesTemp3 == FORCE_UPDATE_N_READS) {
// Only send temperature if it changed since the last measurement or if we didn't send an update for n times
lastTemp3 = temperature3;
if (!metric) {
temperature3 = dht3.toFahrenheit(temperature3);
}
// Reset no updates counter
nNoUpdatesTemp3 = 0;
temperature3 += SENSOR_TEMP3_OFFSET;
send(msgTemp3.set(temperature3, 1));
#ifdef MY_DEBUG
Serial.print("T: ");
Serial.println(temperature3);
#endif
} else {
// Increase no update counter if the temperature stayed the same
nNoUpdatesTemp3++;
}
// Get humidity from DHT library
float humidity3 = dht3.getHumidity();
if (isnan(humidity3)) {
Serial.println("Failed reading humidity from DHT3");
} else if (humidity3 != lastHum3 || nNoUpdatesHum3 == FORCE_UPDATE_N_READS) {
// Only send humidity if it changed since the last measurement or if we didn't send an update for n times
lastHum3 = humidity3;
// Reset no updates counter
nNoUpdatesHum3 = 0;
send(msgHum3.set(humidity3, 1));
#ifdef MY_DEBUG
Serial.print("H: ");
Serial.println(humidity3);
#endif
} else {
// Increase no update counter if the humidity stayed the same
nNoUpdatesHum3++;
}
// Sleep for a while to save energy
sleep(UPDATE_INTERVAL);
// Fetch temperatures from Dallas sensors
sensors.requestTemperatures();
// query conversion time and sleep until conversion completed
// sleep() call can be replaced by wait() call if node need to process incoming messages (or if node is repeater)
wait(conversionTime);
// Read temperatures and send them to controller
for (int i = 0; i < numSensors && i < MAX_ATTACHED_DS18B20; i++) {
// Fetch and round temperature to one decimal
float temperature = static_cast<float>(static_cast<int>((metric ? sensors.getTempCByIndex(i) : sensors.getTempFByIndex(i)) * 10.)) / 10.;
// Only send data if temperature has changed and no error
#if COMPARE_TEMP == 1
if (lastTemperature[i] != temperature && temperature != -127.00 && temperature != 85.00) {
#else
if (temperature != -127.00 && temperature != 85.00) {
#endif
// Send in the new temperature
send(msgTemp.setSensor(ts_spot[i]).set(temperature, 1));
// Save new temperatures for next compare
lastTemperature[i] = temperature;
}
}
wait(SLEEP_TIME);
}
// Subroutines
//##########################################################
//
void initialiseIdArray() {
uint8_t knownSensors = 0;
#ifdef ERASE_HASH
if (numSensors < 1) {
for (uint8_t i = EEPROM_DEVICE_ADDR_START; i < EEPROM_DEVICE_ADDR_END + 1; i++) {
saveState(i, 0xFF); //0xFF seems to be better in line with mysensors standards, was 0x00
}
Serial.println("EEPROM cleared...");
}
#endif
//initialise spot_used array to default false
for (uint8_t i = 0; i < MAX_ATTACHED_DS18B20; i++) {
spot_used[i] = false;
}
// first loop: filling Address-Array with IDs already stored, and clear used spot array
for (uint8_t i = 0; i < numSensors && i < MAX_ATTACHED_DS18B20; i++) {
sensors.getAddress(tempDeviceAddress, i);
// check if we know this sensor
int8_t sidx = getSensorIndex(tempDeviceAddress);
if (sidx < 0) {
ts_spot[i] = 0;
}
else {
// we know this sensor
ts_spot[i] = sidx + DS_First_Child_ID;
spot_used[sidx] = true;
knownSensors++;
}
}
#ifdef MY_DEBUG
Serial.println();
Serial.print(F("# Stored Devices: "));
Serial.println(knownSensors);
#endif
// second loop for filling Address-Array with IDs not stored yet
for (uint8_t i = 0; i < numSensors && i < MAX_ATTACHED_DS18B20; i++) {
sensors.getAddress(tempDeviceAddress, i);
// check if we do not know this sensor yet
if (ts_spot[i] == 0) {
uint8_t k = getUnusedSpot();
ts_spot[i] = k + DS_First_Child_ID;
spot_used[k] = true;
storeSensorAddr(tempDeviceAddress, k);
}
}
#ifdef MY_DEBUG
for (int i = 0; i < numSensors && i < MAX_ATTACHED_DS18B20; i++) {
sensors.getAddress(tempDeviceAddress, i);
Serial.println();
Serial.print(i);
Serial.print(F(" index: "));
Serial.print(ts_spot[i]);
Serial.print(F(" address: "));
charAddr = addrToChar(tempDeviceAddress);
Serial.println(charAddr);
#endif
}
}
// a simple hash function for 1wire address to reduce id to two bytes
uint16_t simpleAddrHash(DeviceAddress a) {
return ((a[1] ^ a[2] ^ a[3] ^ a[4] ^ a[5] ^ a[6]) << 8) + a[7];
}
// search for device address hash in eeprom
// return -1 if not found
int8_t getSensorIndex(DeviceAddress a) {
uint16_t hash = simpleAddrHash(a);
int8_t idx = -1;
uint8_t aidx = 0;
uint8_t ptr = EEPROM_DEVICE_ADDR_START;
while (ptr < EEPROM_DEVICE_ADDR_END && idx == -1) {
uint8_t hash1 = loadState(ptr);
uint8_t hash2 = loadState(ptr + 1);
if ( hash1 == (uint8_t)(hash >> 8) && hash2 == (uint8_t)(hash & 0x00FF)) {
// found device index
idx = aidx;
}
aidx++;
ptr += 2;
}
return idx;
}
// search for first unused spot in eeprom
uint8_t getUnusedSpot() {
int8_t j = 0;
while (spot_used[j] == true) {
j++;
}
return j;
}
// save address hash in EEPROM under index
void storeSensorAddr(DeviceAddress a, uint8_t index) {
uint16_t hash = simpleAddrHash(a);
uint8_t ptr = EEPROM_DEVICE_ADDR_START + index * 2;
if (ptr < EEPROM_DEVICE_ADDR_END) {
saveState(ptr, hash >> 8);
saveState(ptr + 1, hash & 0x00FF);
}
#ifdef MY_DEBUG
Serial.print(F("storeSensorAddr under index: "));
Serial.println(index);
#endif
}
char* addrToChar(uint8_t* data) {
String strAddr = String(data[0], HEX); //Chip Version; should be higher than 16
byte first ;
int j = 0;
for (uint8_t i = 1; i < 8; i++) {
if (data[i] < 16) strAddr = strAddr + 0;
strAddr = strAddr + String(data[i], HEX);
strAddr.toUpperCase();
}
for (int j = 0; j < 16; j++) {
charAddr[j] = strAddr[j];
}
return charAddr;
}
void receive(const MyMessage &message)
{
// We only expect one type of message from controller. But we better check anyway.
if (message.type==V_STATUS) {
// Change relay state
digitalWrite(message.sensor-41+RELAY_PIN, message.getBool()?RELAY_ON:RELAY_OFF);
// Store state in eeprom
saveState(message.sensor, message.getBool());
// Write some debug info
Serial.print("Incoming change for sensor:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(message.getBool());
}
}