Skip to content
Snippets Groups Projects
Commit 2c547386 authored by Andreas Gattringer's avatar Andreas Gattringer
Browse files

network and time fixes

parent e69474cb
No related branches found
No related tags found
No related merge requests found
......@@ -17,6 +17,26 @@ if sys.implementation.name == "micropython":
else:
from umnp.microcontroller.umock import machine
# SPI PARAMETERS
SPI_BAUD = 2_000_000
SPI_MOSI = machine.Pin(19)
SPI_MISO = machine.Pin(16)
SPI_SCK = machine.Pin(18) # serial clock
# I2C PARAMETERS
I2C_SCL = machine.Pin(4) # serial clock
I2C_SDA = machine.Pin(5) # serial data
# ETHERNET PARAMETERS
ETH_CS = machine.Pin(17) # chip select
ETH_RST = machine.Pin(20) # reset
ETH_IP = "192.168.0.33" # IP of microcontroller
ETH_SUBNET = "255.255.255.0" # ...
ETH_GATEWAY = "192.168.0.1" # ...
ETH_DNS = "192.168.0.2" # technically not necessary
ETH_USE_DHCP = False
async def aggregate_and_send(
device: MeasurementDevice, sht45: SHT45, p_sensor: LPS28DFW
......@@ -25,6 +45,7 @@ async def aggregate_and_send(
t, rh = await sht45.measure()
p, p_t = await p_sensor.measure()
data = f"{t},{rh},{p},{p_t}"
print(f"Sending: '{data}'")
msg = DataMessage(data, device.identifier_raw, device.device_type)
await comm.send_message(msg)
......@@ -32,25 +53,13 @@ async def aggregate_and_send(
async def main():
# configure network
device = MeasurementDevice(device_type=DEVICE_TYPE_RHTP)
spi = machine.SPI(
0, 2_000_000, mosi=machine.Pin(19), miso=machine.Pin(16), sck=machine.Pin(18)
)
i2c = machine.I2C(id=1, scl=machine.Pin(5), sda=machine.Pin(4))
ether = EthernetW5500(
spi,
machine.Pin(17),
machine.Pin(20),
mac=device.generated_mac_raw(),
dhcp=False,
)
ip = "192.168.0.33" # IP of computer with receiver software FIXME name
subnet_mask = "255.255.255.0" # ...
gateway = "192.168.0.1" # ...
dns = "192.168.0.2" # technically not necessary
ether.set_network(ip, subnet_mask, gateway, dns)
spi = machine.SPI(0, SPI_BAUD, mosi=SPI_MOSI, miso=SPI_MISO, sck=SPI_SCK)
i2c = machine.I2C(id=1, scl=I2C_SCL, sda=I2C_SDA)
dev_mac = device.generated_mac_raw()
ether = EthernetW5500(spi, ETH_CS, ETH_RST, dev_mac, ETH_USE_DHCP)
ether.set_network(ETH_IP, ETH_SUBNET, ETH_GATEWAY, ETH_DNS)
device.add_network_adapter(ether)
comm = device.create_communicator()
......@@ -59,7 +68,7 @@ async def main():
task = PeriodicTask(aggregate_and_send, None, 1000, device, sht45, p_sensor)
comm.add_task(task, "aggregate_and_send")
# start
asyncio.run(comm.start())
......
......@@ -10,6 +10,7 @@ sock.bind(("0.0.0.0", DEFAULT_UMNP_DATA_IN_PORT))
while True:
data, addr = sock.recvfrom(2048)
msg = Message.from_bytes(data)
if isinstance(msg, DataMessage):
print(msg.payload())
print(msg.sender_id, msg.payload())
......@@ -2,7 +2,7 @@ import sys
from umnp.communication.abstract_serial_connection import AbstractSerialConnection
if sys.implementation == "micropython":
if sys.implementation.name == "micropython":
import machine
else:
from umnp.microcontroller.umock import machine
......
......@@ -16,15 +16,15 @@ class EthernetW5500(EthernetAdapter):
def __init__(
self,
spi: machine.SPI,
pin1: machine.Pin,
pin2: machine.Pin,
pin_cs: machine.Pin,
pin_rst: machine.Pin,
mac: bytes,
dhcp=False,
):
super().__init__()
self._spi = spi
self._pin1 = pin1
self._pin2 = pin2
self._pin1 = pin_cs
self._pin2 = pin_rst
self._nic = network.WIZNET5K(spi, self._pin1, self._pin2)
self._nic.active(True)
self._nic.config(mac=mac)
......
......@@ -23,6 +23,12 @@ class MeasurementDevice:
def __init__(self, device_type: int = DEVICE_TYPE_UNKNOWN):
self._boot_time = time.time()
self._identifier_raw = machine.unique_id()
if len(self._identifier_raw) > 6:
self._identifier_raw = self._identifier_raw[:6]
elif len(self._identifier_raw) < 6:
self._identifier_raw = (
b"0" * (6 - len(self._identifier_raw)) + self._identifier_raw
)
self._identifier = binascii.hexlify(self.identifier_raw).decode(
MSG_STRING_ENCODING
)
......@@ -82,9 +88,7 @@ class MeasurementDevice:
if not self._communicator:
s = self.create_sender(port=data_port)
r = self.create_receiver(port=cmd_port)
self._communicator = UDPCommunicator(
receiver=r, sender=s, device_id=self.identifier
)
self._communicator = UDPCommunicator(receiver=r, sender=s, device=self)
return self._communicator
@property
......
......@@ -26,17 +26,15 @@ class PeriodicTask:
delay_seconds = 25 / 1000
while True:
last = time.time()
print("A0", args)
print("A1", *args)
last = time.ticks_ms()
result = await func(*args)
print(result)
if call_back:
await call_back(result)
while True:
now = time.ticks_ms()
diff = time.ticks_diff(last, now)
diff = time.ticks_diff(now, last)
# print(last, now, diff)
if diff > self._every_ms:
last = now
break
......
import sys
import time
try:
import datetime
except ImportError:
from umnp.protocol.compat.datetime import datetime
if sys.implementation == "micropython":
# noinspection PyUnresolvedReferences
import machine
from umnp.protocol.umnp_time import seconds_since_epoch
"""
rtc = machine.RTC()
now = rtc.datetime()
year, month, day, hour, minute, second, second_fraction = now
"""
if sys.implementation.name == "micropython":
pass
else:
import datetime
def ts_utc_now_second() -> int:
if sys.implementation == "micropython":
rtc = machine.RTC()
if sys.implementation.name == "micropython":
# rtc = machine.RTC()
# https://docs.micropython.org/en/latest/library/time.html#module-time
# Micropython's time.time() returns the number of seconds, as an integer, since the Epoch
return time.time()
return seconds_since_epoch()
else:
ts = datetime.datetime.now(tz=datetime.timezone.utc).timestamp()
return int(round(ts))
......@@ -33,14 +21,20 @@ def ts_utc_now_second() -> int:
class TimeStamp:
def __init__(self, when=None):
if when:
if when is None:
self._ts = ts_utc_now_second()
else:
elif isinstance(when, TimeStamp):
self._ts = when.value
elif isinstance(when, int):
self._ts = when
else:
error = f"Could not convert time {when} (type {type(when)} to TimeStamp."
error += "Allowed values are integers > 0, TimeStamp objects or None."
raise ValueError(error)
if not isinstance(self._ts, int):
raise ValueError("TimeStamp not an integer")
if not self._ts >= 0:
if self._ts < 0:
raise ValueError("TimeStamp < 0")
@property
......@@ -53,6 +47,6 @@ def valid_timestamp(when: TimeStamp | None) -> TimeStamp:
return TimeStamp()
if not isinstance(when, TimeStamp):
raise ValueError("Expected None TimeStamp")
raise ValueError("Expected None or TimeStamp")
return when
import time
try:
# noinspection PyUnresolvedReferences
import machine
except ImportError:
import umnp.microcontroller.umock.machine as machine
class timezone:
def __init__(self):
pass
@property
def utc(self):
return None
class datetime:
def __init__(self):
self.rtc = machine.RTC()
self._time = int(round(time.time()))
date = self.rtc.datetime()
self.year, self.month, self.day, self.hour, self.minute, self.second, self.second_fraction = date
def _update(self):
date = self.rtc.datetime()
self._time = time.time()
self.year, self.month, self.day, self.hour, self.minute, self.second, self.second_fraction = date
@classmethod
def utcnow(cls):
return datetime()
def timestamp(self):
return self._time
@classmethod
def now(cls, *args):
return datetime()
def timezone(self):
return timezone()
try:
import typing
except ImportError:
pass
......@@ -15,12 +15,12 @@ MSG_BYTE_ORDER: typing.Literal["little", "big"] = "big"
MSG_LEN_PROTOCOL_VERSION: int = 2
# message type, see unmp.protocol.messagetype
MSG_LEN_TYPE: int = 2
MSG_LEN_MESSAGE_TYPE: int = 2
# message timestamp (of sending), seconds since epoch for version 0
MSG_LEN_TIMESTAMP: int = 4
# sender unique id (e.g. serial number)
# sender unique id (e.g. serial number or mac address)
MSG_LEN_SENDER_ID: int = 6
# sender type (e.g. model of measurement or control device attached to microcontroller)
......
......@@ -65,6 +65,10 @@ class Message:
def version(self) -> int:
return self._header.version
@property
def sender_id(self) -> str:
return ":".join(f"{byte:02x}" for byte in self._sender_id)
@property
def data(self) -> bytes:
return self._data
......@@ -76,9 +80,7 @@ class Message:
sender_device_id=self._sender_id,
)
payload = self.data
payload_length = len(payload).to_bytes(
length=MSG_LEN_PAYLOAD_SIZE, byteorder=MSG_BYTE_ORDER
)
payload_length = len(payload).to_bytes(MSG_LEN_PAYLOAD_SIZE, MSG_BYTE_ORDER)
return header.encode() + payload_length + payload
@classmethod
......@@ -86,8 +88,17 @@ class Message:
header = MessageHeader.decode(data)
offset = header.encoded_size_bytes
try:
payload_bytes = data[offset : offset + MSG_LEN_PAYLOAD_SIZE]
payload_length = int.from_bytes(payload_bytes, MSG_BYTE_ORDER)
# print("data:", data)
# print("len(data):", len(data))
# print("offset:", offset)
payload_len_bytes = data[offset : offset + MSG_LEN_PAYLOAD_SIZE]
payload_length = int.from_bytes(payload_len_bytes, MSG_BYTE_ORDER)
# print(f"{payload_len_bytes=}")
# print(f"{payload_length=}")
payload = data[offset + MSG_LEN_PAYLOAD_SIZE :]
# print(f"expected: {payload=}")
# raise OSError
except (KeyError, ValueError):
logging.error("Invalid message: could not extract payload length")
return None
......@@ -96,7 +107,7 @@ class Message:
payload = data[offset:]
if len(payload) != payload_length:
logging.error(
"Invalid message: mismatch between specified and actual payload length"
f"Invalid message: mismatch between specified {payload_length} and actual payload length {len(payload)}."
)
return None
......
......@@ -3,7 +3,7 @@ from umnp.protocol.constants import (
MSG_PROTOCOL_VERSION,
MSG_LEN_PROTOCOL_VERSION,
MSG_BYTE_ORDER,
MSG_LEN_TYPE,
MSG_LEN_MESSAGE_TYPE,
MSG_LEN_TIMESTAMP,
MSG_LEN_SENDER_ID,
MSG_LEN_SENDER_TYPE,
......@@ -51,7 +51,7 @@ class MessageHeader:
@property
def message_type_encoded(self) -> bytes:
return self.message_type.to_bytes(MSG_LEN_TYPE, MSG_BYTE_ORDER)
return self.message_type.to_bytes(MSG_LEN_MESSAGE_TYPE, MSG_BYTE_ORDER)
@property
def version(self) -> int:
......@@ -77,9 +77,15 @@ class MessageHeader:
def timestamp(self) -> TimeStamp:
return self._timestamp
@property
def sender_device_id_encoded(self) -> bytes:
if len(self._sender_device_id) != 6:
raise ValueError("Device ID length != 6 bytes")
return self._sender_device_id
def encode(self) -> bytes:
version = self.version_encoded
sender_id = self._sender_device_id
sender_id = self.sender_device_id_encoded
sender_type = self.sender_dev_type_encoded
message_type = self.message_type_encoded
timestamp = self.timestamp_encoded
......@@ -91,22 +97,24 @@ class MessageHeader:
MSG_LEN_PROTOCOL_VERSION
+ MSG_LEN_SENDER_ID
+ MSG_LEN_SENDER_TYPE
+ MSG_LEN_TYPE
+ MSG_LEN_MESSAGE_TYPE
+ MSG_LEN_TIMESTAMP
)
@classmethod
def decode(cls, data: bytes):
offset = 0
protocol_bytes = -1
if not (isinstance(data, bytes)):
logging.error("Invalid message header: not bytes")
return None
try:
protocol_bytes = data[:MSG_LEN_PROTOCOL_VERSION]
protocol_version = int.from_bytes(protocol_bytes, MSG_BYTE_ORDER)
cls._version = protocol_version
except (KeyError, ValueError):
logging.error(f"Protocol bytes: {protocol_bytes}")
logging.error("Invalid message header: could not extract version")
return None
offset += MSG_LEN_PROTOCOL_VERSION
......@@ -122,25 +130,28 @@ class MessageHeader:
return None
try:
message_type_bytes = data[offset : offset + MSG_LEN_TYPE]
message_type_bytes = data[offset : offset + MSG_LEN_MESSAGE_TYPE]
message_type = int.from_bytes(message_type_bytes, MSG_BYTE_ORDER)
except (KeyError, ValueError):
logging.error("Invalid message payload: could not extract version")
return None
offset += MSG_LEN_TYPE
offset += MSG_LEN_MESSAGE_TYPE
if protocol_version < 0 or protocol_version > MSG_PROTOCOL_VERSION:
err = f"Invalid protocol version {protocol_version}, outside of range [0, {MSG_PROTOCOL_VERSION}]"
logging.error(err)
return None
message_ts = None
try:
message_ts = data[offset : offset + MSG_LEN_TIMESTAMP]
timestamp = TimeStamp(int.from_bytes(message_ts, byteorder=MSG_BYTE_ORDER))
except (KeyError, ValueError):
logging.error("Invalid message timestamp: could not extract version")
message_ts_int = int.from_bytes(message_ts, byteorder=MSG_BYTE_ORDER)
timestamp = TimeStamp(message_ts_int)
except (KeyError, ValueError) as e:
logging.error(
f"Invalid message timestamp: could not extract timestamp: {e}"
)
return None
return cls(
......
import time
def seconds_since_epoch() -> int:
return int(time.time())
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment