feat: sensor readings with JWT auth, OLED display, data_destination_url
This commit is contained in:
@@ -290,7 +290,7 @@ def run():
|
|||||||
if caps.get('has_nfc'):
|
if caps.get('has_nfc'):
|
||||||
from provisioning_nfc import run_provisioning_nfc
|
from provisioning_nfc import run_provisioning_nfc
|
||||||
_show(['PROVISIONING', 'NFC mode', '', 'Tap writer badge', ''])
|
_show(['PROVISIONING', 'NFC mode', '', 'Tap writer badge', ''])
|
||||||
cfg = run_provisioning_nfc(_nfc)
|
cfg = run_provisioning_nfc()
|
||||||
else:
|
else:
|
||||||
from provisioning_server import run_provisioning
|
from provisioning_server import run_provisioning
|
||||||
wlan_sta = network.WLAN(network.STA_IF)
|
wlan_sta = network.WLAN(network.STA_IF)
|
||||||
@@ -335,11 +335,11 @@ def run():
|
|||||||
|
|
||||||
if node_type == 'sensor':
|
if node_type == 'sensor':
|
||||||
from node_sensor import run_sensor
|
from node_sensor import run_sensor
|
||||||
run_sensor(cfg, caps)
|
run_sensor(cfg, caps, checkin_result['token'])
|
||||||
|
|
||||||
elif node_type == 'relay':
|
elif node_type == 'relay':
|
||||||
from node_relay import run_relay
|
from node_relay import run_relay
|
||||||
run_relay(cfg, caps)
|
run_relay(cfg, caps, checkin_result['token'])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print('[operational] unknown node_type:', node_type)
|
print('[operational] unknown node_type:', node_type)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"node_type": "sensor",
|
"node_type": "sensor",
|
||||||
"node_label": "node-01",
|
"node_label": "node-01",
|
||||||
"identity_iot_url": "https://api.yourhost.com/API/V1/identity_layer/identity_iot/",
|
"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": {
|
"pinout": {
|
||||||
"i2c0_sda": 4,
|
"i2c0_sda": 4,
|
||||||
"i2c0_scl": 5,
|
"i2c0_scl": 5,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import utime
|
import utime
|
||||||
|
|
||||||
|
|
||||||
def run_relay(cfg, caps):
|
def run_relay(cfg, caps, jwt_token=''):
|
||||||
"""Main loop for relay node type. Called from boot_manager."""
|
"""Main loop for relay node type. Called from boot_manager."""
|
||||||
print('[relay] starting — relay:', caps.get('has_relay'))
|
print('[relay] starting — relay:', caps.get('has_relay'))
|
||||||
|
|
||||||
|
|||||||
140
node_sensor.py
140
node_sensor.py
@@ -5,16 +5,140 @@
|
|||||||
# ============================================================+
|
# ============================================================+
|
||||||
|
|
||||||
import utime
|
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):
|
def _read_internal_temp():
|
||||||
"""Main loop for sensor node type. Called from boot_manager."""
|
sensor = machine.ADC(4)
|
||||||
print('[sensor] starting — sensor:', caps.get('has_sensor'))
|
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
|
def _read_dht22(dht_sensor):
|
||||||
# TODO: polling interval from cfg
|
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:
|
while True:
|
||||||
print('[sensor] polling...')
|
reading = {}
|
||||||
utime.sleep(10)
|
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user