diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6696c95..d0e7ee6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,6 @@ name: Build -on: [push, pull_request] +on: pull_request jobs: build-upload: @@ -19,5 +19,18 @@ jobs: sudo apt-get update sudo apt-get -y install libusb-1.0-0 cmake srecord pip install -U pyserial + - name: Build + env: + PR_NUMBER: ${{ github.event.number }} run: ./scripts/build-firmware + + - uses: keithweaver/aws-s3-github-action@v1.0.0 + name: Copy PR bin to AWS + with: + command: cp + source: build/konnected-esp8266-PR-${{ github.event.number }}.bin + destination: s3://konnected-io/builds/ + aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws_region: us-east-2 diff --git a/.gitignore b/.gitignore index 8c03d32..920a7d8 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,6 @@ private # NodeMCU nodemcu-firmware + +# IDE's +.vscode diff --git a/scripts/build-firmware b/scripts/build-firmware index 3e4ad96..11707e8 100755 --- a/scripts/build-firmware +++ b/scripts/build-firmware @@ -15,9 +15,13 @@ function usage() { set -e if ! git describe --exact-match --tags &> /dev/null; then -BRANCH="$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')" + if [[ -z "${PR_NUMBER}" ]]; then + BRANCH="$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')" + else + BRANCH="PR-"${PR_NUMBER} + fi else -BRANCH="$(git describe --exact-match --tags $(git log -n1 --pretty='%h'))" + BRANCH="$(git describe --exact-match --tags $(git log -n1 --pretty='%h'))" fi FW_TAG="3.0.0-release_20210201" diff --git a/src/lfs/application.lua b/src/lfs/application.lua index f6b68d1..51d5634 100644 --- a/src/lfs/application.lua +++ b/src/lfs/application.lua @@ -6,23 +6,44 @@ local actuators = require("actuators") local settings = require("settings") local ds18b20 = require("ds18b20") local sensorTimer = tmr.create() +local zoneToPin = require("zone_to_pin") -- globals sensorPut = {} actuatorGet = {} +local function getDevicePin(device) + if device.zone ~= nil then + return zoneToPin(device.zone) + else + return device.pin + end +end + -- initialize binary sensors for i, sensor in pairs(sensors) do - print("Heap:", node.heap(), "Initializing sensor pin:", sensor.pin) - gpio.mode(sensor.pin, gpio.INPUT, gpio.PULLUP) + local pin = getDevicePin(sensor) + if sensor.zone ~= nil then + print("Heap:", node.heap(), "Initializing sensor zone:", sensor.zone) + else + print("Heap:", node.heap(), "Initializing sensor pin:", pin) + end + + gpio.mode(pin, gpio.INPUT, gpio.PULLUP) end -- initialize actuators for i, actuator in pairs(actuators) do + local pin = getDevicePin(actuator) local initialState = actuator.trigger == gpio.LOW and gpio.HIGH or gpio.LOW - print("Heap:", node.heap(), "Initializing actuator pin:", actuator.pin, "on:", actuator.trigger or gpio.HIGH, "off:", initialState) - gpio.write(actuator.pin, initialState) - gpio.mode(actuator.pin, gpio.OUTPUT) + if actuator.zone ~= nil then + print("Heap:", node.heap(), "Initializing actuator zone:", actuator.zone, "on:", actuator.trigger or gpio.HIGH, "off:", initialState) + else + print("Heap:", node.heap(), "Initializing actuator pin:", pin, "on:", actuator.trigger or gpio.HIGH, "off:", initialState) + end + + gpio.write(pin, initialState) + gpio.mode(pin, gpio.OUTPUT) table.insert(actuatorGet, actuator) end @@ -30,13 +51,17 @@ end if #dht_sensors > 0 then require("dht") - local function readDht(pin) - local status, temp, humi, temp_dec, humi_dec = dht.read(pin) + local function readDht(sensor) + local status, temp, humi, temp_dec, humi_dec = dht.read(getDevicePin(sensor)) if status == dht.OK then local temperature_string = temp .. "." .. math.abs(temp_dec) local humidity_string = humi .. "." .. humi_dec print("Heap:", node.heap(), "Temperature:", temperature_string, "Humidity:", humidity_string) - table.insert(sensorPut, { pin = pin, temp = temperature_string, humi = humidity_string }) + if sensor.zone ~= nil then + table.insert(sensorPut, { zone = sensor.zone, temp = temperature_string, humi = humidity_string }) + else + table.insert(sensorPut, { pin = sensor.pin, temp = temperature_string, humi = humidity_string }) + end else print("Heap:", node.heap(), "DHT Status:", status) end @@ -45,42 +70,60 @@ if #dht_sensors > 0 then for i, sensor in pairs(dht_sensors) do local pollInterval = tonumber(sensor.poll_interval) or 0 pollInterval = (pollInterval > 0 and pollInterval or 3) * 60 * 1000 - print("Heap:", node.heap(), "Polling DHT on pin " .. sensor.pin .. " every " .. pollInterval .. "ms") - tmr.create():alarm(pollInterval, tmr.ALARM_AUTO, function() readDht(sensor.pin) end) - readDht(sensor.pin) + if sensor.zone ~= nil then + print("Heap:", node.heap(), "Polling DHT on zone " .. sensor.zone .. " every " .. pollInterval .. "ms") + else + print("Heap:", node.heap(), "Polling DHT on pin " .. sensor.pin .. " every " .. pollInterval .. "ms") + end + tmr.create():alarm(pollInterval, tmr.ALARM_AUTO, function() readDht(sensor) end) + readDht(sensor) end end -- initialize ds18b20 temp sensors if #ds18b20_sensors > 0 then - local function ds18b20Callback(pin) + local function ds18b20Callback(sensor) local callbackFn = function(temps) for addr,value in pairs(temps) do print("Heap:", node.heap(), "Temperature:", value) - table.insert(sensorPut, { pin = pin, temp = value, - addr = string.format(('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X'):format(addr:byte(1,8)))}) + local addrStr = string.format(('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X'):format(addr:byte(1,8))) + if sensor.zone ~= nil then + table.insert(sensorPut, { zone = sensor.zone, temp = value, addr = addrStr }) + else + table.insert(sensorPut, { pin = sensor.pin, temp = value, addr = addrStr }) + end end end return callbackFn end for i, sensor in pairs(ds18b20_sensors) do + local pin = getDevicePin(sensor) local pollInterval = tonumber(sensor.poll_interval) or 0 pollInterval = (pollInterval > 0 and pollInterval or 3) * 60 * 1000 - print("Heap:", node.heap(), "Polling DS18b20 on pin " .. sensor.pin .. " every " .. pollInterval .. "ms") - local callbackFn = ds18b20Callback(sensor.pin) - tmr.create():alarm(pollInterval, tmr.ALARM_AUTO, function() ds18b20:read_temp(callbackFn, sensor.pin, ds18b20.C) end) - ds18b20:read_temp(callbackFn, sensor.pin, ds18b20.C) + if sensor.zone ~= nil then + print("Heap:", node.heap(), "Polling DS18b20 on zone " .. sensor.zone .. " every " .. pollInterval .. "ms") + else + print("Heap:", node.heap(), "Polling DS18b20 on pin " .. pin .. " every " .. pollInterval .. "ms") + end + + local callbackFn = ds18b20Callback(sensor) + tmr.create():alarm(pollInterval, tmr.ALARM_AUTO, function() ds18b20:read_temp(callbackFn, pin, ds18b20.C) end) + ds18b20:read_temp(callbackFn, pin, ds18b20.C) end end -- Poll every configured binary sensor and insert into the request queue when changed sensorTimer:alarm(200, tmr.ALARM_AUTO, function(t) for i, sensor in pairs(sensors) do - if sensor.state ~= gpio.read(sensor.pin) then - sensor.state = gpio.read(sensor.pin) - table.insert(sensorPut, { pin = sensor.pin, state = sensor.state }) + if sensor.state ~= gpio.read(getDevicePin(sensor)) then + sensor.state = gpio.read(getDevicePin(sensor)) + if sensor.zone ~= nil then + table.insert(sensorPut, { zone = sensor.zone, state = sensor.state }) + else + table.insert(sensorPut, { pin = sensor.pin, state = sensor.state }) + end end end end) diff --git a/src/lfs/aws_iot.lua b/src/lfs/aws_iot.lua index 1b84402..8a4a3c4 100644 --- a/src/lfs/aws_iot.lua +++ b/src/lfs/aws_iot.lua @@ -2,6 +2,7 @@ local module = ... local mqtt = require('mqtt_ws') local settings = require('settings') +local zoneToPin = require("zone_to_pin") local device_id = wifi.sta.getmac():lower():gsub(':','') local c = mqtt.Client(settings.aws) local topics = settings.aws.topics @@ -103,6 +104,9 @@ c:on('message', function(_, topic, message) local revertIn = (payload.momentary + pause) * times - pause tmr.create():alarm(revertIn, tmr.ALARM_SINGLE, function() local revertState = { pin = endState.pin, state = endState.state == 0 and 1 or 0} + if endState.zone ~= nil then + revertState = { zone = endState.zone, state = endState.state == 0 and 1 or 0} + end table.insert(sensorPut, revertState) end) end @@ -116,7 +120,11 @@ c:on('connect', function() -- update current state of actuators upon boot for i, actuator in pairs(actuatorGet) do - table.insert(sensorPut, { pin = actuator.pin, state = gpio.read(actuator.pin) }) + if actuator.zone ~= nil then + table.insert(sensorPut, { zone = actuator.zone, state = gpio.read(zoneToPin(actuator.zone)) }) + else + table.insert(sensorPut, { pin = actuator.pin, state = gpio.read(actuator.pin) }) + end end heartbeat:start() diff --git a/src/lfs/device.lua b/src/lfs/device.lua index bd078c5..f1e1cdf 100644 --- a/src/lfs/device.lua +++ b/src/lfs/device.lua @@ -2,7 +2,7 @@ local me = { id = "uuid:8f655392-a778-4fee-97b9-4825918" .. string.format("%x", node.chipid()), name = "Konnected", hwVersion = "3.0.0", - swVersion = "3.0.1", + swVersion = "3.1.0", http_port = math.floor(node.chipid()/1000) + 8000, urn = "urn:schemas-konnected-io:device:Security:1" } diff --git a/src/lfs/rest_endpoint.lua b/src/lfs/rest_endpoint.lua index 63ebb9d..5d418df 100644 --- a/src/lfs/rest_endpoint.lua +++ b/src/lfs/rest_endpoint.lua @@ -1,5 +1,7 @@ local module = ... +local zoneToPin = require("zone_to_pin") + -- print HTTP status line local function printHttpResponse(code, data) local a = { "Heap:", node.heap(), "HTTP Call:", code } @@ -27,28 +29,46 @@ local function startLoop(settings) local actuator = actuatorGet[1] timeout:start() - http.get(table.concat({ settings.endpoint, "/device/", dni, '?pin=', actuator.pin }), - table.concat({ "Authorization: Bearer ", settings.token, "\r\nAccept: application/json\r\n" }), + local req = nil + if actuator.zone ~= nil then + req = table.concat({ settings.endpoint, "/device/", dni, '?zone=', actuator.zone }) + else + req = table.concat({ settings.endpoint, "/device/", dni, '?pin=', actuator.pin }) + end + + http.get(req, table.concat({ "Authorization: Bearer ", settings.token, "\r\nAccept: application/json\r\n" }), function(code, response) timeout:stop() - local pin, state, json_response, status + local zone, pin, state, json_response, status if response and code >= 200 and code < 300 then status, json_response = pcall(function() return sjson.decode(response) end) if status then + zone = json_response.zone pin = tonumber(json_response.pin) state = tonumber(json_response.state) end end - printHttpResponse(code, {pin = pin, state = state}) - gpio.mode(actuator.pin, gpio.OUTPUT) - if pin == tonumber(actuator.pin) and code >= 200 and code < 300 and state then - gpio.write(actuator.pin, state) + if zone ~= nil then + printHttpResponse(code, {zone = zone, state = state}) + pin = zoneToPin(actuator.zone) + else + printHttpResponse(code, {pin = pin, state = state}) + end + + gpio.mode(pin, gpio.OUTPUT) + if pin == tonumber(actuator.pin) or zone == actuator.zone and code >= 200 and code < 300 and state then + gpio.write(pin, state) else state = actuator.trigger == gpio.LOW and gpio.HIGH or gpio.LOW - gpio.write(actuator.pin, state) + gpio.write(pin, state) + end + + if zone ~= nil then + print("Heap:", node.heap(), "Initialized actuator Zone:", actuator.zone, "Trigger:", actuator.trigger, "Initial state:", state) + else + print("Heap:", node.heap(), "Initialized actuator Pin:", actuator.pin, "Trigger:", actuator.trigger, "Initial state:", state) end - print("Heap:", node.heap(), "Initialized actuator Pin:", actuator.pin, "Trigger:", actuator.trigger, "Initial state:", state) table.remove(actuatorGet, 1) blinktimer:start() diff --git a/src/lfs/server_device.lua b/src/lfs/server_device.lua index 22c95b9..440f9bd 100644 --- a/src/lfs/server_device.lua +++ b/src/lfs/server_device.lua @@ -4,18 +4,28 @@ local function process(request) if request.method == "GET" then if request.query then request.query.pin = request.query.pin or "all" + request.query.zone = request.query.zone or "all" end local body = {} - if request.query.pin == "all" then + if request.query.pin == "all" or request.query.zone == "all" then local sensors = require("sensors") for i, sensor in pairs(sensors) do - table.insert(body, { pin = sensor.pin, state = sensor.state }) + if sensor.pin ~= nil then + table.insert(body, { pin = sensor.pin, state = sensor.state }) + else + table.insert(body, { zone = sensor.zone, state = sensor.state }) + end end - else + elseif request.query.pin then body = { { pin = request.query.pin, state = gpio.read(request.query.pin) } } + else + body = { { + zone = request.query.zone, + state = gpio.read(require("zone_to_pin")(request.query.zone)) + } } end return sjson.encode(body) end diff --git a/src/lfs/switch.lua b/src/lfs/switch.lua index c05e773..8e5f6c3 100644 --- a/src/lfs/switch.lua +++ b/src/lfs/switch.lua @@ -2,7 +2,7 @@ local module = ... infiniteLoops = {} -local function turnOffIn(pin, on_state, delay, times, pause) +local function turnOffIn(zone, pin, on_state, delay, times, pause) local off = on_state == 0 and 1 or 0 times = times or -1 @@ -10,28 +10,42 @@ local function turnOffIn(pin, on_state, delay, times, pause) infiniteLoops[pin] = true end - print("Heap:", node.heap(), "Actuator Pin:", pin, "Momentary:", delay, "Repeat:", times, "Pause:", pause) + local typeStr = "Actuator Pin:" + local pinZone = pin + if zone ~= nil then + typeStr = "Actuator Zone:" + pinZone = zone + end + + print("Heap:", node.heap(), typeStr, pinZone, "Momentary:", delay, "Repeat:", times, "Pause:", pause) tmr.create():alarm(delay, tmr.ALARM_SINGLE, function() - print("Heap:", node.heap(), "Actuator Pin:", pin, "State:", off) + print("Heap:", node.heap(), typeStr, pinZone, "State:", off) gpio.write(pin, off) times = times - 1 if (times > 0 or infiniteLoops[pin]) and pause then tmr.create():alarm(pause, tmr.ALARM_SINGLE, function() - print("Heap:", node.heap(), "Actuator Pin:", pin, "State:", on_state) + print("Heap:", node.heap(), typeStr, pinZone, "State:", on_state) gpio.write(pin, on_state) - turnOffIn(pin, on_state, delay, times, pause) + turnOffIn(zone, pin, on_state, delay, times, pause) end) end end) end local function updatePin(payload) + local zone = payload.zone local pin = tonumber(payload.pin) local state = tonumber(payload.state) local times = tonumber(payload.times) - print("Heap:", node.heap(), "Actuator Pin:", pin, "State:", state) + + if zone ~= nil then + print("Heap:", node.heap(), "Actuator Zone:", zone, "State:", state) + pin = require("zone_to_pin")(zone) + else + print("Heap:", node.heap(), "Actuator Pin:", pin, "State:", state) + end if infiniteLoops[pin] then infiniteLoops[pin] = false @@ -41,9 +55,12 @@ local function updatePin(payload) blinktimer:start() if payload.momentary then - turnOffIn(pin, state, payload.momentary, times, payload.pause) + turnOffIn(zone, pin, state, payload.momentary, times, payload.pause) if (times == -1) then state = -1 end -- this indicates an infinite repeat - return { pin = pin, state = state } + end + + if zone ~= nil then + return { zone = zone, state = state } else return { pin = pin, state = state } end diff --git a/src/lfs/zone_to_pin.lua b/src/lfs/zone_to_pin.lua new file mode 100644 index 0000000..df24181 --- /dev/null +++ b/src/lfs/zone_to_pin.lua @@ -0,0 +1,21 @@ +local module = ... + +zoneMap = { + [1] = 1, + [2] = 2, + [3] = 5, + [4] = 6, + [5] = 7, + [6] = 9, + ["out"] = 8, +} + +local function zoneToPin(zone) + -- handle strings or numbers + return zoneMap[zone] or zoneMap[tonumber(zone)] +end + +return function(zone) + -- we won't unload this since it's small and frequently called... + return zoneToPin(zone) +end