Add library status indication & improve error LED feedback (#462)

* Added MyIndication to provide status feedback from the library

* Fix: Allow led blinking for hardware errors

* Refactor LED blinking implementation to show blink patterns

* Refactor LED blinking to show blink pattern
This commit is contained in:
Embedded Innovation
2016-06-06 18:43:42 +02:00
parent bc7e24695d
commit e0e78a6bae
11 changed files with 363 additions and 73 deletions
+2
View File
@@ -142,6 +142,8 @@
#include "core/MyLeds.h"
#endif
#include "core/MyIndication.cpp"
// INCLUSION MODE
#if defined(MY_INCLUSION_MODE_FEATURE)
@@ -151,6 +151,8 @@ bool gatewayTransportSend(MyMessage &message)
bool ret = true;
char *_ethernetMsg = protocolFormat(message);
setIndication(INDICATION_GW_TX);
_w5100_spi_en(true);
#if defined(MY_CONTROLLER_IP_ADDRESS)
#if defined(MY_USE_UDP)
@@ -267,6 +269,7 @@ bool gatewayTransportAvailable()
if (packet_size) {
//debug(PSTR("UDP packet available. Size:%d\n"), packet_size);
setIndication(INDICATION_GW_RX);
#if defined(MY_GATEWAY_ESP8266)
_ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
inputString[0].string[packet_size] = 0;
@@ -316,6 +319,7 @@ bool gatewayTransportAvailable()
// Loop over clients connect and read available data
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
if (_readFromClient(i)) {
setIndication(INDICATION_GW_RX);
_w5100_spi_en(false);
return true;
}
@@ -341,6 +345,7 @@ bool gatewayTransportAvailable()
client.stop();
} else {
if (_readFromClient()) {
setIndication(INDICATION_GW_RX);
_w5100_spi_en(false);
return true;
}
@@ -59,6 +59,8 @@ MyMessage _mqttMsg;
bool gatewayTransportSend(MyMessage &message) {
if (!_client.connected())
return false;
setIndication(INDICATION_GW_TX);
snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR(MY_MQTT_PUBLISH_TOPIC_PREFIX "/%d/%d/%d/%d/%d"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type);
debug(PSTR("Sending message on topic: %s\n"), _fmtBuffer);
return _client.publish(_fmtBuffer, message.getString(_convBuffer));
@@ -31,6 +31,7 @@ MyMessage _serialMsg;
bool gatewayTransportSend(MyMessage &message) {
setIndication(INDICATION_GW_TX);
MY_SERIALDEVICE.print(protocolFormat(message));
// Serial print is always successful
return true;
+42
View File
@@ -0,0 +1,42 @@
/**
* 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-2016 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.
*/
#include "MyIndication.h"
#ifdef MY_LEDS_BLINKING_FEATURE
#include "MyLeds.h"
#endif
void setIndication( const indication_t ind )
{
#ifdef MY_LEDS_BLINKING_FEATURE
if ((INDICATION_TX == ind) || (INDICATION_GW_TX == ind))
{
ledsBlinkTx(1);
} else if ((INDICATION_RX == ind) || (INDICATION_GW_RX == ind))
{
ledsBlinkRx(1);
} else if (ind > INDICATION_ERR_START)
{
// Number of blinks indicates which error occurred.
ledsBlinkErr(ind-INDICATION_ERR_START);
}
#endif
if (indication)
indication(ind);
}
+72
View File
@@ -0,0 +1,72 @@
/**
* 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-2016 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.
*/
#ifndef MyIndication_h
#define MyIndication_h
/**
* Indication type
*/
typedef enum {
INDICATION_TX = 0, //!< Sent a message.
INDICATION_RX, //!< Received a message.
INDICATION_GW_TX, //!< Gateway transmit message.
INDICATION_GW_RX, //!< Gateway receive message.
INDICATION_FIND_PARENT, //!< Start finding parent node.
INDICATION_GOT_PARENT, //!< Found parent node.
INDICATION_REQ_NODEID, //!< Request node ID.
INDICATION_GOT_NODEID, //!< Got a node ID.
INDICATION_REBOOT, //!< Rebooting node.
INDICATION_PRESENT, //!< Presenting node to gateway.
INDICATION_CLEAR_ROUTING, //!< Clear rrouting table requested.
INDICATION_SLEEP, //!< Node goes to sleep.
INDICATION_WAKEUP, //!< Node just woke from sleep.
INDICATION_FW_UPDATE_START, //!< Start of OTA firmware update process.
INDICATION_FW_UPDATE_RX, //!< Received a piece of firmware data.
INDICATION_ERR_START = 100,
INDICATION_ERR_TX, //!< Failed to transmit message.
INDICATION_ERR_INIT_TRANSPORT, //!< MySensors transport hardware (radio) init failure.
INDICATION_ERR_FIND_PARENT, //!< Failed to find parent node.
INDICATION_ERR_GET_NODEID, //!< Failed to receive node ID.
INDICATION_ERR_SIGN, //!< Error signing.
INDICATION_ERR_VERSION, //!< Protocol version mismatch.
INDICATION_ERR_NET_FULL, //!< Network full. All node ID's are taken.
INDICATION_ERR_INIT_GWTRANSPORT, //!< Gateway transport hardware init failure.
INDICATION_ERR_LOCKED, //!< Node is locked.
INDICATION_ERR_FW_FLASH_INIT, //!< Firmware update flash initialisation failure.
INDICATION_ERR_FW_TIMEOUT, //!< Firmware update timeout.
INDICATION_ERR_FW_CHECKSUM, //!< Firmware update checksum mismatch.
INDICATION_ERR_END
} indication_t;
/**
* Function which is called when something changes about the internal state of MySensors.
* @param ind Event indication of what happened.
*/
void setIndication( const indication_t ind );
/**
* Allow user to define their own indication handler.
*/
void indication( const indication_t ) __attribute__((weak));
#endif
+38 -55
View File
@@ -19,81 +19,64 @@
#include "MyLeds.h"
#define LED_ON_OFF_RATIO (4) // Power of 2 please
#define LED_PROCESS_INTERVAL_MS (MY_DEFAULT_LED_BLINK_PERIOD/LED_ON_OFF_RATIO)
// these variables don't need to be volatile, since we are not using interrupts
uint8_t _countRx;
uint8_t _countTx;
uint8_t _countErr;
unsigned long _blink_next_time;
static uint8_t countRx;
static uint8_t countTx;
static uint8_t countErr;
static unsigned long prevTime = millis() - LED_PROCESS_INTERVAL_MS; // Substract some, to make sure leds gets updated on first run.
inline void ledsInit()
{
// initialize counters
countRx = 0;
countTx = 0;
countErr = 0;
inline void ledsInit() {
// Setup led pins
pinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT);
pinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT);
pinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT);
pinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT);
pinMode(MY_DEFAULT_ERR_LED_PIN, OUTPUT);
// Set initial state of leds
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF);
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF);
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF);
// initialize counters
_countRx = 0;
_countTx = 0;
_countErr = 0;
ledsProcess();
}
inline void ledsProcess() {
void ledsProcess() {
// Just return if it is not the time...
// http://playground.arduino.cc/Code/TimingRollover
if ((long)(hwMillis() - _blink_next_time) < 0)
if ((hwMillis() - prevTime) < LED_PROCESS_INTERVAL_MS)
return;
else
_blink_next_time = hwMillis() + MY_DEFAULT_LED_BLINK_PERIOD;
// do the actual blinking
if(_countRx && _countRx != 255) {
// switch led on
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_ON);
}
else if(!_countRx) {
// switching off
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF);
}
if(_countRx != 255)
--_countRx;
prevTime += LED_PROCESS_INTERVAL_MS;
if(_countTx && _countTx != 255) {
// switch led on
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_ON);
}
else if(!_countTx) {
// switching off
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF);
}
if(_countTx != 255)
--_countTx;
uint8_t state;
if(_countErr && _countErr != 255) {
// switch led on
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_ON);
}
else if(!_countErr) {
// switching off
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF);
}
if(_countErr != 255)
--_countErr;
// For an On/Off ratio of 4, the pattern repeated will be [on, on, on, off]
// until the counter becomes 0.
state = (countRx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, state);
if (countRx) --countRx;
state = (countTx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, state);
if (countTx) --countTx;
state = (countErr & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, state);
if (countErr) --countErr;
}
void ledsBlinkRx(uint8_t cnt) {
if(_countRx == 255) { _countRx = cnt; }
if (!countRx) { countRx = cnt*LED_ON_OFF_RATIO; }
}
void ledsBlinkTx(uint8_t cnt) {
if(_countTx == 255) { _countTx = cnt; }
if(!countTx) { countTx = cnt*LED_ON_OFF_RATIO; }
}
void ledsBlinkErr(uint8_t cnt) {
if(_countErr == 255) { _countErr = cnt; }
if(!countErr) { countErr = cnt*LED_ON_OFF_RATIO; }
}
@@ -34,6 +34,7 @@ inline void firmwareOTAUpdateRequest() {
unsigned long enter = hwMillis();
if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) {
if (!_fwRetry) {
setIndication(INDICATION_ERR_FW_TIMEOUT);
debug(PSTR("fw upd fail\n"));
// Give up. We have requested MY_OTA_RETRY times without any packet in return.
_fwUpdateOngoing = false;
@@ -57,11 +58,13 @@ inline bool firmwareOTAUpdateProcess() {
NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)_msg.data;
// compare with current node configuration, if they differ, start fw fetch process
if (memcmp(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) {
setIndication(INDICATION_FW_START);
debug(PSTR("fw update\n"));
// copy new FW config
memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
// Init flash
if (!_flash.initialize()) {
setIndication(INDICATION_ERR_FW_FLASH_INIT);
debug(PSTR("flash init fail\n"));
_fwUpdateOngoing = false;
} else {
@@ -81,6 +84,7 @@ inline bool firmwareOTAUpdateProcess() {
} else if (_msg.type == ST_FIRMWARE_RESPONSE) {
if (_fwUpdateOngoing) {
// Save block to flash
setIndication(INDICATION_FW_RX);
debug(PSTR("fw block %d\n"), _fwBlock);
// extract FW block
ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data;
@@ -102,6 +106,7 @@ inline bool firmwareOTAUpdateProcess() {
hwWriteConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
hwReboot();
} else {
setIndication(INDICATION_ERR_FW_CHECKSUM);
debug(PSTR("fw checksum fail\n"));
}
}
+30 -9
View File
@@ -65,6 +65,9 @@ void _infiniteLoop() {
#if defined(MY_GATEWAY_ESP8266)
yield();
#endif
#if defined (MY_LEDS_BLINKING_FEATURE)
ledsProcess();
#endif
}
}
@@ -79,6 +82,10 @@ void _begin() {
debug(PSTR("Starting " MY_NODE_TYPE " (" MY_CAPABILITIES ", " LIBRARY_VERSION ")\n"));
#if defined(MY_LEDS_BLINKING_FEATURE)
ledsInit();
#endif
signerInit();
#if defined(MY_RADIO_FEATURE)
@@ -86,6 +93,7 @@ void _begin() {
// Setup radio
if (!transportInit()) {
setIndication(INDICATION_ERR_INIT_TRANSPORT);
debug(PSTR("Radio init failed. Check wiring.\n"));
// Nothing more we can do
_infiniteLoop();
@@ -101,6 +109,7 @@ void _begin() {
// initialize the transport driver
if (!gatewayTransportInit()) {
setIndication(INDICATION_ERR_INIT_GWTRANSPORT);
debug(PSTR("Transport driver init fail\n"));
// Nothing more we can do
_infiniteLoop();
@@ -108,11 +117,6 @@ void _begin() {
#endif
#if defined(MY_LEDS_BLINKING_FEATURE)
ledsInit();
#endif
// Read latest received controller configuration from EEPROM
hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
if (_cc.isMetric == 0xff) {
@@ -141,6 +145,7 @@ void _begin() {
// We don't actually know the distance to gw here. Let's pretend it is 1.
// If the current node is also repeater, be aware of this.
_nc.distance = 1;
setIndication(INDICATION_GOT_PARENT);
} else if (!isValidParent(_nc.parentNodeId)) {
// Auto find parent, but parent in eeprom is invalid. Try find one.
transportFindParentNode();
@@ -151,6 +156,7 @@ void _begin() {
_nc.nodeId = MY_NODE_ID;
// Save static id in eeprom
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID);
setIndication(INDICATION_GOT_NODEID);
} else if (_nc.nodeId == AUTO && isValidParent(_nc.parentNodeId)) {
// Try to fetch node-id from gateway
transportRequestNodeId();
@@ -174,6 +180,7 @@ void _begin() {
} else {
// Disable pullup
pinMode(MY_NODE_UNLOCK_PIN, INPUT);
setIndication(INDICATION_ERR_LOCKED);
nodeLock("LDB"); //Locked during boot
}
} else if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0xFF) {
@@ -222,7 +229,6 @@ boolean _sendRoute(MyMessage &message) {
if (message.destination == _nc.nodeId) {
// This is a message sent from a sensor attached on the gateway node.
// Pass it directly to the gateway transport layer.
ledBlinkTx(1);
return gatewayTransportSend(message);
}
#endif
@@ -278,6 +284,7 @@ void _processInternalMessages() {
#if !defined(MY_DISABLE_REMOTE_RESET)
if (type == I_REBOOT) {
// Requires MySensors or other bootloader with watchdogs enabled
setIndication(INDICATION_REBOOT);
hwReboot();
} else
#endif
@@ -310,6 +317,7 @@ void _processInternalMessages() {
#if defined(MY_REPEATER_FEATURE)
if (_msg.data[0] == 'C') {
// Clears child relay data for this node
setIndication(INDICATION_CLEAR_ROUTING);
debug(PSTR("clear routing table\n"));
uint8_t i = 255;
do {
@@ -354,6 +362,7 @@ void _processInternalMessages() {
for (int i=EEPROM_START;i<EEPROM_LOCAL_CONFIG_ADDRESS;i++) {
hwWriteConfig(i,0xFF);
}
setIndication(INDICATION_REBOOT);
hwReboot();
}
#endif
@@ -408,7 +417,10 @@ int8_t sleep(unsigned long ms) {
#if defined(MY_RADIO_FEATURE)
transportPowerDown();
#endif
return hwSleep(ms);
setIndication(INDICATION_SLEEP);
const int8_t res = hwSleep(ms);
setIndication(INDICATION_WAKEUP);
return res;
#endif
}
@@ -438,7 +450,10 @@ int8_t sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
#if defined(MY_RADIO_FEATURE)
transportPowerDown();
#endif
return hwSleep(interrupt, mode, ms);
setIndication(INDICATION_SLEEP);
const int8_t res = hwSleep(interrupt, mode, ms);
setIndication(INDICATION_WAKEUP);
return res;
#endif
}
@@ -470,7 +485,10 @@ int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode
#if defined(MY_RADIO_FEATURE)
transportPowerDown();
#endif
return hwSleep(interrupt1, mode1, interrupt2, mode2, ms);
setIndication(INDICATION_SLEEP);
const int8_t res = hwSleep(interrupt1, mode1, interrupt2, mode2, ms);
setIndication(INDICATION_WAKEUP);
return res;
#endif
}
@@ -488,6 +506,7 @@ void nodeLock(const char* str) {
// Make sure EEPROM is updated to locked status
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, 0);
while (1) {
setIndication(INDICATION_ERR_LOCKED);
debug(PSTR("Node is locked. Ground pin %d and reset to unlock.\n"), MY_NODE_UNLOCK_PIN);
#if defined(MY_GATEWAY_ESP8266)
yield();
@@ -497,7 +516,9 @@ void nodeLock(const char* str) {
#if defined(MY_RADIO_FEATURE)
transportPowerDown();
#endif
setIndication(INDICATION_SLEEP);
(void)hwSleep((unsigned long)1000*60*30); // Sleep for 30 min before resending LOCKED message
setIndication(INDICATION_WAKEUP);
}
}
#endif
+16 -9
View File
@@ -42,7 +42,7 @@ inline void transportProcess() {
uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
(void)payloadLength; //until somebody makes use of it
ledBlinkRx(1);
setIndication(INDICATION_RX);
uint8_t command = mGetCommand(_msg);
@@ -54,8 +54,8 @@ inline void transportProcess() {
// Reject massages that do not pass verification
if (!signerVerifyMsg(_msg)) {
setIndication(INDICATION_ERR_SIGN);
debug(PSTR("verify fail\n"));
ledBlinkErr(1);
return;
}
@@ -75,8 +75,8 @@ inline void transportProcess() {
}
if(!(mGetVersion(_msg) == PROTOCOL_VERSION)) {
setIndication(INDICATION_ERR_VERSION);
debug(PSTR("ver mismatch\n"));
ledBlinkErr(1);
return;
}
@@ -121,6 +121,7 @@ inline void transportProcess() {
distance++;
if (isValidDistance(distance) && (distance < _nc.distance)) {
// Found a neighbor closer to GW than previously found
setIndication(INDICATION_GOT_PARENT);
_nc.distance = distance;
_nc.parentNodeId = sender;
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, _nc.parentNodeId);
@@ -135,11 +136,13 @@ inline void transportProcess() {
_nc.nodeId = _msg.getByte();
if (_nc.nodeId == AUTO) {
// sensor net gateway will return max id if all sensor id are taken
setIndication(INDICATION_ERR_NET_FULL);
debug(PSTR("full\n"));
// Nothing else we can do...
_infiniteLoop();
}
setIndication(INDICATION_GOT_NODEID);
transportPresentNode();
if (presentation)
presentation();
@@ -214,9 +217,10 @@ boolean transportSendWrite(uint8_t to, MyMessage &message) {
mSetVersion(message, PROTOCOL_VERSION);
uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message);
message.last = _nc.nodeId;
ledBlinkTx(1);
setIndication(INDICATION_TX);
bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length));
if (!ok) setIndication(INDICATION_ERR_TX);
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"),
message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type,
@@ -234,23 +238,23 @@ boolean transportSendRoute(MyMessage &message) {
// If we still don't have any parent id, re-request and skip this message.
if (_nc.parentNodeId == AUTO) {
transportFindParentNode();
ledBlinkErr(1);
setIndication(INDICATION_ERR_FIND_PARENT);
transportFindParentNode();
return false;
}
// If we still don't have any node id, re-request and skip this message.
if (_nc.nodeId == AUTO) {
setIndication(INDICATION_ERR_GET_NODEID);
transportRequestNodeId();
ledBlinkErr(1);
return false;
}
mSetVersion(message, PROTOCOL_VERSION);
if (!signerSignMsg(message)) {
setIndication(INDICATION_ERR_SIGN);
debug(PSTR("sign fail\n"));
ledBlinkErr(1);
}
#if !defined(MY_REPEATER_FEATURE)
@@ -322,7 +326,6 @@ boolean transportSendRoute(MyMessage &message) {
if (!ok) {
// Failure when sending to parent node. The parent node might be down and we
// need to find another route to gateway.
ledBlinkErr(1);
_failedTransmissions++;
if (_autoFindParent && _failedTransmissions > SEARCH_FAILURES) {
transportFindParentNode();
@@ -336,6 +339,7 @@ boolean transportSendRoute(MyMessage &message) {
void transportRequestNodeId() {
setIndication(INDICATION_REQ_NODEID);
debug(PSTR("req id\n"));
transportSetAddress(_nc.nodeId);
build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("");
@@ -344,6 +348,7 @@ void transportRequestNodeId() {
}
void transportPresentNode() {
setIndication(INDICATION_PRESENT);
// Open reading pipe for messages directed to this node (set write pipe to same)
transportSetAddress(_nc.nodeId);
// Present node and request config
@@ -386,6 +391,8 @@ void transportFindParentNode() {
return;
findingParentNode = true;
setIndication(INDICATION_FIND_PARENT);
_failedTransmissions = 0;
// Set distance to max
@@ -0,0 +1,150 @@
/**
* 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-2016 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
*
* Simple binary switch example
* Connect button or door/window reed switch between
* digitial I/O pin 3 (BUTTON_PIN below) and GND.
* A NeoPixel LED will be used to provide visual feedback of the sensor status.
* http://www.mysensors.org/build/binary
*/
// Enable debug prints to serial monitor
#define MY_DEBUG
// Enable and select radio type attached
#define MY_RADIO_NRF24
//#define MY_RADIO_RFM69
#include <Adafruit_NeoPixel.h>
#include <MsTimer2.h>
#include <SPI.h>
#include <MySensor.h>
#include <Bounce2.h>
#define LED_CYCLE_MS 30 // Update rate of LEDs, in [ms].
#define NEO_PIN 6 // NeoPixels input pin
#define CHILD_ID 3
#define BUTTON_PIN 3 // Arduino Digital I/O pin for button/reed switch
Bounce debouncer = Bounce();
int oldValue=-1;
static Adafruit_NeoPixel pixels = Adafruit_NeoPixel(1, NEO_PIN, NEO_GRB + NEO_KHZ800);
// Change to V_LIGHT if you use S_LIGHT in presentation below
MyMessage msg(CHILD_ID,V_TRIPPED);
#define BLINK_TIMES(x) (x<<4)
static uint8_t ledR = 0;
static uint8_t ledG = 0;
static uint8_t ledB = 0;
static void updateLed(void)
{
// Update the pattern (setting leds).
if (ledR > 0) --ledR;
if (ledG > 0) --ledG;
if (ledB > 0) --ledB;
// Determine of each color if it should be on or off.
uint8_t r = ((ledR & 0x0F) < 6) ? 0 : 255;
uint8_t g = ((ledG & 0x0F) < 6) ? 0 : 255;
uint8_t b = ((ledB & 0x0F) < 6) ? 0 : 255;
pixels.setPixelColor(0, r, g, b);
pixels.show();
}
void before()
{
// Initialize the NeoPixel chain
pixels.begin();
pixels.show();
// Configure timer interrupt
MsTimer2::set(LED_CYCLE_MS, updateLed);
MsTimer2::start();
}
void setup()
{
// Setup the button
pinMode(BUTTON_PIN,INPUT);
// Activate internal pull-up
digitalWrite(BUTTON_PIN,HIGH);
// After setting up the button, setup debouncer
debouncer.attach(BUTTON_PIN);
debouncer.interval(5);
}
void presentation() {
// Register binary input sensor to gw (they will be created as child devices)
// 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, S_DOOR);
}
void indication( const indication_t ind )
{
if ((INDICATION_TX == ind) || (INDICATION_GW_TX == ind))
{
// Blink Blue 1x
ledB = BLINK_TIMES(1);
Serial.println(F("TX"));
} else if ((INDICATION_RX == ind) || (INDICATION_GW_RX == ind))
{
// Blink Blue 1x
ledB = BLINK_TIMES(1);
Serial.println(F("RX"));
} else if (INDICATION_FIND_PARENT == ind)
{
// Blink Green 5x
ledG = BLINK_TIMES(5);
Serial.println(F("JOIN"));
} else if (INDICATION_GOT_PARENT == ind)
{
// Green off
ledG = BLINK_TIMES(0);
Serial.println(F("JOINED"));
} else if (ind > INDICATION_ERR_START)
{
// Blink Red, depending on the error number
ledR = BLINK_TIMES(ind - INDICATION_ERR_START);
Serial.print(F("ERROR")); Serial.println(ind - INDICATION_ERR_START);
}
}
// Check if digital input has changed and send in new value
void loop()
{
debouncer.update();
// Get the update value
int value = debouncer.read();
if (value != oldValue) {
// Send in the new value
send(msg.set(value==HIGH ? 1 : 0));
oldValue = value;
}
}