diff --git a/platformio.ini b/platformio.ini index c37a9462..bad1cb6c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -84,7 +84,7 @@ build_flags = ${STARBASE_USERMOD_E131.build_flags} ; ${STARBASE_USERMOD_HA.build_flags} -DAPP=StarBase - -DVERSION=24050120 ; Date and time (GMT!) + -DVERSION=24050213 ; Date and time (GMT!) -DPIOENV=$PIOENV lib_deps = ${StarBase.lib_deps} diff --git a/src/User/UserModInstances.h b/src/User/UserModInstances.h index 0052207d..b49930ac 100644 --- a/src/User/UserModInstances.h +++ b/src/User/UserModInstances.h @@ -21,11 +21,13 @@ struct DMX { byte count:4; // 4 bits / 16 }; //total 16 bits +//additional data not in wled header struct SysData { unsigned long upTime; byte type; byte syncMaster; DMX dmx; + uint8_t macAddress[6]; // 48 bits }; struct VarData { @@ -33,82 +35,15 @@ struct VarData { byte value; }; //4 -#define nrOfAppVars 20 - -struct AppData { - VarData vars[nrOfAppVars]; //total 80 - - bool str3cmp(const char * lhs, const char * rhs) { - for (int i = 0; i < 3; i++) { - if (lhs[i] == '\0' || rhs[i] == '\0') - return true; - if (lhs[i] != rhs[i]) - return false; - } - return true; - } - - void str3cpy(char * lhs, const char * rhs) { - bool found0 = false; - for (int i = 0; i < 3; i++) { - if (rhs[i] == '\0') - found0 = true; - if (!found0) - lhs[i] = rhs[i]; //copy - else - lhs[i] = '\0'; //add 0 - } - } - - int getVar(const char * varID) { //int to support -1 - for (int i=0; i < nrOfAppVars; i++) { - // ppf("getVar %d %s %s %d\n", i, varID, vars[i].id, vars[i].value); - if (varID && !str3cmp(vars[i].id, "-") && str3cmp(vars[i].id, varID)) { - return vars[i].value; - } - } - // ppf("getVar %s not found\n", varID); - return -1; - } - - void setVar(const char * varID, unsigned8 value) { - size_t foundAppVar; - for (int i=0; i< nrOfAppVars; i++) { - if (str3cmp(vars[i].id, "-")) { - foundAppVar = i; //use empty slot - break; - } - else if (str3cmp(vars[i].id, varID)) { - foundAppVar = i; //use existing slot for var - break; - } - } - size_t i = foundAppVar; - if (str3cmp(vars[i].id, "-")) str3cpy(vars[i].id, varID); //assign empty slot to var - vars[i].value = value; - // ppf("setVar %d %s %d\n", i, varID, vars[i].value); - } - - void initVars() { - for (int i = 0; i < nrOfAppVars; i++) { - str3cpy(vars[i].id, "-"); - vars[i].value = 0; - } - } - -}; - -//note: changing SysData and AppData sizes: all instances should have the same version so change with care +//note: changing SysData and jsonData sizes: all instances should have the same version so change with care struct InstanceInfo { IPAddress ip; char name[32]; - uint8_t macAddress[6]; // 48 bits uint32_t version; unsigned long timeStamp; //when was the package received, used to check on aging - SysData sys; - AppData app; //total 80 - + SysData sysData; + JsonDocument jsonData; }; struct UDPWLEDMessage { @@ -127,9 +62,8 @@ struct UDPWLEDMessage { //compatible with WLED instances as it only interprets first 44 bytes struct UDPStarMessage { UDPWLEDMessage header; // 44 bytes fixed! - SysData sys; - AppData app; - char body[1460 - sizeof(UDPWLEDMessage) - sizeof(SysData) - sizeof(AppData)]; + SysData sysData; + char jsonString[1460 - sizeof(UDPWLEDMessage) - sizeof(SysData)]; }; //WLED syncmessage @@ -222,7 +156,7 @@ class UserModInstances:public SysModule { ui->initText(tableVar, "insType", nullptr, 16, true, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun case f_ValueFun: for (forUnsigned8 rowNrL = 0; rowNrL < instances.size() && (rowNr == UINT8_MAX || rowNrL == rowNr); rowNrL++) - mdl->setValue(var, (instances[rowNrL].sys.type==0)?"WLED":(instances[rowNrL].sys.type==1)?"StarBase":(instances[rowNrL].sys.type==2)?"StarLeds":"StarFork", rowNrL); + mdl->setValue(var, (instances[rowNrL].sysData.type==0)?"WLED":(instances[rowNrL].sysData.type==1)?"StarBase":(instances[rowNrL].sysData.type==2)?"StarLeds":"StarFork", rowNrL); return true; case f_UIFun: ui->setLabel(var, "Type"); @@ -244,7 +178,7 @@ class UserModInstances:public SysModule { ui->initNumber(tableVar, "insUp", UINT16_MAX, 0, (unsigned long)-1, true, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun case f_ValueFun: for (forUnsigned8 rowNrL = 0; rowNrL < instances.size() && (rowNr == UINT8_MAX || rowNrL == rowNr); rowNrL++) - mdl->setValue(var, instances[rowNrL].sys.upTime, rowNrL); + mdl->setValue(var, instances[rowNrL].sysData.upTime, rowNrL); return true; case f_UIFun: ui->setLabel(var, "Uptime"); @@ -293,10 +227,11 @@ class UserModInstances:public SysModule { for (forUnsigned8 rowNrL = 0; rowNrL < instances.size() && (rowNr == UINT8_MAX || rowNrL == rowNr); rowNrL++) { // ppf("initVar dash %s[%d]\n", mdl->varID(insVar), rowNrL); //do what setValue is doing except calling changeFun - // insVar["value"][rowNrL] = instances[rowNrL].app.getVar(mdl->varID(var)); //only int values... - web->addResponse(insVar["id"], "value", instances[rowNrL].app.getVar(mdl->varID(var)), rowNrL); //only int values...); + // insVar["value"][rowNrL] = instances[rowNrL].jsonData[mdl->varID(var)]; //only int values... - // mdl->setValue(insVar, instances[rowNr].app.getVar(var["id"]), rowNr); + web->addResponse(insVar["id"], "value", instances[rowNrL].jsonData[mdl->varID(var)], rowNrL); + + // mdl->setValue(insVar, instances[rowNrL].jsonData[mdl->varID(var)], rowNr); //send to ws? } return true; @@ -327,7 +262,7 @@ class UserModInstances:public SysModule { web->sendUDPCounter++; web->sendUDPBytes+=sizeof(buffer); instanceUDP.endPacket(); - ppf("StarMod udp json write %s\n", buffer); + ppf("udp json write %s\n", buffer); } } } @@ -421,18 +356,18 @@ class UserModInstances:public SysModule { InstanceInfo *instance = findInstance(remoteIp); //if not exist, created - instance->sys.upTime = (wledSyncMessage.timebase[0] * 256*256*256 + 256*256*wledSyncMessage.timebase[1] + 256*wledSyncMessage.timebase[2] + wledSyncMessage.timebase[3]) / 1000; - instance->sys.syncMaster = wledSyncMessage.syncGroups; //tbd: change + instance->sysData.upTime = (wledSyncMessage.timebase[0] * 256*256*256 + 256*256*wledSyncMessage.timebase[1] + 256*wledSyncMessage.timebase[2] + wledSyncMessage.timebase[3]) / 1000; + instance->sysData.syncMaster = wledSyncMessage.syncGroups; //tbd: change stackUnsigned8 syncMaster = mdl->getValue("sma"); if (syncMaster == remoteIp[3]) { - if (instance->app.getVar("bri") != wledSyncMessage.bri) mdl->setValue("bri", wledSyncMessage.bri); + if (instance->jsonData["bri"] != wledSyncMessage.bri) mdl->setValue("bri", wledSyncMessage.bri); //only set brightness } - instance->app.setVar("bri", wledSyncMessage.bri); - instance->app.setVar("fx", wledSyncMessage.mainsegMode); //tbd: rowNr - instance->app.setVar("pal", wledSyncMessage.palette); //tbd: rowNr + instance->jsonData["bri"] = wledSyncMessage.bri; + instance->jsonData["fx"] = wledSyncMessage.mainsegMode; //tbd: rowNr + instance->jsonData["pal"] = wledSyncMessage.palette; //tbd: rowNr // for (size_t x = 0; x < packetSize; x++) { // char xx = (char)udpIn[x]; @@ -464,7 +399,7 @@ class UserModInstances:public SysModule { byte *udpIn = (byte *)&starMessage.header; instanceUDP.read(udpIn, packetSize); - starMessage.sys.type = 0; //WLED + starMessage.sysData.type = 0; //WLED updateInstance(starMessage); } @@ -472,11 +407,11 @@ class UserModInstances:public SysModule { UDPStarMessage starMessage; byte *udpIn = (byte *)&starMessage; instanceUDP.read(udpIn, packetSize); - starMessage.sys.type = (strcmp(_INIT(TOSTRING(APP)), "StarBase")==0)?1:(strcmp(_INIT(TOSTRING(APP)), "StarLeds")==0)?2:3; //1=StarBase,2=StarLeds, 3=StarFork + starMessage.sysData.type = (strcmp(_INIT(TOSTRING(APP)), "StarBase")==0)?1:(strcmp(_INIT(TOSTRING(APP)), "StarLeds")==0)?2:3; //1=StarBase,2=StarLeds, 3=StarFork updateInstance(starMessage); } - else if (packetSize == 500) { //StarBase upp json + else if (packetSize == 500) { //udp json message char buffer[packetSize]; instanceUDP.read(buffer, packetSize); @@ -486,7 +421,7 @@ class UserModInstances:public SysModule { mdl->setValue(message["id"].as(), message["value"].as()); - ppf("StarMod udp json read %s\n", buffer); + ppf("udp json read %s\n", buffer); } else { @@ -553,39 +488,48 @@ class UserModInstances:public SysModule { #endif starMessage.header.insId = localIP[3]; //WLED: used in map of instances as index! starMessage.header.version = VERSION; - starMessage.sys.type = (strcmp(_INIT(TOSTRING(APP)), "StarBase")==0)?1:(strcmp(_INIT(TOSTRING(APP)), "StarLeds")==0)?2:3; //1=StarBase,2=StarLeds, 3=StarFork - starMessage.sys.upTime = millis()/1000; - starMessage.sys.syncMaster = mdl->getValue("sma"); - starMessage.sys.dmx.universe = 0; - starMessage.sys.dmx.start = 0; - starMessage.sys.dmx.count = 0; + starMessage.sysData.type = (strcmp(_INIT(TOSTRING(APP)), "StarBase")==0)?1:(strcmp(_INIT(TOSTRING(APP)), "StarLeds")==0)?2:3; //1=StarBase,2=StarLeds, 3=StarFork + starMessage.sysData.upTime = millis()/1000; + starMessage.sysData.syncMaster = mdl->getValue("sma"); + starMessage.sysData.dmx.universe = 0; + starMessage.sysData.dmx.start = 0; + starMessage.sysData.dmx.count = 0; #ifdef STARBASE_USERMOD_E131 if (e131mod->isEnabled) { - starMessage.sys.dmx.universe = mdl->getValue("dun"); - starMessage.sys.dmx.start = mdl->getValue("dch"); - starMessage.sys.dmx.count = 3;//e131->varsToWatch.size(); + starMessage.sysData.dmx.universe = mdl->getValue("dun"); + starMessage.sysData.dmx.start = mdl->getValue("dch"); + starMessage.sysData.dmx.count = 3;//e131->varsToWatch.size(); } #endif - //dash values default 0 - starMessage.app.initVars(); - - //send dash values - mdl->findVars("dash", true, [&starMessage](JsonObject var) { //varFun - // print->printJson("setVar", var); - JsonArray valArray = mdl->varValArray(var); - if (valArray.isNull()) - starMessage.app.setVar(var["id"], var["value"]); - else if (valArray.size()) - starMessage.app.setVar(var["id"], valArray[0].as()); //set the first value (tbd: add multiple) - }); - updateInstance(starMessage); //temp? to show own instance in list as instance is not catching it's own udp message... - IPAddress broadcastIP(255, 255, 255, 255); - if (0 != instanceUDP.beginPacket(broadcastIP, instanceUDPPort)) { // WLEDMM beginPacket == 0 --> error + //other way around: first set instance variables, then fill starMessage + for (InstanceInfo &instance: instances) { + if (instance.ip == WiFi.localIP()) { + instance.jsonData.to(); //clear + + //send dash values + mdl->findVars("dash", true, [&instance](JsonObject var) { //varFun + // print->printJson("setVar", var); + JsonArray valArray = mdl->varValArray(var); + if (valArray.isNull()) + instance.jsonData[mdl->varID(var)] = var["value"]; + else if (valArray.size()) + instance.jsonData[mdl->varID(var)] = valArray; + }); + + serializeJson(instance.jsonData, starMessage.jsonString); + ppf("udp json String send ip:%d s:%s\n", instance.ip[3], starMessage.jsonString); + // print->printJson(" d:", instance.jsonData); + // print->printJDocInfo(" info", instance.jsonData); + } + } + + // broadcast to network + if (0 != instanceUDP.beginPacket(IPAddress(255, 255, 255, 255), instanceUDPPort)) { // WLEDMM beginPacket == 0 --> error // ppf("sendSysInfoUDP %s s:%d p:%d i:...%d\n", starMessage.header.name, sizeof(UDPStarMessage), instanceUDPPort, localIP[3]); - // for (size_t x = 0; x < sizeof(UDPWLEDMessage) + sizeof(SysData) + sizeof(AppData); x++) { + // for (size_t x = 0; x < sizeof(UDPWLEDMessage) + sizeof(SysData); x++) { // char * xx = (char *)&starMessage; // Serial.printf("%d: %d - %c\n", x, xx[x], xx[x]); // } @@ -614,23 +558,24 @@ class UserModInstances:public SysModule { if (!instanceFound) { //new instance InstanceInfo instance; instance.ip = messageIP; - if (udpStarMessage.sys.type == 0) {//WLED only - instance.sys.type = 0; //WLED + if (udpStarMessage.sysData.type == 0) {//WLED only + instance.sysData.type = 0; //WLED //updated in udp sync message: - instance.sys.upTime = 0; - instance.sys.dmx.universe = 0; - instance.sys.dmx.start = 0; - instance.sys.dmx.count = 0; - instance.sys.syncMaster = 0; + instance.sysData.upTime = 0; + instance.sysData.dmx.universe = 0; + instance.sysData.dmx.start = 0; + instance.sysData.dmx.count = 0; + instance.sysData.syncMaster = 0; //dash values default 0 - instance.app.initVars(); + instance.jsonData.to(); } instances.push_back(instance); std::sort(instances.begin(),instances.end(), [](InstanceInfo &a, InstanceInfo &b){ return a.ip < b.ip; });//Sorting the vector strcmp(a.name,b.name); } - //iterate vector pointers so we can update the instances + //update the instance in the instances array with the message data + // stackUnsigned8 rowNr = 0; for (InstanceInfo &instance: instances) { if (instance.ip == messageIP) { @@ -638,42 +583,36 @@ class UserModInstances:public SysModule { strncpy(instance.name, udpStarMessage.header.name, sizeof(instance.name)-1); instance.version = udpStarMessage.header.version; if (instance.ip == WiFi.localIP()) { - esp_wifi_get_mac((wifi_interface_t)ESP_IF_WIFI_STA, instance.macAddress); + esp_wifi_get_mac((wifi_interface_t)ESP_IF_WIFI_STA, instance.sysData.macAddress); // ppf("macaddress %02X:%02X:%02X:%02X:%02X:%02X\n", instance.macAddress[0], instance.macAddress[1], instance.macAddress[2], instance.macAddress[3], instance.macAddress[4], instance.macAddress[5]); } - if (udpStarMessage.sys.type >= 1) {//StarBase, StarLeds and forks only - instance.sys = udpStarMessage.sys; - - //check for syncing - stackUnsigned8 syncMaster = mdl->getValue("sma"); - if (syncMaster == messageIP[3]) { - - //find matching var - for (int i=0; i< nrOfAppVars; i++) { - //set instance kv - - VarData newVar = udpStarMessage.app.vars[i]; - - if (!instance.app.str3cmp(newVar.id, "-")) { - - int value = instance.app.getVar(newVar.id); - - //if no value found or value has changed - if (value == -1 || value != newVar.value) { - char varID[4]; - instance.app.str3cpy(varID, newVar.id); - varID[3]='\0'; - - ppf("AppData3 %s %s %d\n", instance.name, varID, newVar.value); - mdl->setValue(varID, newVar.value); + if (udpStarMessage.sysData.type >= 1) {//StarBase, StarLeds and forks only + instance.sysData = udpStarMessage.sysData; + + if (instance.ip != WiFi.localIP()) { //send from localIP will be done after updateInstance + //set instance.jsonData from new string + JsonDocument newData; + DeserializationError error = deserializeJson(newData, udpStarMessage.jsonString); + if (error || !newData.is()) { + ppf("udp json String receive failed ip:%d e:%s\n", instance.ip[3], error.c_str(), udpStarMessage.jsonString); + } + else { + stackUnsigned8 syncMaster = mdl->getValue("sma"); + if (syncMaster == messageIP[3]) { //if the message is from the master, set all values from the master + for (JsonPair pair: newData.as()) { + if (pair.key() != "sma" && pair.key() != "dch") { // do not sync sma and dch !!! + ppf("updateInstance sync from i:%s k:%s v:%s\n", instance.name, pair.key().c_str(), pair.value().as().c_str()); + // if (mdl->getValue(pair.key().c_str) != pair.value()) + mdl->setValue(pair.key().c_str(), pair.value()); + } } } - } - } - //set values to instance - for (int i=0; i< nrOfAppVars; i++) - instance.app.vars[i] = udpStarMessage.app.vars[i]; + instance.jsonData = newData; // deepcopy: https://github.com/bblanchon/ArduinoJson/issues/1023 + ppf("udp json String receive ip:%d", instance.ip[3]); + print->printJson(" d:", instance.jsonData); + } + } } //only update cell in instbl!