MQTT->CAN proxy. CRC16 for CAN config

This commit is contained in:
2024-08-19 22:35:20 +03:00
parent 82f729216e
commit 2b638b1310
7 changed files with 245 additions and 71 deletions

View File

@@ -131,15 +131,19 @@ bool canDriver::sendRemoteID(macAddress mac)
{
canid_t id;
//datagram_t packet;
bool res=false;
bool res=false;
id.subjId=0;
id.deviceId=getIdByMac(mac); //Retrieved controllerID
if (!id.deviceId) return false;
aJsonObject * config=getConfbyID(id.deviceId);
if (config) id.subjId=getCRC(config); //CRC16 of remote config
id.reserve=0;
id.status=1; //response
id.payloadType=payloadType::lookupMAC;
id.subjId=200; //CRC16 of remote config
debugSerial<<("CAN: Send remote ID")<<endl;
res = write (id.id,(datagram_t*)mac,6);
@@ -152,6 +156,11 @@ return res;
bool canDriver::begin()
{
if (!root) return false;
canConfigObj = aJson.getObjectItem(root, "can");
if (!canConfigObj) return false;
canRemoteConfigObj= aJson.getObjectItem(canConfigObj, "conf");
controllerId = getMyId();
if (!ready) // not reInitialization
@@ -419,7 +428,7 @@ if (id.status){
case payloadType::itemCommand:
{
if (len!=8) return false;
aJsonObject *confObj = findConfbyID(id.deviceId);
aJsonObject *confObj = getConfbyID(id.deviceId);
if (confObj)
{
debugSerial<<F("CAN: status received for dev ")<<id.deviceId<<endl;
@@ -434,7 +443,7 @@ if (id.status){
ic.cmd = packet->cmd;
ic.param = packet->param;
debugSerial<<F("CAN: item ")<<it.itemArr->name;
debugSerial<<F("CAN: item ")<<it.itemArr->name<<" ";
ic.debugOut();
if (ic.isCommand()) flags |= FLAG_COMMAND;
@@ -456,9 +465,17 @@ if (id.status){
case canState::MACLookup:
if ((id.payloadType == payloadType::lookupMAC))
{
debugSerial<<"\nCAN: Got Controller addr: "<<id.deviceId<<endl;
controllerId=id.deviceId;
state = canState::ReadConfig;
if (canConfigObj && (id.subjId == getCRC(canConfigObj))) ///?
{
infoSerial << (F("Valid config already onboard")) << endl;
state = canState::Idle;
}
else
{
infoSerial<<"\nCAN: Got Controller addr: "<<id.deviceId<<endl;
state = canState::ReadConfig;
controllerId=id.deviceId;
}
}
return true;
@@ -511,7 +528,7 @@ else //Requests
else if ((id.payloadType == payloadType::configFrame) && (id.subjId == 0xFFFF))
{
debugSerial<<F("CAN: Requested conf for dev#")<<id.deviceId<<endl;
aJsonObject * remoteConfObj = findConfbyID(id.deviceId);
aJsonObject * remoteConfObj = getConfbyID(id.deviceId);
if (remoteConfObj)
{
infoSerial<<F("CAN: Sending conf for dev#")<<id.deviceId<<endl;
@@ -531,26 +548,17 @@ return false;
uint8_t canDriver::getMyId()
{
if (!root) return 0;
aJsonObject * canObj = aJson.getObjectItem(root, "can");
if (!canObj) return 0;
aJsonObject * addrObj = aJson.getObjectItem(canObj, "addr");
if (!canConfigObj) return 0;
aJsonObject * addrObj = aJson.getObjectItem(canConfigObj, "addr");
if (addrObj && (addrObj->type == aJson_Int)) return addrObj->valueint;
return 0;
}
aJsonObject * canDriver::findConfbyID(uint8_t devId)
aJsonObject * canDriver::getConfbyID(uint8_t devId)
{
if (!root) return NULL;
aJsonObject * canObj = aJson.getObjectItem(root, "can");
if (!canObj) return NULL;
aJsonObject * remoteConfObj = aJson.getObjectItem(canObj, "conf");
if (!remoteConfObj) return NULL;
remoteConfObj=remoteConfObj->child;
if (!canConfigObj) return NULL;
if (!canRemoteConfigObj) return NULL;
aJsonObject * remoteConfObj=canRemoteConfigObj->child;
while (remoteConfObj)
{
aJsonObject * remoteCanObj = aJson.getObjectItem(remoteConfObj, "can");
@@ -564,30 +572,77 @@ while (remoteConfObj)
return NULL;
}
aJsonObject * canDriver::findConfbyName(char* devName, int * devAddr)
{
if (!canRemoteConfigObj && !devName) return NULL;
aJsonObject * remoteConfObj=canRemoteConfigObj->child;
while (remoteConfObj)
{
aJsonObject * remoteCanObj = aJson.getObjectItem(remoteConfObj, "can");
if (remoteCanObj)
{
aJsonObject * nameObj = aJson.getObjectItem(remoteCanObj, "name");
if (nameObj && (nameObj->type == aJson_String) && nameObj->valuestring && (strncasecmp(nameObj->valuestring,devName,strlen(nameObj->valuestring)) == 0))
{
if (devAddr)
{
aJsonObject * addrObj = aJson.getObjectItem(remoteCanObj, "addr");
if (addrObj && (addrObj->type == aJson_Int)) *devAddr=addrObj->valueint;
}
return remoteConfObj;
}
}
remoteConfObj=remoteConfObj->next;
}
return NULL;
}
#if not defined (NOIP)
extern PubSubClient mqttClient;
bool canDriver::subscribeTopics(char * root, size_t buflen)
{
if (!root) return false;
if (!canRemoteConfigObj) return false;
int rootLen = strlen(root);
aJsonObject * remoteConfObj=canRemoteConfigObj->child;
while (remoteConfObj)
{
aJsonObject * remoteCanObj = aJson.getObjectItem(remoteConfObj, "can");
if (remoteCanObj)
{
aJsonObject * addrObj = aJson.getObjectItem(remoteCanObj, "name");
if (addrObj && (addrObj->type == aJson_String) && addrObj->valuestring)
{
strncpy(root+rootLen, addrObj->valuestring, buflen-rootLen-1);
strncat(root+rootLen, "/", buflen-rootLen-1);
strncat(root+rootLen, "#", buflen-rootLen-1);
debugSerial.println(root);
mqttClient.subscribe(root);
}
}
remoteConfObj=remoteConfObj->next;
}
}
#endif
uint8_t canDriver::getIdByMac(macAddress mac)
{
char macStr[19];
uint8_t strptr = 0;
char macStr[19];
uint8_t strptr = 0;
if (!root) return 0;
aJsonObject * canObj = aJson.getObjectItem(root, "can");
if (!canObj) return 0;
aJsonObject * confObj = aJson.getObjectItem(canObj, "conf");
if (!confObj) return 0;
memset(macStr,0,sizeof(macStr));
for (byte i = 0; i < 6; i++)
{
// if (mac[i]<16) macStr[strptr++]='0';
SetBytes(&mac[i],1,&macStr[strptr]);
strptr+=2;
if (i < 5) macStr[strptr++]=':';
}
if (!canRemoteConfigObj) return 0;
memset(macStr,0,sizeof(macStr));
for (byte i = 0; i < 6; i++)
{
SetBytes(&mac[i],1,&macStr[strptr]);
strptr+=2;
if (i < 5) macStr[strptr++]=':';
}
debugSerial<<F("CAN: Searching devId for ")<<macStr<<endl;
aJsonObject * remoteConfObj = aJson.getObjectItem(confObj, macStr);
aJsonObject * remoteConfObj = aJson.getObjectItem(canRemoteConfigObj, macStr);
if (!remoteConfObj) return 0;

View File

@@ -119,7 +119,7 @@ Error
class canDriver
{
public:
canDriver(){ready=false; controllerId=0; responseTimer=0; state=canState::stateUnknown;};
canDriver(){ready=false; controllerId=0; responseTimer=0; state=canState::stateUnknown;canConfigObj=NULL;canRemoteConfigObj=NULL;};
uint8_t getMyId();
bool sendStatus(uint16_t itemNum, itemCmd cmd, int subItem = NO_SUBITEM);
bool sendCommand(uint8_t devID, uint16_t itemID, itemCmd cmd, bool status=false, int subItemID=NO_SUBITEM );
@@ -133,16 +133,22 @@ bool sendRemoteID(macAddress mac);
bool begin();
void Poll();
bool processPacket(canid_t id, datagram_t *packet, uint8_t len, bool rtr=false);
bool write(uint32_t msg_id, datagram_t * buf = NULL, uint8_t size=0);
bool write(uint32_t msg_id, datagram_t * buf = NULL, uint8_t size=0);
aJsonObject * findConfbyName(char* devName, int * devAddr=NULL);
#if not defined (NOIP)
bool subscribeTopics(char * root, size_t buflen);
#endif
uint8_t getControllerID(){return controllerId;};
uint8_t getIdByMac(macAddress mac);
aJsonObject * canConfigObj;
aJsonObject * canRemoteConfigObj;
datagram_t RXpacket;
canid_t RXid;
uint8_t RXlen;
private:
aJsonObject * findConfbyID(uint8_t devId);
aJsonObject * getConfbyID(uint8_t devId);
#if defined(ARDUINO_ARCH_STM32)
CAN_message_t CAN_RX_msg;

View File

@@ -649,7 +649,7 @@ if (suffix = strrchr(*psubItem, '/')) //Trying to retrieve right part
*suffix= 0; //Truncate subItem string
suffix++;
suffixCode = txt2subItem(suffix);
debugSerial<<F("suffixCode:")<<suffixCode<<endl;
// debugSerial<<F("suffixCode:")<<suffixCode<<endl;
// myhome/dev/item/sub.....Item/suffix
}
else
@@ -684,7 +684,7 @@ long int Item::limitSetValue()
// myhome/dev/item/subItem
int Item::Ctrl(char * payload, char * subItem)
int Item::Ctrl(char * payload, char * subItem, int remoteID)
{
if (!payload) return 0;
int fr = freeRam();
@@ -709,13 +709,16 @@ if (suffixCode == S_RAW)
{ itemCmd ic;
ic.Str(payload);
ic.setSuffix(suffixCode);
if (remoteID) return remoteCtrl(ic,remoteID,subItem);
return Ctrl(ic,subItem);
}
bool authorized = false;
char * authPos = strchr(payload,'@');
char * authToken = NULL;
char * authPos = strchr(payload,'@'); //token@command
if (authPos)
{
authToken = payload;
*authPos=0;
authorized = checkToken(payload,authPos+1);
payload=authPos+1;
@@ -782,7 +785,7 @@ st.setSuffix(suffixCode);
default: errorSerial<<F("Wrong paylad")<<endl;
}
}
if (remoteID) return remoteCtrl(st,remoteID,subItem,authToken);
return Ctrl(st,subItem,true,authorized);
} //Void command
break;
@@ -806,7 +809,7 @@ st.setSuffix(suffixCode);
case 4: st.RGBW(Par[0],Par[1],Par[2],Par[3]);
default:;
}
if (remoteID) return remoteCtrl(st,remoteID,subItem,authToken);
return Ctrl(st,subItem,true,authorized);
}
case CMD_UP:
@@ -815,18 +818,40 @@ st.setSuffix(suffixCode);
itemCmd Par0 = getNumber((char **) &payload);
Par0.Cmd(cmd);
Par0.setSuffix(suffixCode);
if (remoteID) return remoteCtrl(Par0,remoteID,subItem,authToken);
return Ctrl(Par0, subItem,true,authorized);
}
default: //some known command
{
int32_t intParam = getIntFromStr((char **) &payload);
if (intParam) st.Int(intParam);
if (remoteID) return remoteCtrl(st,remoteID,NULL,authToken);
return Ctrl(st,NULL, true, authorized);
}
} //ctrl
return 0;
}
int Item::remoteCtrl(itemCmd cmd, int remoteID, char* subItem, char * authToken)
{
#ifdef CANDRV
// Retrieve remote item id
if (!itemArr) return 0;
aJsonObject * itemHandler = itemArr->child;
if (!itemHandler || itemHandler->type!=aJson_Array) return 0;
aJsonObject * itemIdObj=aJson.getArrayItem(itemHandler,1);
if (itemIdObj->type != aJson_Int) return 0;
//Retrieve target controller ID
if (!remoteID) return 0;
//TODO - translate subItem & auth token
short subitemNum = NO_SUBITEM;
return LHCAN.sendCommand(remoteID,itemIdObj->valueint,cmd,false,subitemNum);
#endif
return 0;
}
// Recursive function with small stack consumption
// if cmd defined - execute Ctrl for any group members recursively
// else performs Activity check for group members and return true if any member is active
@@ -1663,7 +1688,7 @@ int Item::isActive() {
itemCmd st;
int val = 0;
debugSerial<<itemArr->name<<F(" ");
debugSerial<<F("ACTIVE:")<<itemArr->name<<F(" ");
if (!isValid())
{
debugSerial<<F(" invalid")<<endl;
@@ -1841,7 +1866,8 @@ int Item::SendStatus(int sendFlags) {
st.debugOut();
#ifdef CANDRV
if (!(sendFlags & FLAG_NOT_SEND_CAN)) LHCAN.sendStatus(getCanNum(itemArr->child),st, getSubitemId(subItem));
if (!(sendFlags & FLAG_NOT_SEND_CAN))
LHCAN.sendStatus(getCanNum(itemArr->child),(sendFlags & FLAG_SEND_DELAYED)?st.setSuffix(S_DELAYED):st, getSubitemId(subItem));
#endif
if (sendFlags & FLAG_COMMAND)

View File

@@ -117,8 +117,8 @@ class Item
void Stop();
//int Ctrl(short cmd, short n=0, int * Parameters=NULL, int suffixCode=0, char* subItem=NULL);
int Ctrl(itemCmd cmd, char* subItem=NULL, bool allowRecursion = true, bool authorized=false);
int Ctrl(char * payload, char * subItem=NULL);
int Ctrl(char * payload, char * subItem=NULL, int remoteID = 0);
int remoteCtrl(itemCmd cmd, int remoteID, char* subItem=NULL, char * authToken=NULL);
int getArg(short n=0);
float getFloatArg(short n=0);
short getArgCount();

View File

@@ -434,8 +434,10 @@ void mqttCallback(char *topic, byte *payload, unsigned int length)
short pfxlen = 0;
char * itemName = NULL;
char * subItem = NULL;
char savedTopic[MQTT_TOPIC_LENGTH] = "";
bool forLocal=false;
aJsonObject * remoteConfig = NULL;
int remoteID=0;
// in Retaining status - trying to restore previous state from retained output topic. Retained input topics are not relevant.
if (lanStatus == RETAINING_COLLECTING)
@@ -455,17 +457,39 @@ if (lanStatus == RETAINING_COLLECTING)
}
return;
}
else forLocal = true;
}
else
{
pfxlen=inTopic(topic,T_DEV);
if (!pfxlen) pfxlen = inTopic(topic,T_BCST);
else // Personal device topic
strncpy(savedTopic,topic,sizeof(savedTopic)-1);
strncpy(savedTopic,topic,sizeof(savedTopic)-1); //Save topic to delete after exec
if (pfxlen) forLocal=true;
#ifdef CANDRV
else //Nor local or bcst name, try can names
{
pfxlen=inTopic(topic,T_ROOT); //check root
if (pfxlen)
{
remoteConfig = LHCAN.findConfbyName(topic+pfxlen,&remoteID);
if (remoteConfig)
{
while(*(topic+pfxlen) && *(topic+pfxlen)!='/') pfxlen++; //skip controller name
if (*(topic+pfxlen)=='/') pfxlen++;
debugSerial<<F("MQTT: remote command Item:")<<topic+pfxlen<<endl;
strncpy(savedTopic,topic,sizeof(savedTopic)-1); //Save topic to delete after exec
}
else pfxlen=0;
}
}
#endif
}
if (!pfxlen) {
debugSerial<<F("Skipping..")<<endl;
// debugSerial<<F("Skipping..")<<endl;
return;// -3;
}
@@ -473,13 +497,16 @@ else
// debugSerial<<itemName<<endl;
if(!strcmp_P(itemName,CMDTOPIC_P) && payload && (strlen((char*) payload)>1)) {
mqttClient.deleteTopic(topic);
cmd_parse((char *)payload);
if (forLocal)((char *)payload);
//TODO implement for remote
return;// -4;
}
//if (itemName[0]=='$') return;// -6; //Skipping homie stuff
if (strrchr(topic,'$')) return;
Item item(itemName);
/*
Item item(itemName);
if (item.isValid() && (item.Ctrl((char *)payload)>0) && savedTopic[0] && lanStatus != RETAINING_COLLECTING)
//if (lanStatus != RETAINING_COLLECTING && (mqttClient.isRetained()))
@@ -487,7 +514,36 @@ else
debugSerial<<F("MQTT: Complete. Remove topic ")<<savedTopic<<endl;
mqttClient.deleteTopic(savedTopic);
}
*/
bool succeeded = false;
if (forLocal)
{
Item item(itemName);
if (item.isValid()) //Local item
{
if (item.Ctrl((char *)payload)>0) succeeded = true;
}
}
#ifdef CANDRV
if (remoteConfig)
{
aJsonObject * remoteItems = aJson.getObjectItem(remoteConfig,"items");
if (remoteItems)
{
Item remoteItem(itemName,remoteItems);
if (remoteItem.Ctrl((char *)payload,NULL,remoteID)>0) succeeded = true;
}
}
#endif
if (succeeded && savedTopic[0] && lanStatus != RETAINING_COLLECTING)
{
debugSerial<<F("MQTT: Complete. Remove topic ")<<savedTopic<<endl;
mqttClient.deleteTopic(savedTopic);
}
return;// -7;
}
@@ -713,12 +769,17 @@ lan_status lanLoop() {
setTopic(buf,sizeof(buf),T_OUT);
strncat(buf, "+/+/#", sizeof(buf)); // Subscribing only on separated command/parameters topics
mqttClient.unsubscribe(buf);
#ifdef CANDRV
setTopic(buf,sizeof(buf),T_ROOT);
LHCAN.subscribeTopics(buf,sizeof(buf));
#endif
onMQTTConnect();
#ifdef CRYPT
//setTopic(buf,sizeof(buf),T_OUT);
strncpy(buf, "+/+/$salt", sizeof(buf)); // Only on separated cmd/val topics
setTopic(buf,sizeof(buf),T_ROOT);
strncat(buf, "+/$salt", sizeof(buf)); // Only on separated cmd/val topics
mqttClient.subscribe(buf);
#endif
@@ -2733,7 +2794,6 @@ LHCAN.upTime(ut);
#endif
}
//#if not defined (NOIP)
void setupMacAddress() {
//Check MAC, stored in NVRAM
@@ -2765,12 +2825,7 @@ if (!sysConf.getMAC()) {
#endif
}
printMACAddress();
#ifdef CANDRV
// LHCAN.lookupMAC();
#endif
}
//#endif //NOIP
void setupCmdArduino() {
//cmdInit(uint32_t(SERIAL_BAUD));

View File

@@ -531,9 +531,12 @@ if (_l2 && _l2->type == aJson_String) strncat(buf,_l2->valuestring,buflen);
strncat_P(buf,inTopic,buflen); /////
break;
}
strncat(buf,"/",buflen);
if (suffix) strncat(buf,suffix,buflen);
if (tt!=T_ROOT)
{
strncat(buf,"/",buflen);
if (suffix) strncat(buf,suffix,buflen);
}
return buf;
}
@@ -975,6 +978,14 @@ return true;
uint16_t getCRC(aJsonObject * in)
{
if (!in) return 0;
CRCStream crcStream;
aJsonStream aJsonCrcStream = aJsonStream(&crcStream);
aJson.print(in,&aJsonCrcStream);
return crcStream.getCRC16();
}
#pragma message(VAR_NAME_VALUE(debugSerial))
#pragma message(VAR_NAME_VALUE(SERIAL_BAUD))

View File

@@ -38,6 +38,7 @@ using namespace ios;
#endif
enum topicType {
T_ROOT = 4,
T_DEV = 1,
T_BCST= 2,
T_OUT = 3
@@ -80,4 +81,24 @@ int str2regSize(char * str);
bool checkToken(char * token, char * data);
bool isProtectedPin(short pin);
bool i2cReset();
uint16_t getCRC(aJsonObject * in);
#include "util/crc16_.h"
class CRCStream : public Stream
{
public:
CRCStream() : CRC16(0xFFFF){}
uint16_t CRC16;
uint16_t getCRC16() {return CRC16;}
// Stream methods
virtual int available(){return 0;};
virtual int read(){return 0;};
virtual int peek(){return 0;};
virtual void flush(){};
// Print methods
virtual size_t write(uint8_t c) {CRC16 = crc16_update(CRC16, c);};
virtual int availableForWrite(){return 1;};
};