ESP-AT gateway#

IoT gateway gebaseerd op ESP-AT module, voor gebruik met de iot:bit

_images/iot-protocollen.drawio.png

Fig. 32 Gateway als verbinding tussen het IoT-netwerk en het internet#

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#

_images/microbit-gateway-hardware.drawio.png

Fig. 33 micro:bit met iot:bit (ESP-WiFi hardware module)#

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()