#include #include #include #include #include #include #include "janitza.h" #include "common.h" #include "led.h" #define MQTT_RECONNECT_SECONDS 3 #define LOOP_DELAY 500 #define READ_FREQ 5000 #define CONFIG_WIFI_SSID "ssid" #define CONFIG_WIFI_PSK "11112222" // Certificate const char* mqttRootCA = \ "-----BEGIN CERTIFICATE-----\n" \ ... "-----END CERTIFICATE-----"; const char* mqttServer = CONFIG_MQTT_SERVER; const char* mqttUserName = CONFIG_MQTT_USERNAME; const char* mqttPassword = CONFIG_MQTT_PASSWORD; static JanitzaRegister umg104_registers[] = { {19000, JanitzaType::FLOAT, JanitzaUnit::V, "_ULN[0]", "Voltage L1-N"}, {19002, JanitzaType::FLOAT, JanitzaUnit::V, "_ULN[1]", "Voltage L2-N"}, {19004, JanitzaType::FLOAT, JanitzaUnit::V, "_ULN[2]", "Voltage L3-N"}, {19006, JanitzaType::FLOAT, JanitzaUnit::V, "_ULL[0]", "Voltage L1-L2"}, {19008, JanitzaType::FLOAT, JanitzaUnit::V, "_ULL[1]", "Voltage L2-L3"}, {19010, JanitzaType::FLOAT, JanitzaUnit::V, "_ULL[2]", "Voltage L3-L1"}, {19012, JanitzaType::FLOAT, JanitzaUnit::A, "_ILN[0]", "Apparent current L1"}, {19014, JanitzaType::FLOAT, JanitzaUnit::A, "_ILN[1]", "Apparent current L2"}, {19016, JanitzaType::FLOAT, JanitzaUnit::A, "_ILN[2]", "Apparent current L3"}, {19018, JanitzaType::FLOAT, JanitzaUnit::A, "_I_SUM3", "Vector sum; IN=I1+I2+I3"}, {19026, JanitzaType::FLOAT, JanitzaUnit::W, "_P_SUM3", "Sum; Psum3=P1+P2+P3"}, {19020, JanitzaType::FLOAT, JanitzaUnit::W, "_PLN[0]", "Real power L1"}, {19022, JanitzaType::FLOAT, JanitzaUnit::W, "_PLN[1]", "Real power L2"}, {19024, JanitzaType::FLOAT, JanitzaUnit::W, "_PLN[2]", "Real power L3"}, {19028, JanitzaType::FLOAT, JanitzaUnit::VA, "_SLN[0]", "Apparent power L1"}, {19030, JanitzaType::FLOAT, JanitzaUnit::VA, "_SLN[1]", "Apparent power L2"}, {19032, JanitzaType::FLOAT, JanitzaUnit::VA, "_SLN[2]", "Apparent power L3"}, {19054, JanitzaType::FLOAT, JanitzaUnit::Wh, "_WH[0]", "Real energy L1"}, {19056, JanitzaType::FLOAT, JanitzaUnit::Wh, "_WH[1]", "Real energy L2"}, {19058, JanitzaType::FLOAT, JanitzaUnit::Wh, "_WH[2]", "Real energy L3"} }; static WiFiClientSecure espClient; static PubSubClient mqtt(espClient); static JanitzaReader jr; static const char* MqttHelloTopic = "slrmn/%d/hello"; static const char* MqttOtaTopic = "slrmn/%d/ota"; static const char* MqttEnergyTopic = "slrmn/%d/energy"; static const char* MqttErrorTopic = "slrmn/%d/error"; struct __attribute__((__packed__)) MqttEnergyDataPayload { float uln0; float uln1; float uln2; float ull0; float ull1; float ull2; float iln0; float iln1; float iln2; float i_sum3; float p_sum3; float pln0; float pln1; float pln2; float sln0; float sln1; float sln2; float wh0; float wh1; float wh2; uint32_t timestamp; }; struct __attribute__((__packed__)) MqttErrorPayload { uint32_t timestamp; uint16_t addr; uint8_t code; }; struct __attribute__((__packed__)) MqttHelloPayload { uint32_t timestamp; uint16_t fw_version; }; static volatile bool mqttNeedsReconnect = true; static bool timeConfigured = false; static bool wifiConnectionCalled = false; // static bool wifiOnceConnected = false; static long lastNTPCheck = 0; static long lastJanitraRead = 0; static long lastMqttReconnectAttempt = 0; static long wifiReconnectStarted = 0; static void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info); static void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); static void mqttReconnect(); static void mqttCallback(char* topic, uint8_t* payload, size_t length); static void getTopicName(char* buf, size_t size, const char* fmt); static String strgen(int length); void setup() { #ifdef DEBUG Serial.begin(115200); #endif #ifndef CONFIG_JANITZA_EMULATE jr.configure(); #endif WiFi.begin(CONFIG_WIFI_SSID, CONFIG_WIFI_PSK); WiFi.setAutoReconnect(true); WiFi.hostname(CONFIG_WIFI_HOSTNAME); espClient.setCACert(mqttRootCA); // espClient.setInsecure(); wifiConnectionCalled = true; } void loop() { auto ws = WiFi.status(); if (ws != WL_CONNECTED) { if (wifiReconnectStarted > 0 && millis() - wifiReconnectStarted > 60000) { PRINTF("\nfailed to reconnect, restarting...\n"); ESP.restart(); return; } if (!wifiConnectionCalled) { PRINT("Connecting to wifi.."); // WiFi.setAutoReconnect(true); WiFi.disconnect(); WiFi.reconnect(); wifiConnectionCalled = true; wifiReconnectStarted = millis(); } PRINT("."); mcu_led->blink(2, 50); delay(LOOP_DELAY); mqttNeedsReconnect = true; return; } wifiConnectionCalled = false; if (!timeConfigured) { randomSeed(micros()); configTime(3600*3, 0, "pool.ntp.org", "ntp0.ntp-servers.net", "ntp4.ntp-servers.net"); timeConfigured = true; PRINTLN("Waiting for time"); while (time(nullptr) < 24 * 3600) { PRINT("."); delay(LOOP_DELAY); } PRINTLN(""); } time_t timestamp = time(nullptr); long now = millis(); long delaytime = LOOP_DELAY; bool firstTimeFetch = lastNTPCheck == 0; int mqttState = mqtt.state(); if (!mqtt.connected() && (mqttNeedsReconnect && (!lastMqttReconnectAttempt || now-lastMqttReconnectAttempt > MQTT_RECONNECT_SECONDS*1000)) || mqttState < 0) mqttReconnect(); if (mqtt.connected()) { mqtt.loop(); if (!lastJanitraRead || now-lastJanitraRead > READ_FREQ) { uint16_t buf[2]; char topic[32]; float val; uint16_t failedAddr = 0; uint8_t failedCode = 0; uint32_t ts = timestamp; MqttEnergyDataPayload energyPayload; energyPayload.timestamp = ts; JanitzaRegister* reg; for (int i = 0; i < ARRAY_SIZE(umg104_registers); i++) { reg = &umg104_registers[i]; if (!jr.readFloat(*reg, &val)) { failedCode = jr.getLastErrorCode(); failedAddr = reg->addr; break; } (*(float*)((uint8_t*)&energyPayload + sizeof(float)*i)) = val; } if (failedAddr != 0) { MqttErrorPayload errorPayload = { .timestamp = ts, .addr = failedAddr, .code = failedCode }; getTopicName(topic, sizeof(topic), MqttErrorTopic); mqtt.publish(topic, (uint8_t*)&errorPayload, sizeof(errorPayload)); } else { getTopicName(topic, sizeof(topic), MqttEnergyTopic); mqtt.publish(topic, (uint8_t*)&energyPayload, sizeof(energyPayload)); } lastJanitraRead = now; } else { long timedelta = READ_FREQ - (now - lastJanitraRead); delaytime = timedelta > LOOP_DELAY ? LOOP_DELAY : timedelta; } } // PRINTLN(timestamp); delay(delaytime); } static void mqttCallback(char* topic, uint8_t* payload, size_t length) { } static void mqttReconnect() { char buf[32]; long now = millis(); mqttNeedsReconnect = false; lastMqttReconnectAttempt = now; mqtt.setBufferSize(1024); mqtt.setServer(mqttServer, CONFIG_MQTT_PORT); String clientId = String(CONFIG_WIFI_HOSTNAME) + "_" + strgen(4); PRINTF("mqtt client id will be %s\n", clientId.c_str()); if (mqtt.connect(clientId.c_str(), mqttUserName, mqttPassword)) { PRINTLN("mqtt: connected [1]"); mqtt.loop(); mqtt.setCallback(mqttCallback); // subscribe to the OTA topic getTopicName(buf, sizeof(buf), MqttOtaTopic); mqtt.subscribe(buf); // send hello payl getTopicName(buf, sizeof(buf), MqttHelloTopic); auto payload = MqttHelloPayload { .timestamp = static_cast(time(nullptr)), .fw_version = CONFIG_FW_VERSION }; mqtt.publish(buf, (uint8_t*)&payload, sizeof(payload)); PRINTLN("mqtt: connected [2]"); lastMqttReconnectAttempt = 0; } else { PRINTF("mqtt: failed to connect, rc=%d, retrying in %d seconds\n", mqtt.state(), MQTT_RECONNECT_SECONDS); mqttNeedsReconnect = true; } } static void getTopicName(char* buf, size_t size, const char* fmt) { memset(buf, 0, size); snprintf(buf, size-1, fmt, CONFIG_NODE_ID); } static String strgen(int length) { String randomString = ""; String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (int i = 0; i < length; i++) { int index = random(characters.length()); char nextChar = characters.charAt(index); randomString += nextChar; } return randomString; }