ESP-AT gateway#
IoT gateway gebaseerd op ESP-AT module, voor gebruik met de iot:bit
De ESP-AT gateway vormt de verbinding tussen een lokaal microbit-IoT-netwerk en het internet. Lokale (uplink) sensorberichten in LPP-formaat worden omgezet in JSON, en verstuurd via MQTT. Downlink actuatorberichten in JSON-formaat worden ontvangen via MQTT, omgezet naar LPP-formaat, en verstuurd naar het lokale microbit-netwerk.
De IoT-knopen in het lokale netwerk hebben een (4-bytes) Node-ID, zoals gebruikelijk in het platform voor het keuzethema-materiaal. Aan de hand van de ontvangen uplink-berichten bepaalt de gateway welke nodes in het lokale netwerk aanwezig zijn. Alleen downlink-berichten voor deze nodes worden naar het lokale microbit-netwerk verstuurd.
Voorbeelden van JSON-berichten#
Downlink-bericht (actuators):
{"2": {"dOut": 0},
"8": {"aOut": 12}
}
Merk op dat deze in tegenstelling tot de uplink-berichten alleen uit de payload bestaat. (Ja, dat is inderdaar niet erg consequent… Maar min of meer zo gegroeid… In een volgende versie aanpassen?)
NB: in het binaire formaat in het microbit-netwerk gebruiken we voor sensoren en actuatoren hetzelfde formaat: de eerste 5 bytes zijn de protocol-id (1), node-id (2 bytes), en counter (2 bytes); daarna volgt de payload, in binair LPP-formaat.
Uplink-bericht (sensoren):
{"nodeid": "fe3d",
"counter": 3027,
"payload": {
"0": {"temperature": 235},
"1": {"barometer": 10093},
"2": {"dOut": 1},
"8": {"aOut": 255}
}
}
Gateway hardware: micro:bit en iot:bit#
De gateway bestaat uit een micro:bit in combinatie met een iot:bit bordje. Dit bordje heeft een ESP WiFi-module die via de serieverbinding met de micro:bit communiceert. De micro:bit bestuurt deze ESP WiFi-module door middel van AT-opdrachten.
Gateway-code#
# ESP-AT gateway for microbit IoT-network
# version 0.2 (EJD - 20230619)
from microbit import *
import radio
from espat import *
from ulpp import bytes_to_dict, dict_to_bytes
import json
# gateway connection configuration
wifi_ssid = 'infvo-iot'
wifi_password = 'LISP77(Midas'
mqtt_user = 'mqtttest'
mqtt_password = 'testmqtt'
mqtt_host = 'infvopedia.nl'
mqtt_port = 1883
uplink_tag = 0x0A
downlink_tag = 0x0B
radio.on()
def log (msg: str):
radio.send(msg)
local_nodes = []
# handle received MQTT message (from broker)
def on_mqtt_message (topic: str, msg: str):
log('-topic: ' + topic)
log('-msg: ' + msg)
# items = topic.decode('ascii').split('/')
items = topic.split('/')
if len(items) == 3 and items[0] == 'node' and items[2] == 'actuators':
nodeID = int(items[1], 16)
if nodeID in local_nodes:
lpp_bytes = dict_to_bytes(json.loads(msg))
downlink_header = bytes([0x0B, nodeID // 256, nodeID % 256, 0, 0])
radio.send_bytes(downlink_header + lpp_bytes)
# publish sensor data in JSON format to MQTT
def sensordata_to_mqtt(data):
nodeID = data[1] * 256 + data[2]
if not nodeID in local_nodes:
local_nodes.append(nodeID)
nodeID_hex = '{0:x}'.format(nodeID)
counter = data[3] * 256 + data[4]
lpp_dict = bytes_to_dict(data[5:])
msg = {'nodeid': nodeID_hex,
'counter': counter,
'payload': lpp_dict
}
log(json.dumps(msg))
at_mqtt_publish('node/{a}/sensors'.format(a=nodeID_hex),
json.dumps(msg),
0, False)
# should this be done in the library???
uart.init(baudrate=115200, tx=pin8, rx=pin12) # connect to ESP8266
sleep(100)
display.show('0')
at_init_ESP()
display.show('1')
while not at_wifi_connected():
try:
at_wifi_connect(wifi_ssid, wifi_password)
except Exception as exc:
display.show(at_errorcode)
sleep(2000)
at_init_ESP()
display.show('1')
display.show('2')
mac_address = at_get_mac_address()
log('My mac-addr: ' + mac_address)
at_mqtt_set_userconfig(1, 'gw-'+mac_address, mqtt_user, mqtt_password)
display.show('3')
mqtt_retries = 0
while True:
if not at_mqtt_connected():
if not at_wifi_connected() or mqtt_retries > 20:
reset()
mqtt_retries += 1
at_mqtt_connect(mqtt_host, mqtt_port, 1)
at_set_on_mqtt_message(on_mqtt_message)
at_mqtt_subscribe('node/+/actuators', 0)
display.show('M')
msg = radio.receive_bytes()
if msg != None:
if msg[0] == uplink_tag:
sensordata_to_mqtt(msg)
if button_a.was_pressed():
if at_mqtt_connected():
at_mqtt_publish('gateway/hello', 'button A', 0, False)
at_check_input()