Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added standalone M5Stack gateway #50

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Added standalone M5Stack gateway
SinishaDjukic committed May 20, 2020
commit 6cafc3eb82b3896341c2dd7e1213f0f471d8924e
213 changes: 213 additions & 0 deletions examples/GatewayM5StackStandalone/GatewayM5StackStandalone.ino
Original file line number Diff line number Diff line change
@@ -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 <Arduino.h>
#include <stdint.h>

#include <M5Stack.h>
#include <MySensors.h>

// 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<EEPROM_LOCAL_CONFIG_ADDRESS; i++) {
hwWriteConfig(i, 0xFF);
}
resetTransport();
}

void loop()
{
//Call M5 lib to keep track of button states
M5.update();

//M5.BtnA/B/C.pressedFor(uint32_t ms);isPressed();wasReleasedFor(uint32_t ms);wasPressed();isPressed();
if (M5.BtnA.pressedFor(5000)) {//hold button A to reset gateway
M5.Lcd.fillScreen(TFT_BLACK);
M5.Lcd.setCursor(0, 0);
M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Lcd.print("Resetting...\n");
resetGateway();
M5.Lcd.print("Reset done.\n");
delay(1000);
startScreen();
setTransportInclusion(false);
} else if (M5.BtnB.wasPressed()) {
setInclusion(!getInclusion());
} else if (M5.BtnC.wasPressed()) {//cycle back and print node state
nodeIndex --;
nodeIndex = nodeIndex > 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);
}
216 changes: 216 additions & 0 deletions examples/GatewayM5StackStandalone/lib/MyGatewayTransportStandalone.cpp
Original file line number Diff line number Diff line change
@@ -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 <Arduino.h>
#include <stdint.h>

#include <core/MyProtocol.h>
#include <core/MyTransport.h>
#include <core/MyGatewayTransport.h>
#include <core/MyMessage.h>
#include <core/MyInclusionMode.h>
#include <core/MyIndication.h>

#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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef MyGatewayTransportStandalone_h
#define MyGatewayTransportStandalone_h

#include <Arduino.h>
#include <stdint.h>

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