diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..c6096a8 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,27 @@ +##################### +ISEMS-ESP32 changelog +##################### + + +Development +=========== +- Add dry-dock environment. +- Add MQTT- and HTTP-based telemetry with JSON encoding. + + +2020-01-09 0.0.0 +================ +- Add the full set of LUA files to run the new hardware board. +- Fix temperature sensor detection and battery charge state estimatation bugs. +- Cleanup debugging statements. + + +2019-12-03 0.0.0 +================ +- Add preliminary version of ISEMS-ESP32 for external module. + + +2019-10-07 0.0.0 +================ +- Initial commit. +- Add README. diff --git a/LUA/ff-esp32-openmppt/config.lua b/LUA/ff-esp32-openmppt/config.lua index 712925f..6cc17ee 100644 --- a/LUA/ff-esp32-openmppt/config.lua +++ b/LUA/ff-esp32-openmppt/config.lua @@ -10,6 +10,9 @@ long = 13.404954 -- Node-ID nodeid="ESP32-Meshnode-1" +-- The telemetry channel to send metrics to. +-- See also MQTT and HTTP configuration below. +telemetry_channel = "isems/testdrive/foobar/" .. nodeid -- Rated capacity of battery in Ampere hours (Ah) rated_batt_capacity = 8.0 @@ -54,3 +57,12 @@ sta_ssid="AP2.freifunk.net" -- WPA key to connect to the existing AP as WiFi client sta_pwd="" + + +-- Telemetry configuration for MQTT and HTTP +mqtt_enabled = false +mqtt_broker = "isems.mqtthub.net" +mqtt_topic = telemetry_channel + +http_enabled = false +http_endpoint = "http://isems.mqtthub.net/api-notls/" .. telemetry_channel .. "/data.json" diff --git a/LUA/ff-esp32-openmppt/mp2.lua b/LUA/ff-esp32-openmppt/mp2.lua index 837967a..9d359f3 100644 --- a/LUA/ff-esp32-openmppt/mp2.lua +++ b/LUA/ff-esp32-openmppt/mp2.lua @@ -501,7 +501,7 @@ statuscode = (bin2hextable[bit_string_0] .. bin2hextable[bit_string_1] .. bin2he print("statuscode =", statuscode) --- Create CSV data set +-- CSV payload timestamp = time.get() @@ -511,7 +511,7 @@ print(nodeid, packetrev, timestamp, firmware_type, nextreboot, powersave, V_oc, ffopenmppt_log = nodeid .. ";" .. packetrev .. ";" .. timestamp .. ";" .. firmware_type .. ";" .. nextreboot .. ";" .. powersave .. ";".. V_oc .. ";".. V_in .. ";".. V_out .. ";".. charge_state_int .. ";" .. health_estimate .. ";".. battery_temperature .. ";".. low_voltage_disconnect .. ";".. V_out_max_temp .. ";" .. rated_batt_capacity .. ";".. solar_module_capacity .. ";".. lat .. ";" .. long .. ";" .. statuscode -print(ffopenmppt_log) +print("CSV payload:", ffopenmppt_log) if ffopenmppt_log5 ~= nil then ffopenmppt_log1 = ffopenmppt_log2 @@ -531,8 +531,12 @@ if ffopenmppt_log5 ~= nil then elseif ffopenmppt_log1 == nil then ffopenmppt_log1 = ffopenmppt_log csvlog = ffopenmppt_log1 - end - +end + +-- HTTP- and MQTT telemetry +dofile "telemetry.lua" + +-- HTML output pagestring = "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n

Independent Solar Energy Mesh


Status of " .. nodeid pagestring = pagestring .. " (local node)



Summary: " .. charge_status .. ". " .. system_status pagestring = pagestring .. "
Charge state: " diff --git a/LUA/ff-esp32-openmppt/telemetry.lua b/LUA/ff-esp32-openmppt/telemetry.lua new file mode 100644 index 0000000..d7608be --- /dev/null +++ b/LUA/ff-esp32-openmppt/telemetry.lua @@ -0,0 +1,114 @@ +--[[ +Telemetry implementation for MQTT and HTTP. +]] + +function get_telemetry_data() + --[[ + Collect all metric values from global variables + and bundle them into a single telemetry data container. + + Note: This might well be improved but for now it's better than nothing. + ]] + data = { + nodeId = nodeid, + isemsRevision = packetrev, + timestamp = timestamp, + timeToShutdown = nextreboot, + isPowerSaveMode = powersave, + openCircuitVoltage = V_oc, + mppVoltage = V_in, + batteryVoltage = V_out, + batteryChargeEstimate = charge_state_int, + batteryHealthEstimate = health_estimate, + batteryTemperature = battery_temperature, + lowVoltageDisconnectVoltage = low_voltage_disconnect, + temperatureCorrectedVoltage = V_out_max_temp, + rateBatteryCapacity = rated_batt_capacity, + ratedSolarModuleCapacity = solar_module_capacity, + latitude = lat, + longitude = long, + status = statuscode, + } + return data +end + +function mqtt_publish(data) + --[[ + MQTT telemetry + + Encode telemetry data as JSON and publish message to + MQTT broker at topic configured within "config.lua". + ]] + + print("Submitting telemetry data to MQTT broker.") + + -- JSON payload + -- https://nodemcu.readthedocs.io/en/master/modules/sjson/ + -- https://github.com/ISEMS/isems-data-collector/blob/926eb4a3/test_importer.py + print("Creating JSON payload.") + sjson.encode(data) + ok, json = pcall(sjson.encode, data) + if ok then + print("JSON payload:", json) + else + print("ERROR: Encoding to JSON failed!") + return + end + + -- https://nodemcu.readthedocs.io/en/master/modules/mqtt/ + m = mqtt.Client("isems-" .. nodeid, 120) + m:connect(mqtt_broker, 1883, 0, + function(client) + print("Connected to MQTT broker.") + client:publish(mqtt_topic, "hello", 0, 0, function(client) print("MQTT message sent.") end) + end, + function(client, reason) + print("MQTT connect failed. Reason: " .. reason) + end + ) + +end + +function http_post(data) + --[[ + HTTP telemetry + + Encode telemetry data as JSON and send as POST request + to HTTP endpoint configured within "config.lua". + ]] + + print("Submitting telemetry data to HTTP endpoint.") + + -- JSON payload + -- https://nodemcu.readthedocs.io/en/master/modules/sjson/ + -- https://github.com/ISEMS/isems-data-collector/blob/926eb4a3/test_importer.py + print("Creating JSON payload.") + ok, json = pcall(sjson.encode, data) + if ok then + print("JSON payload:", json) + else + print("ERROR: Encoding to JSON failed!") + return + end + + -- https://nodemcu.readthedocs.io/en/master/modules/http/ + http.post(http_endpoint, + 'Content-Type: application/json\r\n', + json, + function(code, data) + if (code < 0) then + print("HTTP request failed", code, data) + else + print("HTTP request succeeded", code, data) + end + end) + +end + +if mqtt_enabled == true then + mqtt_publish(get_telemetry_data()) +end + +if http_enabled == true then + http_post(get_telemetry_data()) +end diff --git a/LUA/ff-esp32-openmppt/test/README.rst b/LUA/ff-esp32-openmppt/test/README.rst new file mode 100644 index 0000000..44fed49 --- /dev/null +++ b/LUA/ff-esp32-openmppt/test/README.rst @@ -0,0 +1,14 @@ +#################### +ISEMS-ESP32 dry-dock +#################### + +About +===== +This environment makes it possible to run parts of the +ISEMS-ESP32 Node MCU Lua code on a vanilla PC. + +Synopsis +======== +:: + + lua test/run_basic.lua diff --git a/LUA/ff-esp32-openmppt/test/nodemcu_mock.lua b/LUA/ff-esp32-openmppt/test/nodemcu_mock.lua new file mode 100644 index 0000000..4faaf6b --- /dev/null +++ b/LUA/ff-esp32-openmppt/test/nodemcu_mock.lua @@ -0,0 +1,64 @@ +--[[ +Mock the API of a NodeMCU device. +]] + + +--[[ +Mocks for NodeMCU core modules. +]] +time = { + get = function() + return os.time() + end, + getlocal = function() + date = os.date("*t") + date["mon"] = date["month"] + date["dst"] = "1" + return date + end, +} + +node = { + dsleep = function() end, +} + +file = { + list = function() end, + exists = function() end, +} + +gpio = { + config = function() end, + wakeup = function() end, + write = function() end, +} + +dac = { + enable = function() end, +} + +adc = { + setup = function() end, + setwidth = function() end, + read = function() return 42.42 end, +} + + +--[[ +Requires nodemcu-lua-mocks to be installed for JSON support. +https://github.com/fikin/nodemcu-lua-mocks +]] + +local sjson = require("sjson") +sjson.encode = function(data) + encoder = sjson.encoder(data) + return encoder:read(8192) +end + + +--[[ +TODO: Add mocks for mqtt and http modules. +Currently, "test/run_basic" will croak with:: + + lua: telemetry.lua:59: attempt to index a nil value (global 'mqtt') +]] diff --git a/LUA/ff-esp32-openmppt/test/run_basic.lua b/LUA/ff-esp32-openmppt/test/run_basic.lua new file mode 100644 index 0000000..260c320 --- /dev/null +++ b/LUA/ff-esp32-openmppt/test/run_basic.lua @@ -0,0 +1,14 @@ +--[[ +Run a single duty cycle to completion. +]] + +-- Bootstrap +dofile "test/nodemcu_mock.lua" +dofile "config.lua" + +-- No periodic execution. +-- dofile "is.lua" + +-- Run cycle. +Vref = 1100 --mV +dofile "mp2.lua"