diff --git a/examples/GatewayM5StackStandalone/GatewayM5StackStandalone.ino b/examples/GatewayM5StackStandalone/GatewayM5StackStandalone.ino new file mode 100644 index 0000000..bacc1a2 --- /dev/null +++ b/examples/GatewayM5StackStandalone/GatewayM5StackStandalone.ino @@ -0,0 +1,213 @@ +#define MY_DEBUG + +#define MY_RADIO_RF24 +#define MY_RF24_CE_PIN (SCL) +#define MY_RF24_CS_PIN (SDA) +#define MY_RF24_PA_LEVEL RF24_PA_LOW + +#define MY_REPEATER_FEATURE +#define MY_GATEWAY_FEATURE +#define MY_IS_GATEWAY (true) +#define MY_NODE_TYPE "GW" + +// Enable inclusion mode +#define MY_INCLUSION_MODE_FEATURE + +// Set inclusion mode duration (in seconds) +#define MY_INCLUSION_MODE_DURATION 60 + +// Replace default LED handler with TFT display indication +#define MY_INDICATION_HANDLER +#define TFT_IND_W (20) +#define TFT_IND_H (20) +#define TFT_IND_X (320 - 3 * TFT_IND_W) +#define TFT_IND_Y (240 - 1 * TFT_IND_H) + +#include +#include + +#include +#include + +// Include from subfolders to avoid funny linking issues from Arduino +#include "lib/MyGatewayTransportStandalone.h" +#include "lib/MyGatewayTransportStandalone.cpp" + +uint8_t nodeIndex = 0; + +void indication( const indication_t ind ) +{ + if ((INDICATION_TX == ind) || (INDICATION_GW_TX == ind)) { + M5.Lcd.fillRect(TFT_IND_X + 0 * TFT_IND_W, TFT_IND_Y, TFT_IND_W, TFT_IND_H, BLUE); + delay(25); + M5.Lcd.fillRect(TFT_IND_X + 0 * TFT_IND_W, TFT_IND_Y, TFT_IND_W, TFT_IND_H, TFT_BLACK); + } else if ((INDICATION_RX == ind) || (INDICATION_GW_RX == ind)) { + M5.Lcd.fillRect(TFT_IND_X + 1 * TFT_IND_W, TFT_IND_Y, TFT_IND_W, TFT_IND_H, GREEN); + delay(25); + M5.Lcd.fillRect(TFT_IND_X + 1 * TFT_IND_W, TFT_IND_Y, TFT_IND_W, TFT_IND_H, TFT_BLACK); + } else if (ind > INDICATION_ERR_START) { + M5.Lcd.fillRect(TFT_IND_X + 2 * TFT_IND_W, TFT_IND_Y, TFT_IND_W, TFT_IND_H, RED); + delay(25); + M5.Lcd.fillRect(TFT_IND_X + 2 * TFT_IND_W, TFT_IND_Y, TFT_IND_W, TFT_IND_H, TFT_BLACK); + } +} + +void startScreen() +{ + M5.Lcd.fillScreen(TFT_BLACK); + M5.Lcd.setCursor(0, 0); + M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK); + M5.Lcd.print (" Status: Ready\n"); + M5.Lcd.printf("Channel: %d\n", MY_RF24_CHANNEL); + M5.Lcd.printf(" Nodes: %d\n", getLastNodeId()); +} + +void setup() +{ + //Set up screen and rotate vertically as SPI pins on M5Stack are on the bottom + // + // /----- [ SPI ] -----\ + // |-------------------| + // | [ C ] [ B ] [ A ] | + // | /---------------\ | + // | | | | + // | | | | + // | | | | + // | | | | + // | \---------------/ | + // \-------------------/ + M5.begin(); + M5.Lcd.setTextSize(3); + M5.Lcd.setRotation(3); + + startScreen(); + setTransportInclusion(false); +} + +void presentation() +{ + // Present locally attached sensors +} + +void setInclusion(bool include) +{ + if ( getTransportInclusion() && getLastNodeId() == 255 ) { + //No available Ids in the network + M5.Lcd.setCursor(0, 220); + M5.Lcd.setTextColor(TFT_RED, TFT_BLACK); + M5.Lcd.print("Nwk full"); + } else { + setTransportInclusion(include); + } +} + +bool getInclusion() +{ + return getTransportInclusion(); +} + +void resetGateway() +{ + for (uint16_t i=0; i getLastNodeId() ? getLastNodeId() : nodeIndex; + printNodeState(nodeIndex, false); + } else if (M5.BtnA.wasPressed()) {//cycle forward and print node state + nodeIndex ++; + nodeIndex = nodeIndex > getLastNodeId() ? 0 : nodeIndex; + printNodeState(nodeIndex, false); + } + + if ( getInclusion() && getTransportInclusionTimeout(MY_INCLUSION_MODE_DURATION * 1000) ) { + setInclusion(false); + } + + M5.Lcd.setCursor(0, 220); + if ( getInclusion() ) { + M5.Lcd.setTextColor(TFT_GREEN, TFT_BLACK); + M5.Lcd.print("Include "); + } else { + M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK); + M5.Lcd.print("Normal "); + } +} + + +//Print some common sensor values like voltage and temperature +void printNodeState(uint8_t node, bool last) +{ + M5.Lcd.fillScreen(TFT_BLACK); + M5.Lcd.setCursor(0, 0); + M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK); + + nodeState_t nodeState = getNodeState(node); + + if ( last ) { + M5.Lcd.printf("Sender: %d\n", getLastMessageSender()); + M5.Lcd.printf("Sensor: %d\n", getLastMessageSensor()); + M5.Lcd.printf(" Type: %d\n", getLastMessageType()); + } else { + M5.Lcd.printf("Sender: %d\n", node); + if ( nodeState.time > 0 ) { + uint32_t nowMillis = millis() - nodeState.time; + uint32_t seconds = nowMillis / 1000; + int days = seconds / 86400; + seconds %= 86400; + byte hours = seconds / 3600; + seconds %= 3600; + byte minutes = seconds / 60; + seconds %= 60; + M5.Lcd.printf("Ago: %02dh:%02dm:%02ds\n", hours, minutes, seconds); + } else { + M5.Lcd.printf(" Ago: N/A\n", node); + } + M5.Lcd.printf("\n"); + } + M5.Lcd.printf("\n"); + + M5.Lcd.setTextColor(DARKCYAN, TFT_BLACK); + M5.Lcd.printf(" Volts: "); + if ( nodeState.voltage != INVALID_F ) + M5.Lcd.printf("%2.2f", nodeState.voltage); + M5.Lcd.printf("\n"); + + M5.Lcd.setTextColor(DARKGREEN, TFT_BLACK); + M5.Lcd.printf(" Temp: "); + if ( nodeState.temp != INVALID_F ) + M5.Lcd.printf("%2.2f", nodeState.temp); + M5.Lcd.printf("\n"); + + M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK); + M5.Lcd.printf("\n"); + M5.Lcd.printf("\n"); + M5.Lcd.printf("Total Rx: %d\n", getMessageCounter()); +} + +void received(uint8_t nodeId) +{ + printNodeState(nodeId, true); +} \ No newline at end of file diff --git a/examples/GatewayM5StackStandalone/lib/MyGatewayTransportStandalone.cpp b/examples/GatewayM5StackStandalone/lib/MyGatewayTransportStandalone.cpp new file mode 100644 index 0000000..775d3f2 --- /dev/null +++ b/examples/GatewayM5StackStandalone/lib/MyGatewayTransportStandalone.cpp @@ -0,0 +1,216 @@ +/* Following must be included in the sketch +#define MY_REPEATER_FEATURE +#define MY_GATEWAY_FEATURE +#define MY_IS_GATEWAY (true) +#define MY_NODE_TYPE "GW" + +// Enable inclusion mode +#define MY_INCLUSION_MODE_FEATURE + +// Set inclusion mode duration (in seconds) +#define MY_INCLUSION_MODE_DURATION 60ul +*/ + +#include "MyGatewayTransportStandalone.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define EEPROM_LAST_NODE_ID (100) + +//last known node states in RAM +nodeState_t nodeStates[255]; + +//inclusion process vars +bool inclusion = false; +uint32_t inclusionStartTime = 0; +uint8_t nextNodeId = 255; + +//messaging vars +mysensors_command_t lastMessageCommand; +uint8_t lastMessageSender; +uint8_t lastMessageSensor; +uint8_t lastMessageType; +uint8_t lastMessageByte; +MyMessage _responseMsg; +uint16_t messageCounter = 0; + +uint16_t getMessageCounter() +{ + return messageCounter; +} + +uint8_t getLastMessageSender() +{ + return lastMessageSender; +} + +uint8_t getLastMessageSensor() +{ + return lastMessageSensor; +} + +uint8_t getLastMessageType() +{ + return lastMessageType; +} + +bool getTransportInclusionTimeout(uint32_t duration) +{ + return (bool) (millis() - inclusionStartTime > duration); +} + +uint8_t getLastNodeId() +{ + uint8_t lastNodeId = loadState(EEPROM_LAST_NODE_ID); + if ( lastNodeId == 0xFF ) { + lastNodeId = 0; + } + return lastNodeId; +} + +uint8_t getnextNodeId() +{ + uint8_t lastNodeId = loadState(EEPROM_LAST_NODE_ID); + if ( lastNodeId == 0xFF ) { + lastNodeId = 0; + } + lastNodeId ++; + saveState(EEPROM_LAST_NODE_ID, lastNodeId); + return lastNodeId; +} + +void resetTransport() +{ + saveState(EEPROM_LAST_NODE_ID, 0); +} + +void setTransportInclusion(bool include) +{ + inclusion = include; + inclusionModeSet(inclusion); + inclusionStartTime = millis(); + delay(100); + if ( !include ) { + nextNodeId = 255; + } +} + +bool getTransportInclusion() +{ + return inclusion; +} + +nodeState_t& getNodeState(uint8_t nodeId) +{ + return nodeStates[nodeId]; +} + +void gatewayTransportReceived(const MyMessage &message) +{ + messageCounter ++; + + lastMessageCommand = message.getCommand(); + lastMessageSender = message.getSender(); + lastMessageSensor = message.getSensor(); + lastMessageType = message.getType(); + lastMessageByte = message.getByte(); + + if ( lastMessageSensor == 1 && lastMessageType == 0 ) { //temp + nodeStates[lastMessageSender].temp = message.getFloat(); + } + + if ( lastMessageSensor == 201 && lastMessageType == 38 ) { //battery + nodeStates[lastMessageSender].voltage = message.getFloat(); + } + + nodeStates[lastMessageSender].time = millis(); + + //notify that a message has been received + received(lastMessageSender); +} + +bool gatewayTransportSend(MyMessage &message) +{ + gatewayTransportReceived(message); + setIndication(INDICATION_GW_TX); + return true; +} + +bool gatewayTransportInit(void) +{ + inclusion = false; + inclusionInit(); + inclusionModeSet(false); + inclusionStartTime = millis(); + + for ( uint8_t i = 0; ; i ++ ) { + nodeStates[i].voltage = INVALID_F; + nodeStates[i].temp = INVALID_F; + nodeStates[i].time = 0; + if ( i == 255 ) + break; + } + return true; +} + +bool gatewayTransportAvailable(void) +{ + bool result = false; + if ( ( lastMessageCommand == C_INTERNAL ) && inclusion ) { + result = true; + + _responseMsg.clear(); + _responseMsg.setCommand(C_INTERNAL); + _responseMsg.setSender(getNodeId()); + _responseMsg.setDestination(lastMessageSender); + + switch ( lastMessageType ) { + case I_REGISTRATION_REQUEST: //presumably last in the inclusion sequence + setIndication(INDICATION_GW_RX); + _responseMsg.setType(I_REGISTRATION_RESPONSE); + _responseMsg.set((bool) true); + break; + case I_ID_REQUEST: + _responseMsg.setType(I_ID_RESPONSE); + _responseMsg.setSensor(AUTO); + nextNodeId = nextNodeId == 255 ? getnextNodeId() : nextNodeId; + if ( nextNodeId != 0 ) { //sanity check for free Ids available + _responseMsg.set((const uint8_t) nextNodeId); + } + break; + case I_FIND_PARENT_REQUEST: + _responseMsg.setType(I_FIND_PARENT_RESPONSE); + _responseMsg.set((const uint8_t) 0); + break; + case I_DISCOVER_REQUEST: + _responseMsg.setType(I_DISCOVER_RESPONSE); + _responseMsg.set((const uint8_t) 0); + break; + case I_PING: + _responseMsg.setType(I_PONG); + _responseMsg.set((const uint8_t) lastMessageByte++); + break; + case I_CONFIG: + _responseMsg.setType(I_CONFIG); + _responseMsg.set((const uint8_t) 0);// metric + break; + default: + result = false; + } + lastMessageCommand = C_INVALID_7;//mark as handled + } + return result; +} + +MyMessage& gatewayTransportReceive(void) +{ + return _responseMsg; +} \ No newline at end of file diff --git a/examples/GatewayM5StackStandalone/lib/MyGatewayTransportStandalone.h b/examples/GatewayM5StackStandalone/lib/MyGatewayTransportStandalone.h new file mode 100644 index 0000000..9f4a559 --- /dev/null +++ b/examples/GatewayM5StackStandalone/lib/MyGatewayTransportStandalone.h @@ -0,0 +1,39 @@ +#ifndef MyGatewayTransportStandalone_h +#define MyGatewayTransportStandalone_h + +#include +#include + +const float INVALID_F = -1000; + +typedef struct { + float voltage; + float temp; + uint32_t time; +} nodeState_t; + +extern void received(uint8_t nodeId); + +bool getTransportInclusionTimeout(uint32_t duration); + +uint8_t getLastNodeId(); + +uint8_t getNextNodeId(); + +void resetTransport(); + +void setTransportInclusion(bool include); + +bool getTransportInclusion(); + +nodeState_t& getNodeState(uint8_t nodeId); + +uint16_t getMessageCounter(); + +uint8_t getLastMessageSender(); + +uint8_t getLastMessageSensor(); + +uint8_t getLastMessageType(); + +#endif \ No newline at end of file