feat: sensor readings with JWT auth, OLED display, data_destination_url

This commit is contained in:
2026-06-09 13:20:10 +02:00
parent d1a28d989c
commit a25bbf0eb3
4 changed files with 137 additions and 13 deletions

View File

@@ -290,7 +290,7 @@ def run():
if caps.get('has_nfc'):
from provisioning_nfc import run_provisioning_nfc
_show(['PROVISIONING', 'NFC mode', '', 'Tap writer badge', ''])
cfg = run_provisioning_nfc(_nfc)
cfg = run_provisioning_nfc()
else:
from provisioning_server import run_provisioning
wlan_sta = network.WLAN(network.STA_IF)
@@ -335,11 +335,11 @@ def run():
if node_type == 'sensor':
from node_sensor import run_sensor
run_sensor(cfg, caps)
run_sensor(cfg, caps, checkin_result['token'])
elif node_type == 'relay':
from node_relay import run_relay
run_relay(cfg, caps)
run_relay(cfg, caps, checkin_result['token'])
else:
print('[operational] unknown node_type:', node_type)

View File

@@ -13,7 +13,7 @@
"node_type": "sensor",
"node_label": "node-01",
"identity_iot_url": "https://api.yourhost.com/API/V1/identity_layer/identity_iot/",
"data_destination_url": "",
"data_destination_url": "https://api.parta.app/API/V1/identity_iot/",
"pinout": {
"i2c0_sda": 4,
"i2c0_scl": 5,

View File

@@ -7,7 +7,7 @@
import utime
def run_relay(cfg, caps):
def run_relay(cfg, caps, jwt_token=''):
"""Main loop for relay node type. Called from boot_manager."""
print('[relay] starting — relay:', caps.get('has_relay'))

View File

@@ -5,16 +5,140 @@
# ============================================================+
import utime
import ujson
import machine
try:
import uurequests as requests
except ImportError:
import urequests as requests
from identity_iot_aes import aes_cbc_encrypt, aes_cbc_decrypt
def run_sensor(cfg, caps):
"""Main loop for sensor node type. Called from boot_manager."""
print('[sensor] starting — sensor:', caps.get('has_sensor'))
def _read_internal_temp():
sensor = machine.ADC(4)
reading = sensor.read_u16()
voltage = reading * 3.3 / 65535
temp_c = 27 - (voltage - 0.706) / 0.001721
return round(temp_c, 2)
# TODO: read DHT22 / other sensor
# TODO: POST readings to backend via AES channel
# TODO: polling interval from cfg
def _read_dht22(dht_sensor):
try:
dht_sensor.measure()
return {
'temperature': dht_sensor.temperature(),
'humidity': dht_sensor.humidity(),
}
except Exception as e:
print('[sensor] DHT22 read error:', e)
return None
def _show_reading(reading, label):
try:
import boot_manager
temp = reading.get('temperature', reading.get('temp_internal', '--'))
hum = reading.get('humidity')
boot_manager._show([
'Identity IoT',
label or 'sensor',
'Temp: ' + str(temp) + 'C',
'Hum: ' + (str(hum) + '%' if hum is not None else '--'),
'',
])
except Exception as e:
print('[sensor] display error:', e)
def _post_reading(cfg, key, iv, payload, jwt_token=''):
url = cfg.get('data_destination_url', '')
if not url:
print('[sensor] no data_destination_url configured')
return False
try:
body = ujson.dumps({'data': aes_cbc_encrypt(ujson.dumps(payload), key, iv)})
resp = requests.post(
url,
headers={
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + jwt_token,
},
data=body,
timeout=10,
)
raw = resp.text
resp.close()
outer = ujson.loads(raw)
clear = aes_cbc_decrypt(outer['data'], key, iv)
result = ujson.loads(clear)
if result.get('status') == 'ok':
print('[sensor] reading posted ok')
return True
print('[sensor] post error:', result.get('code', 'unknown'))
return False
except Exception as e:
print('[sensor] post failed:', e)
return False
def run_sensor(cfg, caps, jwt_token=''):
print('[sensor] starting — has_sensor:', caps.get('has_sensor'))
key = cfg['comm_key'].encode() if isinstance(cfg['comm_key'], str) else cfg['comm_key']
iv = cfg['comm_iv'].encode() if isinstance(cfg['comm_iv'], str) else cfg['comm_iv']
poll_interval = int(cfg.get('poll_interval', 60))
temp_min = cfg.get('temp_min')
temp_max = cfg.get('temp_max')
node_id = cfg.get('node_id', '')
tenant_id = cfg.get('tenant_id', '')
label = cfg.get('node_label', '')
dht_sensor = None
if caps.get('has_sensor'):
try:
import dht
from machine import Pin
dht_pin = cfg.get('pinout', {}).get('dht_pin', 15)
dht_sensor = dht.DHT22(Pin(dht_pin))
except Exception as e:
print('[sensor] DHT22 init error:', e)
while True:
print('[sensor] polling...')
utime.sleep(10)
reading = {}
reading['temp_internal'] = _read_internal_temp()
if dht_sensor:
dht_data = _read_dht22(dht_sensor)
if dht_data:
reading['temperature'] = dht_data['temperature']
reading['humidity'] = dht_data['humidity']
print('[sensor] reading:', reading)
_show_reading(reading, label)
should_post = True
if temp_min is not None or temp_max is not None:
temp = reading.get('temperature', reading.get('temp_internal'))
in_range = True
if temp_min is not None and temp < float(temp_min):
in_range = False
if temp_max is not None and temp > float(temp_max):
in_range = False
if in_range:
should_post = False
if should_post:
payload = {
'request': 'iot_node_reading',
'node_id': node_id,
'tenant_id': tenant_id,
'label': label,
'reading': reading,
'ts': utime.time(),
}
_post_reading(cfg, key, iv, payload, jwt_token)
utime.sleep(poll_interval)