@gohan
If I am uploading the sketch below then I get the warning from the ArduinoIDE that only few ressources are left / its complaining about stability problems that my occur. (also with debugging disabled)
And if I run this on a ArduinoUNO or Micro then after a few hours the board doesn't respond anymore. I have to power off and on (or reset) to get it working again.
Code:
/**
* 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.
*
*******************************
*
* REVISION HISTORY
* Version 1.0 - Henrik Ekblad
*
* DESCRIPTION
* The W5100 MQTT gateway sends radio network (or locally attached sensors) data to your MQTT broker.
* The node also listens to MY_MQTT_TOPIC_PREFIX and sends out those messages to the radio network
*
* LED purposes:
* - To use the feature, uncomment WITH_LEDS_BLINKING in MyConfig.h
* - RX (green) - blink fast on radio message received. In inclusion mode will blink fast only on presentation received
* - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly
* - ERR (red) - fast blink on error during transmission error or receive crc error
*
* See http://www.mysensors.org/build/esp8266_gateway for wiring instructions.
* nRF24L01+ ESP8266
* VCC VCC
* CE GPIO4
* CSN/CS GPIO15
* SCK GPIO14
* MISO GPIO12
* MOSI GPIO13
*
* Not all ESP8266 modules have all pins available on their external interface.
* This code has been tested on an ESP-12 module.
* The ESP8266 requires a certain pin configuration to download code, and another one to run code:
* - Connect REST (reset) via 10K pullup resistor to VCC, and via switch to GND ('reset switch')
* - Connect GPIO15 via 10K pulldown resistor to GND
* - Connect CH_PD via 10K resistor to VCC
* - Connect GPIO2 via 10K resistor to VCC
* - Connect GPIO0 via 10K resistor to VCC, and via switch to GND ('bootload switch')
*
* Inclusion mode button:
* - Connect GPIO5 via switch to GND ('inclusion switch')
*
* Hardware SHA204 signing is currently not supported!
*
* Make sure to fill in your ssid and WiFi password below for ssid & pass.
*/
// Enable debug prints to serial monitor
#define MY_DEBUG
// ID of the current node
#define MY_NODE_ID 1
// Enables and select radio type (if attached)
//#define MY_RADIO_NRF24
//#define MY_RADIO_RFM69
//#define MY_RADIO_RFM95
#define MY_GATEWAY_MQTT_CLIENT
// Set this node's subscribe and publish topic prefix
#define MY_MQTT_PUBLISH_TOPIC_PREFIX "ff-kitchen-gw1-out"
#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "ff-kitchen-gw1-in"
// Set MQTT client id
#define MY_MQTT_CLIENT_ID "ff-kitchen-1"
// W5100 Ethernet module SPI enable (optional if using a shield/module that manages SPI_EN signal)
//#define MY_W5100_SPI_EN 4
// Enable Soft SPI for NRF radio (note different radio wiring is required)
// The W5100 ethernet module seems to have a hard time co-operate with
// radio on the same spi bus.
#if !defined(MY_W5100_SPI_EN) && !defined(ARDUINO_ARCH_SAMD)
#define MY_SOFTSPI
#define MY_SOFT_SPI_SCK_PIN 14
#define MY_SOFT_SPI_MISO_PIN 16
#define MY_SOFT_SPI_MOSI_PIN 15
#endif
// When W5100 is connected we have to move CE/CSN pins for NRF radio
#ifndef MY_RF24_CE_PIN
#define MY_RF24_CE_PIN 5
#endif
#ifndef MY_RF24_CS_PIN
#define MY_RF24_CS_PIN 6
#endif
// Enable these if your MQTT broker requires username/password
#define MY_MQTT_USER "Username"
#define MY_MQTT_PASSWORD "Password"
#define MY_SENSOR_PASSWORD "Password"
// Enable MY_IP_ADDRESS here if you want a static ip address (no DHCP)
#define MY_IP_ADDRESS xx,x,xx,xxx
// If using static ip you can define Gateway and Subnet address as well
#define MY_IP_GATEWAY_ADDRESS xx,x,xx,xxx
#define MY_IP_SUBNET_ADDRESS xxx,xxx,xxx,x
// MQTT broker ip address or url. Define one or the other.
//#define MY_CONTROLLER_URL_ADDRESS "m20.cloudmqtt.com"
#define MY_CONTROLLER_IP_ADDRESS xx, x, xx, xxx
// The MQTT broker port to to open
#define MY_PORT 1883
/*
// Enable inclusion mode
#define MY_INCLUSION_MODE_FEATURE
// Enable Inclusion mode button on gateway
//#define MY_INCLUSION_BUTTON_FEATURE
// Set inclusion mode duration (in seconds)
#define MY_INCLUSION_MODE_DURATION 60
// Digital pin used for inclusion mode button
//#define MY_INCLUSION_MODE_BUTTON_PIN 3
// Set blinking period
#define MY_DEFAULT_LED_BLINK_PERIOD 300
// Flash leds on rx/tx/err
// Uncomment to override default HW configurations
//#define MY_DEFAULT_ERR_LED_PIN 16 // Error led pin
//#define MY_DEFAULT_RX_LED_PIN 16 // Receive led pin
//#define MY_DEFAULT_TX_LED_PIN 16 // the PCB, on board LED
*/
#include <Ethernet.h>
#include <MySensors.h>
#include <DHT.h>
#include <BH1750.h>
#include <Wire.h>
// Pin configuration
// Interrupt pins: 2,3 (Arduino UNO)
#define PIR_DATA_PIN 2 // PIR Motion sensor
#define DHT_DATA_PIN 3 // Temperature and humidity sensor
#define WINDOW_TOP_REEDCONTACT_DATA_PIN 5 // Reed contact sensor
#define WINDOW_BOTTOM_REEDCONTACT_DATA_PIN 6 // Reed contact sensor
const unsigned int RELAY_DATA_PINS[] = {4, 7, 8, 9}; // Relay module
char* RELAY_ANALOGDATA_PINS[] = { "A0", "A1", "A2", "A3"}; // Relay module
// Settings
#define RELEASE_VERSION "1.0"
bool enableSaveState = false; // Save states in local eeprom
bool metric = true;
const unsigned long WARMUP_TIME = 30000; // Timer for initializing sensors like DHT, PIR, etc...
const unsigned long SENSOR_UPDATE_TIME = 3000; // Timer for periodic sensor readings
const unsigned long FAST_UPDATE_TIME = 250; // Timer for special sensors like PIR
unsigned long lastSensorUpdateTime = 0;
unsigned long lastFastUpdateTime = 0;
// Sensor offsets
// SENSOR_TEMP_OFFSET: Set this offset if the sensor has a permanent small offset to the real temperatures
// SENSOR_MINCHANGE_OFFSET*: Set this offset for wheter sending mqtt updates or not
#define SENSOR_TEMP_OFFSET 0
#define SENSOR_MINCHANGE_OFFSET 0.2
#define SENSOR_MINCHANGE_OFFSET_LARGE 2
// Force sending an update of the temperature after n sensor reads, so a controller showing the
// timestamp of the last update doesn't show something like 3 hours in the unlikely case, that
// the value didn't change since;
// i.e. the sensor would force sending an update every UPDATE_INTERVAL*FORCE_UPDATE_N_READS [ms]
static const uint8_t FORCE_UPDATE_N_READS = 20;
// Set sensor child ids
#define CHILD_ID_MOTION 30
#define CHILD_ID_TEMP 40
#define CHILD_ID_HUM 50
#define CHILD_ID_LIGHT 60
#define CHILD_ID_WINDOW_TOP 5
#define CHILD_ID_WINDOW_BOTTOM 6
// States
#define RELAY_ON 0 // GPIO value to write to turn on attached relay
#define RELAY_OFF 1 // GPIO value to write to turn off attached relay
// MotionSensor
unsigned long lastPirUpdate = 0;
long unsigned int lowIn; // Save the time when the PIR sensor outputs a low impulse.
long unsigned int motionPause = 60000; // The amount of milliseconds the sensor has to be low, before we assume all motion has stopped.
bool lockLow = true;
bool takelowTime;
MyMessage msgMotion(CHILD_ID_MOTION, V_TRIPPED);
void initPirSensor()
{
pinMode(PIR_DATA_PIN, INPUT); // Init PIR motion sensor
}
void updateMotionSensor(bool forceUpdate = false)
{
bool tripped = digitalRead(PIR_DATA_PIN) == HIGH;
#ifdef MY_DEBUG
Serial.print("M: ");
Serial.println(tripped);
#endif
if (tripped == HIGH)
{
if (lockLow)
{
// Make sure to wait for a transition to LOW before any further progress is made.
lockLow = false;
#ifdef MY_DEBUG
Serial.print("Motion detected at ");
Serial.print(millis() / 1000);
Serial.println(" sec.");
#endif
send(msgMotion.set(tripped ? "1" : "0"));
}
takelowTime = true;
}
else if (tripped == LOW)
{
if (takelowTime)
{
lowIn = millis(); // Save the time of the transition from HIGH to LOW.
takelowTime = false; // Make sure this is only done at the start of a new LOW ph.
}
// If the sensor is low for more than the given pause,
// then we assume that nor more motion (human presence) is going to happen.
if (!lockLow && millis() - lowIn > motionPause)
{
// Make sure this code block is only executed after a new motion sequence has been detected.
lockLow = true;
#ifdef MY_DEBUG
Serial.print("Motion ended at ");
Serial.print((millis() - motionPause) / 1000);
Serial.println(" sec.");
#endif
send(msgMotion.set(tripped ? "1" : "0"));
return;
}
}
unsigned long currentMillis = millis();
if (currentMillis - lastPirUpdate > WARMUP_TIME && lockLow)
{
lastPirUpdate = currentMillis;
send(msgMotion.set(tripped ? "1" : "0"));
}
}
// TempSensor
float lastTemp;
float lastHum;
float lastHic;
uint8_t nNoUpdatesTemp;
uint8_t nNoUpdatesHum;
uint8_t nNoUpdatesHic;
MyMessage msgHum(CHILD_ID_HUM, V_HUM);
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
MyMessage msgHic(CHILD_ID_TEMP, V_VAR1);
DHT dht;
void initTempSensor()
{
dht.setup(DHT_DATA_PIN); // set data pin of DHT sensor
if (SENSOR_UPDATE_TIME <= dht.getMinimumSamplingPeriod())
{
#ifdef MY_DEBUG
Serial.println("Warning: UPDATE_INTERVAL is smaller than supported by the sensor!");
#endif
}
// 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)
wait(dht.getMinimumSamplingPeriod());
}
void updateTempSensor()
{
// Force reading sensor, so it works also after sleep()
// Source: https://gist.github.com/jamesabruce/382fe945d39cf41b29f9
dht.readSensor(true);
// Get temperature from DHT library
float temperature = dht.getTemperature();
if (isnan(temperature))
{
#ifdef MY_DEBUG
Serial.println("Failed reading temperature from DHT!");
#endif
}
else if ((temperature != lastTemp && abs(temperature - lastTemp) > SENSOR_MINCHANGE_OFFSET) || nNoUpdatesTemp == 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
lastTemp = temperature;
if (!metric)
{
temperature = dht.toFahrenheit(temperature);
}
// Reset no updates counter
nNoUpdatesTemp = 0;
temperature += SENSOR_TEMP_OFFSET;
send(msgTemp.set(temperature, 1));
#ifdef MY_DEBUG
Serial.print("T: ");
Serial.println(temperature);
#endif
}
else
{
// Increase no update counter if the temperature stayed the same
nNoUpdatesTemp++;
}
// Get humidity from DHT library
float humidity = dht.getHumidity();
if (isnan(humidity))
{
#ifdef MY_DEBUG
Serial.println("Failed reading humidity from DHT");
#endif
}
else if ((humidity != lastHum && abs(humidity - lastHum) > SENSOR_MINCHANGE_OFFSET) || nNoUpdatesHum == FORCE_UPDATE_N_READS)
{
lastHum = humidity;
nNoUpdatesHum = 0;
send(msgHum.set(humidity, 1));
#ifdef MY_DEBUG
Serial.print("H: ");
Serial.println(humidity);
#endif
}
else
{
nNoUpdatesHum++;
}
// Compute heat index in Celsius (isFahreheit = false)
if (isnan(temperature) && isnan(humidity))
{
float hic = dht.computeHeatIndex(temperature, humidity, false);
#ifdef MY_DEBUG
Serial.print("Hic: ");
Serial.println(humidity);
#endif
if ((hic != lastHic && abs(hic - lastHic) > SENSOR_MINCHANGE_OFFSET) || nNoUpdatesHic == FORCE_UPDATE_N_READS)
{
nNoUpdatesHic = 0;
send(msgHic.set(hic, 1));
lastHic = hic;
}
else
{
nNoUpdatesHic++;
}
}
}
// LightSensor
// V_LIGHT_LEVEL should only be used for uncalibrated light level 0-100%.
// If your controller supports the new V_LEVEL variable, use this instead for
// transmitting LUX light level.
MyMessage msgLux(CHILD_ID_LIGHT, V_LIGHT_LEVEL);
// MyMessage msg(CHILD_ID_LIGHT, V_LEVEL);
uint16_t lastlux;
uint8_t nNoUpdatesLux;
BH1750 lightSensor;
void initLightSensor()
{
// Initialize the I2C bus (BH1750 library doesn't do this automatically)
// On esp8266 devices you can select SCL and SDA pins using Wire.begin(D4, D3);
Wire.begin();
lightSensor.begin();
}
void updateLightSensor()
{
uint16_t lux = lightSensor.readLightLevel();
#ifdef MY_DEBUG
Serial.print("L: ");
Serial.println(lux);
#endif
if ((lux != lastlux && abs(lux - lastlux) > SENSOR_MINCHANGE_OFFSET_LARGE) || nNoUpdatesLux == FORCE_UPDATE_N_READS)
{
nNoUpdatesLux = 0;
send(msgLux.set(lux));
lastlux = lux;
}
else
{
nNoUpdatesLux++;
}
}
// ReedContacts
int lastBounceWindowTop = -1;
int lastBounceWindowBottom = -1;
unsigned long lastBounceUpdate = 0;
// Change to V_LIGHT if you use S_LIGHT in presentation below
MyMessage msgWindowTop(CHILD_ID_WINDOW_TOP, V_TRIPPED);
MyMessage msgWindowBottom(CHILD_ID_WINDOW_BOTTOM, V_TRIPPED);
void initReedContact(int pin)
{
pinMode(pin, INPUT);
digitalWrite(pin, HIGH); // Activate internal pull-up
}
void updateReedSensor(int pin, MyMessage &message, int &lastState, bool reset)
{
int state = digitalRead(pin);
#ifdef MY_DEBUG
Serial.print("R: ");
Serial.println(state);
#endif
if (state != lastState)
{
send(message.set(state == HIGH ? 1 : 0));
lastState = state;
return;
}
unsigned long currentMillis = millis();
if (currentMillis - lastBounceUpdate > WARMUP_TIME)
{
if (reset)
{
lastBounceUpdate = currentMillis;
}
send(message.set(state == HIGH ? 1 : 0));
}
}
// Relays Module
void initRelayModules()
{
for (size_t sensor = 0; sensor < (sizeof(RELAY_DATA_PINS) / sizeof(int)); sensor++)
{
pinMode(RELAY_DATA_PINS[sensor], OUTPUT); // Then set relay pins in output mode
digitalWrite(RELAY_DATA_PINS[sensor], loadState(RELAY_DATA_PINS[sensor]) ? RELAY_ON : RELAY_OFF); // Set relay to last known state (using eeprom storage)
}
for (size_t sensor = 0; sensor < (sizeof(RELAY_ANALOGDATA_PINS) / sizeof(int)); sensor++)
{
pinMode(RELAY_ANALOGDATA_PINS[sensor], OUTPUT);
digitalWrite(RELAY_ANALOGDATA_PINS[sensor], RELAY_OFF);
}
}
void presentRelays()
{
for (size_t sensor = 0; sensor < (sizeof(RELAY_DATA_PINS) / sizeof(int)); sensor++)
{
present(RELAY_DATA_PINS[sensor], S_LIGHT);
}
for (size_t sensor = 0; sensor < (sizeof(RELAY_ANALOGDATA_PINS) / sizeof(int)); sensor++)
{
present(RELAY_ANALOGDATA_PINS[sensor], S_LIGHT);
}
}
void setup()
{
// Setup locally attached sensors
initTempSensor(); // Init temperature and humidity sensor. (DHT22)
initLightSensor(); // Init one wire light sensor. (BH1750)
initPirSensor(); // Init PIR sensor
initRelayModules(); // Init relay modules
initReedContact(WINDOW_TOP_REEDCONTACT_DATA_PIN); // Init read contact
initReedContact(WINDOW_BOTTOM_REEDCONTACT_DATA_PIN); // Init read contact
wait(WARMUP_TIME);
}
void presentation()
{
// Present locally attached sensors here
// Optional: Send the sketch version information to the gateway
sendSketchInfo("Multi room sensor", RELEASE_VERSION);
present(CHILD_ID_HUM, S_HUM);
present(CHILD_ID_TEMP, S_TEMP);
present(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
present(CHILD_ID_MOTION, S_MOTION);
presentRelays();
// You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage.
// If S_LIGHT is used, remember to update variable type you send in. See "msg" above.
present(CHILD_ID_WINDOW_TOP, S_DOOR);
present(CHILD_ID_WINDOW_BOTTOM, S_DOOR);
metric = getControllerConfig().isMetric;
}
void loop()
{
// Send locally attached sensors data here
unsigned long currentMillis = millis();
if (currentMillis - lastSensorUpdateTime > SENSOR_UPDATE_TIME)
{
lastSensorUpdateTime = currentMillis;
updateTempSensor();
updateLightSensor();
}
else if (currentMillis - lastFastUpdateTime > FAST_UPDATE_TIME)
{
lastFastUpdateTime = currentMillis;
updateMotionSensor();
updateReedSensor(WINDOW_TOP_REEDCONTACT_DATA_PIN, msgWindowTop, lastBounceWindowTop, false);
updateReedSensor(WINDOW_BOTTOM_REEDCONTACT_DATA_PIN, msgWindowBottom, lastBounceWindowBottom, true);
}
}
void receive(const MyMessage &message)
{
#ifdef MY_DEBUG
Serial.print("Incoming message received. Type: ");
Serial.println(message.type);
Serial.print("Destination: ");
Serial.println(message.destination);
#endif
// We only expect one type of message from controller. But we better check anyway.
if (message.type == V_LIGHT)
{
#ifdef MY_DEBUG
Serial.print("Incoming change for sensor: ");
Serial.println(message.sensor);
Serial.print("New status: ");
Serial.println(message.getBool());
#endif
digitalWrite(message.sensor, message.getBool() ? RELAY_ON : RELAY_OFF);
// Store state in eeprom
// if(enableSaveState) {
// saveState(message.sensor, message.getBool());
// }
}
}