MeTIOT 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metiot-0.1.0/.gitignore +26 -0
- metiot-0.1.0/CMakeLists.txt +18 -0
- metiot-0.1.0/LICENSE +21 -0
- metiot-0.1.0/PKG-INFO +53 -0
- metiot-0.1.0/README.md +45 -0
- metiot-0.1.0/docs/API_REFERENCE.md +138 -0
- metiot-0.1.0/docs/DEVICE_PROTOCOL_DEV_GUIDE.md +23 -0
- metiot-0.1.0/docs/PYTHON_LIB_GUIDE.md +125 -0
- metiot-0.1.0/docs/TCP_PROTOCOL_SPEC.md +182 -0
- metiot-0.1.0/docs/images/DeviceData.jpg +0 -0
- metiot-0.1.0/docs/images/DeviceIdentifierResponse.jpg +0 -0
- metiot-0.1.0/docs/images/FishTankSensorAttributeResponse.jpg +0 -0
- metiot-0.1.0/docs/images/Packet.jpg +0 -0
- metiot-0.1.0/examples/.gitkeep +0 -0
- metiot-0.1.0/pyproject.toml +16 -0
- metiot-0.1.0/src/MeTIOTCommunication/CMakeLists.txt +25 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/client.hpp +94 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/core/exceptions.hpp +30 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/core/socket_core.hpp +38 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/interfaces/event_handler.hpp +32 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/protocol/abstract_protocol.hpp +47 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/protocol/device_agnostic_protocol.hpp +32 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/protocol/fish_tank_protocol.hpp +19 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/protocol/protocol_constants.hpp +42 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/utils/byte_conversion.hpp +29 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/utils/cobs.hpp +26 -0
- metiot-0.1.0/src/MeTIOTCommunication/include/utils/crc.hpp +29 -0
- metiot-0.1.0/src/MeTIOTCommunication/src/client.cpp +163 -0
- metiot-0.1.0/src/MeTIOTCommunication/src/core/socket_core.cpp +51 -0
- metiot-0.1.0/src/MeTIOTCommunication/src/protocol/abstract_protocol.cpp +64 -0
- metiot-0.1.0/src/MeTIOTCommunication/src/protocol/device_agnostic_protocol.cpp +22 -0
- metiot-0.1.0/src/MeTIOTCommunication/src/protocol/fish_tank_protocol.cpp +47 -0
- metiot-0.1.0/src/MeTIOTCommunication/src/utils/byte_conversion.cpp +88 -0
- metiot-0.1.0/src/MeTIOTCommunication/src/utils/cobs.cpp +82 -0
- metiot-0.1.0/src/MeTIOTCommunication/src/utils/crc.cpp +17 -0
- metiot-0.1.0/src/python_bindings/CMakeLists.txt +18 -0
- metiot-0.1.0/src/python_bindings/PythonEventHandler.hpp +50 -0
- metiot-0.1.0/src/python_bindings/bindings.cpp +140 -0
- metiot-0.1.0/tests/test_basic.py +5 -0
metiot-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
venv/
|
|
2
|
+
**/build*/
|
|
3
|
+
|
|
4
|
+
CMakeCache.txt
|
|
5
|
+
CMakeFiles/
|
|
6
|
+
cmake_install.cmake
|
|
7
|
+
install_manifest.txt
|
|
8
|
+
|
|
9
|
+
*.o
|
|
10
|
+
*.obj
|
|
11
|
+
*.lo
|
|
12
|
+
*.la
|
|
13
|
+
*.d
|
|
14
|
+
*.a
|
|
15
|
+
*.so
|
|
16
|
+
*.dylib
|
|
17
|
+
*.dll
|
|
18
|
+
*.pyd
|
|
19
|
+
|
|
20
|
+
*.pyc
|
|
21
|
+
*.pyo
|
|
22
|
+
dist/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
|
|
25
|
+
*~
|
|
26
|
+
.vscode/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.10)
|
|
2
|
+
project(MeTIOTPythonLibrary LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 23)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
7
|
+
|
|
8
|
+
include(FetchContent)
|
|
9
|
+
FetchContent_Declare(
|
|
10
|
+
pybind11
|
|
11
|
+
GIT_REPOSITORY https://github.com/pybind/pybind11.git
|
|
12
|
+
GIT_TAG v3.0.1
|
|
13
|
+
)
|
|
14
|
+
FetchContent_MakeAvailable(pybind11)
|
|
15
|
+
|
|
16
|
+
add_subdirectory(src/MeTIOTCommunication)
|
|
17
|
+
|
|
18
|
+
add_subdirectory(src/python_bindings)
|
metiot-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Microelectronic Technologies
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
metiot-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: MeTIOT
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Communication handling for all MeT IOT devices
|
|
5
|
+
Author-Email: Cody Jackson <cjackson@micr.com.au>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# MeT IOT Communication
|
|
10
|
+
|
|
11
|
+
## Protocol Information
|
|
12
|
+
|
|
13
|
+
Communication between MeT IOT devices and this library are conducted over TCP sockets. To see more in-depth details about this protocol including encryption standard see [Protocol Docs](docs/Protocol.md)
|
|
14
|
+
|
|
15
|
+
## How To Use
|
|
16
|
+
|
|
17
|
+
For all information on **how** to use go to [HowToUse Docs](docs/HowToUse.md). Also see examples of use in the [Examples folder](examples/)
|
|
18
|
+
|
|
19
|
+
## How to Compile
|
|
20
|
+
|
|
21
|
+
Linux based systems:
|
|
22
|
+
|
|
23
|
+
1. Create and navigate into build directory
|
|
24
|
+
```bash
|
|
25
|
+
mkdir build
|
|
26
|
+
cd build
|
|
27
|
+
```
|
|
28
|
+
2. Compile library using CMake
|
|
29
|
+
```bash
|
|
30
|
+
cmake ..
|
|
31
|
+
cmake --build . --config Release
|
|
32
|
+
```
|
|
33
|
+
3. Export path to python
|
|
34
|
+
```bash
|
|
35
|
+
export PYTHONPATH=./src/python_bindings:$PYTHONPATH
|
|
36
|
+
```
|
|
37
|
+
4. Now you can use the library. If you first want to test it you can try
|
|
38
|
+
```bash
|
|
39
|
+
python3
|
|
40
|
+
```
|
|
41
|
+
```py
|
|
42
|
+
import MeTIOT
|
|
43
|
+
client = MeTIOT.DeviceClient("0.0.0.0", 12345)
|
|
44
|
+
type(client)
|
|
45
|
+
client.connect()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## How to Add More Features
|
|
49
|
+
|
|
50
|
+
**WIP** docs
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
metiot-0.1.0/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# MeT IOT Communication
|
|
2
|
+
|
|
3
|
+
## Protocol Information
|
|
4
|
+
|
|
5
|
+
Communication between MeT IOT devices and this library are conducted over TCP sockets. To see more in-depth details about this protocol including encryption standard see [Protocol Docs](docs/Protocol.md)
|
|
6
|
+
|
|
7
|
+
## How To Use
|
|
8
|
+
|
|
9
|
+
For all information on **how** to use go to [HowToUse Docs](docs/HowToUse.md). Also see examples of use in the [Examples folder](examples/)
|
|
10
|
+
|
|
11
|
+
## How to Compile
|
|
12
|
+
|
|
13
|
+
Linux based systems:
|
|
14
|
+
|
|
15
|
+
1. Create and navigate into build directory
|
|
16
|
+
```bash
|
|
17
|
+
mkdir build
|
|
18
|
+
cd build
|
|
19
|
+
```
|
|
20
|
+
2. Compile library using CMake
|
|
21
|
+
```bash
|
|
22
|
+
cmake ..
|
|
23
|
+
cmake --build . --config Release
|
|
24
|
+
```
|
|
25
|
+
3. Export path to python
|
|
26
|
+
```bash
|
|
27
|
+
export PYTHONPATH=./src/python_bindings:$PYTHONPATH
|
|
28
|
+
```
|
|
29
|
+
4. Now you can use the library. If you first want to test it you can try
|
|
30
|
+
```bash
|
|
31
|
+
python3
|
|
32
|
+
```
|
|
33
|
+
```py
|
|
34
|
+
import MeTIOT
|
|
35
|
+
client = MeTIOT.DeviceClient("0.0.0.0", 12345)
|
|
36
|
+
type(client)
|
|
37
|
+
client.connect()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## How to Add More Features
|
|
41
|
+
|
|
42
|
+
**WIP** docs
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
The `MeTIOT` library provides a high-level interface for interacting with MeTIOT IoT devices via TCP. The core of the library is the `DeviceClient`, which manages the connection, event loops, and protocol handling.
|
|
4
|
+
|
|
5
|
+
## 1. Client Library Components (`MeTIOT`)
|
|
6
|
+
|
|
7
|
+
The `DeviceClient` class is the primary entry point for all device interactions.
|
|
8
|
+
|
|
9
|
+
### Lifecycle Management
|
|
10
|
+
|
|
11
|
+
#### `DeviceClient(ip: str, port: int)`
|
|
12
|
+
|
|
13
|
+
*Constructor* | **Module:** `MeTIOT`
|
|
14
|
+
|
|
15
|
+
Initializes the client object with the target device's network information.
|
|
16
|
+
|
|
17
|
+
* **Arguments**
|
|
18
|
+
* `ip` (str): IPv4 address of the device.
|
|
19
|
+
* `port` (int): Communication port.
|
|
20
|
+
* **Errors:** Raises `SocketError` if the IP address is invalid or unsupported.
|
|
21
|
+
|
|
22
|
+
#### `connect()`
|
|
23
|
+
|
|
24
|
+
*Method* | **Module:** `MeTIOT.DeviceClient`
|
|
25
|
+
|
|
26
|
+
Establishes the TCP connection and performs initial handshake/device discovery.
|
|
27
|
+
|
|
28
|
+
* **Errors:** Raises `SocketError` if the device is unreachable.
|
|
29
|
+
|
|
30
|
+
#### `disconnect()`
|
|
31
|
+
|
|
32
|
+
*Method* | **Module:** `MeTIOT.DeviceClient`
|
|
33
|
+
|
|
34
|
+
Safely closes the active TCP connection.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### Protocol & Identity
|
|
39
|
+
|
|
40
|
+
#### `get_protocol_handler()`
|
|
41
|
+
|
|
42
|
+
*Method* | **Module:** `MeTIOT.DeviceClient`
|
|
43
|
+
|
|
44
|
+
Returns the `ProtocolHandler` specific to the current device type. use this to encode/decode bytes.
|
|
45
|
+
|
|
46
|
+
* **Returns:** `ProtocolHandler`
|
|
47
|
+
|
|
48
|
+
> [!TIP]
|
|
49
|
+
> Use this instead of the deprecated `get_specific_protocol_handler()`
|
|
50
|
+
|
|
51
|
+
#### `get_unique_id()`
|
|
52
|
+
|
|
53
|
+
*Method* | **Module:** `MeTIOT.DeviceClient`
|
|
54
|
+
|
|
55
|
+
Retrieves the globally unique 64-bit ID assigned to the hardware.
|
|
56
|
+
|
|
57
|
+
* **Returns:** 8-byte Device ID.
|
|
58
|
+
|
|
59
|
+
#### `get_device_type()`
|
|
60
|
+
|
|
61
|
+
*Method* | **Module:** `MeTIOT.DeviceClient`
|
|
62
|
+
|
|
63
|
+
Identifies the model/type of the connected device.
|
|
64
|
+
|
|
65
|
+
* **Returns:** `MeTIOT.DeviceType`
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### Asynchronous Operations
|
|
70
|
+
|
|
71
|
+
#### `assign_handlers(on_data, on_warning=None, on_fatal=None)`
|
|
72
|
+
|
|
73
|
+
*Method* | **Module:** `MeTIOT.DeviceClient`
|
|
74
|
+
|
|
75
|
+
Registers callback functions for the background listening thread.
|
|
76
|
+
|
|
77
|
+
| Argument | Signature | Description |
|
|
78
|
+
|-|-|-|
|
|
79
|
+
| `on_data` | `(device, header, data)` | Triggered on successful packet reception. |
|
|
80
|
+
| `on_warning` | `(device, msg)` | **Optional.** Triggered on non-fatal issues (e.g., CRC mismatch). |
|
|
81
|
+
| `on_fatal` | `(device, msg)` | **Optional.** Triggered when the loop crashes or 10 warnings occur |
|
|
82
|
+
|
|
83
|
+
#### `start_listening()`
|
|
84
|
+
|
|
85
|
+
*Method* | **Module:** `MeTIOT.DeviceClient`
|
|
86
|
+
|
|
87
|
+
Spawns a background thread to monitor the TCP socket. **Requires handlers to be assigned first.**
|
|
88
|
+
|
|
89
|
+
* **Errors:** `LibraryError` if `assign_handlers` has not been called.
|
|
90
|
+
|
|
91
|
+
#### `send_packet(packet: bytes)`
|
|
92
|
+
|
|
93
|
+
*Method* | **Module:** `MeTIOT.DeviceClient`
|
|
94
|
+
|
|
95
|
+
Sends a raw byte buffer to the device.
|
|
96
|
+
|
|
97
|
+
* **Arguments:** `packet` (bytes): The encoded payload.
|
|
98
|
+
* **Errors:** `SocketError` on transmission failure.
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
> [!IMPORTANT]
|
|
102
|
+
> **WIP** Document. Below text may not be correct.
|
|
103
|
+
|
|
104
|
+
## 2. Protocol Handler Methods (Accessed via `client.get_protocol_handler()`)
|
|
105
|
+
|
|
106
|
+
The methods available on the protocol handler instance depend entirely on the connected device type. The protocol handler also contains universal utility methods for packet handling.
|
|
107
|
+
|
|
108
|
+
### 2.1 Universal Protocol Utility Methods
|
|
109
|
+
|
|
110
|
+
| Method | Returns | Description |
|
|
111
|
+
|-|-|-|
|
|
112
|
+
| `deconstruct_packet(packet: bytes)` | `tuple[int, bytes]` | Decodes the packet buffer, validating CRC, performing COBS unstuffing, and returning the Data Header and raw Data Payload. |
|
|
113
|
+
| `interpret_data(data_payload: bytes)` | `dict` | Interprets the raw Data Payload into a key-value dictionary using known Sub-Headers and Data Types. Keys match the Data Field purpose (e.g., "Temperature_C"). |
|
|
114
|
+
| `fetch_last_packet()` | `bytes` | Returns the raw byte buffer of the last packet successfully created by this protocol handler instance. Useful for re-sending after a `MalformedPacketNotification`. |
|
|
115
|
+
|
|
116
|
+
### 2.2 Fish Tank Protocol Methods (`Device ID: 0xFF`)
|
|
117
|
+
|
|
118
|
+
These methods are available if the device type is "Fish Tank"
|
|
119
|
+
|
|
120
|
+
| Method | Returns | Purpose | Arguments |
|
|
121
|
+
|-|-|-|-|
|
|
122
|
+
|
|
123
|
+
## 3. Protocol Constants (`MeTIOT.ProtocolConstants`)
|
|
124
|
+
|
|
125
|
+
These constants are integers representing the 1-byte Header/Sub-Header codes used in the communication. All constants mentioned here are the exposed constants. In other documentation the codes here are referenced as *Device-to-Library Codes*.
|
|
126
|
+
|
|
127
|
+
### 3.1 Standard Data Header Codes
|
|
128
|
+
|
|
129
|
+
| Constant Name | Value | Description |
|
|
130
|
+
|:---|:---|:---|
|
|
131
|
+
| `MalformedPacketNotification` | `0xFF` | Notification for a corrupted/unparsable packet. |
|
|
132
|
+
| `DeviceIdentifier` | `0xFE` | Response from the device containing the Device ID and Unique ID. |
|
|
133
|
+
| `DataTransfer` | `0xFD` | Unsolicited data/status update from the device. |
|
|
134
|
+
|
|
135
|
+
### 3.2 Fish Tank Data Header Codes
|
|
136
|
+
|
|
137
|
+
| Constant Name | Value | Description |
|
|
138
|
+
|-|-|-|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Device Protocol
|
|
2
|
+
|
|
3
|
+
## Current Device Protocol Implementation
|
|
4
|
+
|
|
5
|
+
**Challenge:** Our `DeviceClient` class's `protocolHandler` is of the type `std::unique_ptr<AbstractProtocol>` which stops us from calling device protocol specific functions (e.g., function to create a calibration packet with specific parameters.)
|
|
6
|
+
|
|
7
|
+
**Solution:** Ideally the python programmer using this library simply calls the same function to get all of their device specific commands regardless of the device type. The solution to this issue is during the python function call `get_specific_protocol_handler` we automatically resolve exactly what protocol data type we are using (stored in `deviceType` for simplicity) then we dynamically cast the `std::unique_ptr<AbstractProtocol>` type into what ever the device protocol actually is.
|
|
8
|
+
|
|
9
|
+
## Creating New Device Protocols
|
|
10
|
+
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
> The above information in `Current Device Protocol Implementation` is important to read to understand *why* you are doing the following
|
|
13
|
+
|
|
14
|
+
When creating a new device protocol (to support device specific protocol commands) following these steps should lead to success:
|
|
15
|
+
|
|
16
|
+
1. Add protocol .hpp file to `include/protocol/` and .cpp file to `src/protocol/`.
|
|
17
|
+
2. Create a class inheriting `AbstractProtocol` (This gives access to required functions like `constructPacket`).
|
|
18
|
+
3. Copy a similar workflow to other reference protocols. Function called -> Create data field -> Call constructPacket() -> return packet.
|
|
19
|
+
4. Add your new device type to `enum class DeviceType` in `include/client.hpp`.
|
|
20
|
+
5. Modify the device identification function in `src/client.cpp` to support your new protocol.
|
|
21
|
+
6. Modify `/src/python_bindings/bindings.cpp` to support your new protocol. You need to add support for protocol specific functions as well as support for dynamically casting the `AbstractProtocol` pointer to your Protocol pointer.
|
|
22
|
+
|
|
23
|
+
> If you want to add more OUTGOING fields or any protocol related header/subheader names add them to `include/protocol/protocol_constants.hpp`.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Guide - How To Use the Library
|
|
2
|
+
|
|
3
|
+
This guide provides practical steps for using the MeT IoT Python client library. For details on the underlying packet structure, see [TCP_PROTOCOL_SPEC.md](TCP_PROTOCOL_SPEC.md).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
The MeT IoT library is *soon to be* available on PyPI.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install x
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Device Discovery
|
|
14
|
+
|
|
15
|
+
MeT IoT devices broadcast their presence using Zeroconf (mDNS). The library includes a function to listen for and find available devices on your local network.
|
|
16
|
+
|
|
17
|
+
1. **WIP**
|
|
18
|
+
|
|
19
|
+
## Connecting to a Device
|
|
20
|
+
|
|
21
|
+
Establish a persistent TCP connection and perform the initial device identification handshake.
|
|
22
|
+
|
|
23
|
+
1. **Create Client:** Create a client with the device's IP & Port.
|
|
24
|
+
```py
|
|
25
|
+
import MeTIOT
|
|
26
|
+
|
|
27
|
+
client = MeTIOT.DeviceClient("192.168.1.100", 12345)
|
|
28
|
+
```
|
|
29
|
+
2. **Connect:** Use the `connect()` method to connect to the device.
|
|
30
|
+
```py
|
|
31
|
+
client.connect()
|
|
32
|
+
```
|
|
33
|
+
**Behind the Scenes:** After connecting to the TCP socket, the client sends a Device Identification Request and waits for the Device Identifier response. This identifies the device type and its unique ID.
|
|
34
|
+
|
|
35
|
+
3. **Device Protocol:** For sending and interpreting message you will need a protocol handler.
|
|
36
|
+
```py
|
|
37
|
+
protocol = client.get_protocol_handler()
|
|
38
|
+
```
|
|
39
|
+
**Behind the Scenes:** After the connection has happened and the device type is identified that devices unique protocol class will be assigned to the client. This protocol class comes with its own unique methods. You can find all unique device protocol methods in the [API Reference](API_REFERENCE.md).
|
|
40
|
+
|
|
41
|
+
## Receiving Data
|
|
42
|
+
|
|
43
|
+
This library uses a dedicated thread to listen for unsolicited data transfers from the device. You should register callback functions to handle incoming data updates.
|
|
44
|
+
|
|
45
|
+
> [!NOTE]
|
|
46
|
+
> **Prerequisite:** You must first follow [Connecting to a Device](#connecting-to-a-device) creating a client, connecting, **and** fetching the device protocol.
|
|
47
|
+
|
|
48
|
+
1. **Define Async Callbacks:** Create function to process incoming data object, warning messages, and fatal errors. You will likely have different data processing function for each device type you expect to handle.
|
|
49
|
+
```py
|
|
50
|
+
def my_update_handler(device, header, data):
|
|
51
|
+
print(f"Received message from {device.get_unique_id()} with header: {header}")
|
|
52
|
+
|
|
53
|
+
match header:
|
|
54
|
+
case MeTIOT.Incoming.General.Data:
|
|
55
|
+
# Use the protocol handler to interpret the raw bytes into a dictionary
|
|
56
|
+
deviceProtocol = device.get_protocol_handler()
|
|
57
|
+
telemetry = deviceProtocol.interpret_data(data)
|
|
58
|
+
print(f"Temperature: {telemetry['Temperature_C']} C")
|
|
59
|
+
|
|
60
|
+
case MeTIOT.Incoming.MalformedPacketNotification:
|
|
61
|
+
print("Device reported a communication error.")
|
|
62
|
+
# Since the library is stateless, you must track your last
|
|
63
|
+
# sent packet locally if you wish to re-send it (Recommended).
|
|
64
|
+
|
|
65
|
+
# Example of modular expansion for specific devices
|
|
66
|
+
# case MeTIOT.Incoming.FishTank.SpecificAlert:
|
|
67
|
+
# print("Fish tank alert!")
|
|
68
|
+
|
|
69
|
+
case _:
|
|
70
|
+
# Handle other commands defined in the API Reference
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
def warning_handler(device, msg):
|
|
74
|
+
# This function is optional
|
|
75
|
+
#
|
|
76
|
+
# When this function is called the warning handling is already handled by the library. This function is just for debugging.
|
|
77
|
+
print(f"Device {device.get_unique_id()} has non-fatal warning: {msg}")
|
|
78
|
+
|
|
79
|
+
def error_handler(device, msg):
|
|
80
|
+
# This function is optional
|
|
81
|
+
#
|
|
82
|
+
# When this function is called a fatal error has occured and the listening loop has stopped. The device will no longer listen for messages.
|
|
83
|
+
print(f"Device {device.get_unique_id()} has fatal error: {msg}")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
2. **Register the Handler/s:** Attach the handler/s to the client instance.
|
|
87
|
+
```py
|
|
88
|
+
# --- To assign all devices the same handler ---
|
|
89
|
+
client.assign_handlers(on_data=my_update_handler, on_warning=warning_handler, on_fatal=error_handler)
|
|
90
|
+
# If you do not want to specify an on_warning or on_fatal handler they can both be removed
|
|
91
|
+
# client.assign_handlers(on_data=my_update_handler)
|
|
92
|
+
|
|
93
|
+
# OR
|
|
94
|
+
# --- To assign different devices types unique handlers ---
|
|
95
|
+
#
|
|
96
|
+
# devType = client.get_device_type() # Fetch the current device type
|
|
97
|
+
#
|
|
98
|
+
# if (devType == MeTIOT.DeviceType.FISH_TANK):
|
|
99
|
+
# client.assign_handlers(on_data=fish_tank_update_handler, on_warning=warning_handler, on_fatal=error_handler)
|
|
100
|
+
# elif (devType == MeTIOT.DeviceType.UNKNOWN):
|
|
101
|
+
# print("Unknown device type. Assigning generic handler")
|
|
102
|
+
# client.assign_handlers(on_data=generic_update_handler, on_warning=warning_handler, on_fatal=error_handler)
|
|
103
|
+
```
|
|
104
|
+
**Behind the Scenes:** The client initiates a background listener thread to monitor the socket. When a message is detected, the library automatically handles the decoding and verification (CRC/COBS). It segregated the payload into its `header` and `data` components and dispatches them directly to your handler.
|
|
105
|
+
|
|
106
|
+
## Sending Commands
|
|
107
|
+
|
|
108
|
+
To send a command (like changing a setting), use the client objects protocol handler for high-level command methods.
|
|
109
|
+
|
|
110
|
+
> [!NOTE]
|
|
111
|
+
> **Prerequisite:** You must first follow [Connecting to a Device](#connecting-to-a-device) creating a client, connecting, **and** fetching the device protocol.
|
|
112
|
+
|
|
113
|
+
1. **Create Packet:** Use the device-specific methods on the `protocol` object to generate the complete, ready-to-send packet buffer.
|
|
114
|
+
```py
|
|
115
|
+
# Example for sending a calibration command to a Fish Tank device
|
|
116
|
+
# The protocol method generates the Data Payload + Header + CRC + COBS.
|
|
117
|
+
packet = protocol.create_fish_tank_calibration()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
2. **Send Packet:** Supply that packet buffer to the client's send function.
|
|
121
|
+
```py
|
|
122
|
+
client.send_packet(packet)
|
|
123
|
+
print("Calibration request packet sent.")
|
|
124
|
+
```
|
|
125
|
+
**Behind the Scenes:** The protocol handler wraps your command with mandatory overhead (CRC and COBS encoding), ensuring the device can validate the integrity of the message upon receipt.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# TCP Communication Protocol Specification
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> This documentation details the technical specifications of the protocol used for all TCP communication. **It is not required** to be read or understood for general use of the associated Python library. For usage instructions, please refer to the [How To Use](HowToUse.md) documentation.
|
|
5
|
+
|
|
6
|
+
## Protocol Scope and Usage
|
|
7
|
+
|
|
8
|
+
This protocol governs all TCP communication between MeT IoT devices and external clients (e.g., standalone library, Home Assistant integration).
|
|
9
|
+
|
|
10
|
+
## Connection Initilization Sequence
|
|
11
|
+
|
|
12
|
+
This sequence desribes the steps a MeT IoT device and the client library take to establish and identify a connection.
|
|
13
|
+
|
|
14
|
+
### Initialization Sequence
|
|
15
|
+
|
|
16
|
+
1. **Discovery:** The device broadcasts a Zeroconf (mDNS) packet for initial discovery.
|
|
17
|
+
2. **Connection:** The client library connects to the device's TCP socket.
|
|
18
|
+
3. **Request:** The client library sends a **Device Identification Request** command.
|
|
19
|
+
4. **Response:** The device responds with a **Device Identifier** response packet.
|
|
20
|
+
5. **Operation:** Regular data exchange begins.
|
|
21
|
+
|
|
22
|
+
### Device Identifier Response Packet
|
|
23
|
+
|
|
24
|
+
This specific packet is sent by the IoT device to the library during connection initialization.
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
The Data Payload (within a Data Header `0xFE` packet) contains the following information:
|
|
29
|
+
|
|
30
|
+
* **8-Bit Device ID:** Specifies the type of device (e.g., Fish Tank).
|
|
31
|
+
* **64-Bit Unique Device ID:** A unique identifier for the specific device instance.
|
|
32
|
+
|
|
33
|
+
## General Packet Format
|
|
34
|
+
|
|
35
|
+
All data over the TCP connection adheres to a common structure.
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
### Packet Structure Breakdown
|
|
40
|
+
|
|
41
|
+
| Field | Bytes | Description |
|
|
42
|
+
|-|-|-|
|
|
43
|
+
| COBS Header | 1 | Marks the start of the packet and contains information for COBS decoding. |
|
|
44
|
+
| CRC | 2 | 16-bit Cyclic Redundancy Check for error detection across the entire data payload. |
|
|
45
|
+
| Data Header | 1 | Identifies the overall purpose or type of the packet (e.g., data transfer, identification). |
|
|
46
|
+
| Data Payload | 0-n | The main data content. Its exact structure depends on the Data Header. |
|
|
47
|
+
| COBS Trailer | 1 | Marks the end of the packet, always the value `0x00`. |
|
|
48
|
+
|
|
49
|
+
### Purpose of Protocol Components
|
|
50
|
+
|
|
51
|
+
* **Consistent Overhead Byte Stuffing (COBS):** Ensures reliable packet delimitation by guaranteeing the packet trailer byte (`0x00`) never appears in the body of the packet. The header contains essential information for the receiver on how to decode the data.
|
|
52
|
+
* **Cyclic Redundancy Check (CRC):** A 16-bit code used for robust error detection. If the received CRC does not match the CRC recalculated from the Data Header and Payload, the packet is discarded as corrupted. The CRC algorithm is based on: G.D. Nguyen, “Fast CRCs,” IEEE Transactions on Computers, vol. 58, no. 10, pp. 1321-1331, Oct. 2009.
|
|
53
|
+
|
|
54
|
+
## Data Header Commands and Flow
|
|
55
|
+
|
|
56
|
+
> [!NOTE]
|
|
57
|
+
> All relevant **Bytes** columns in tables **do not include protocol overhead** and only include the length of the header + data. The protocol overhead specifically refers to COBS trailer, COBS deliminator, and 16-Bit CRC which make up for a total of **4 bytes not included in those values**.
|
|
58
|
+
|
|
59
|
+
The Data Header (1 byte) is the first piece of information after the CRC and determines the primary function of the packet.
|
|
60
|
+
|
|
61
|
+
### Standard Library-to-Device Commands
|
|
62
|
+
|
|
63
|
+
These are requests or notifications sent from the client library to the MeT IoT device.
|
|
64
|
+
|
|
65
|
+
| Purpose | Data Header | Data Payload | Bytes |
|
|
66
|
+
|-|-|-|-|
|
|
67
|
+
| Malformed Packet Notification | `0xFF` | (None) | 1 |
|
|
68
|
+
| Device Identification Request | `0xFE` | (None) | 1 |
|
|
69
|
+
|
|
70
|
+
### Standard Device-to-Library Commands
|
|
71
|
+
|
|
72
|
+
These are responses or unsolicited notifications sent from the MeT IoT device to the client library.
|
|
73
|
+
|
|
74
|
+
| Purpose | Data Header | Data Payload | Bytes |
|
|
75
|
+
|-|-|-|-|
|
|
76
|
+
| Malformed Packet Notification | `0xFF` | (None) | 1 |
|
|
77
|
+
| Device Identifier | `0xFE` | [1 byte Device ID + 8 byte Unique Device ID](#device-identifier-response-packet). | 10 |
|
|
78
|
+
| Data Transfer | `0xFD` | [Variable-length structure.](#data-field-format) | n |
|
|
79
|
+
|
|
80
|
+
### Device Identification Codes
|
|
81
|
+
|
|
82
|
+
This 1-byte code is carried in the **Device Identifier** packet (`0xFE`).
|
|
83
|
+
|
|
84
|
+
| Code (1 Byte) | Device Type |
|
|
85
|
+
|-|-|
|
|
86
|
+
| `0xFF` | Fish Tank |
|
|
87
|
+
| ... | *(Future devices)* |
|
|
88
|
+
|
|
89
|
+
## Data Payload Structure (Header 0xFD)
|
|
90
|
+
|
|
91
|
+
When the **Data Header** is `0xFD` (Data Transfer), the **Data Payload** is structured as a series of repeating **Sub-Header** and **Data** fields.
|
|
92
|
+
|
|
93
|
+
### Data Field Format
|
|
94
|
+
|
|
95
|
+

|
|
96
|
+
|
|
97
|
+
The Data Payload consists of one or more data points, each preceded by its own Sub-Header. This pattern repeats until the payload ends: `Sub-Header`, `Data`, `Sub-Header`, `Data`, ...
|
|
98
|
+
|
|
99
|
+
| Field | Purpose |
|
|
100
|
+
|-|-|
|
|
101
|
+
| Data Sub-Header | Identifies the specific data point (e.g., "Current Temp"). |
|
|
102
|
+
| Data | The actual data value, interpreted based on the Sub-Header. |
|
|
103
|
+
|
|
104
|
+
### Supported Data Types
|
|
105
|
+
|
|
106
|
+
> [!IMPORTANT]
|
|
107
|
+
> The following standard C/C++ data types are supported for transmission within the **Data** field:
|
|
108
|
+
>
|
|
109
|
+
> * `int8_t`
|
|
110
|
+
> * `int16_t`
|
|
111
|
+
> * `int32_t`
|
|
112
|
+
> * `int64_t`
|
|
113
|
+
>
|
|
114
|
+
> *All unsigned variants of the above data types are allowed*
|
|
115
|
+
>
|
|
116
|
+
> The following data types *can* be used but research must first be conducted to ensure each devices architecture support the same byte length.
|
|
117
|
+
>
|
|
118
|
+
> * `float`
|
|
119
|
+
> * `double`
|
|
120
|
+
|
|
121
|
+
## Device-Specific Protocol: Fish Tank
|
|
122
|
+
|
|
123
|
+
This section details the custom commands and data formats specific to the Fish Tank device (`Device ID: 0xFF`). The Fish Tank protocol still inherits all commands defined in [Data Header Commands and Flow](#data-header-commands-and-flow).
|
|
124
|
+
|
|
125
|
+
### Fish Tank: Library-to-Device Commands
|
|
126
|
+
|
|
127
|
+
| Purpose | Data Header | Data Payload | Bytes |
|
|
128
|
+
|-|-|-|-|
|
|
129
|
+
| Sensor Attribute Request | `0xFC` | None | 1 |
|
|
130
|
+
| Calibration Values | `0xFB` | *To be decided* | |
|
|
131
|
+
|
|
132
|
+
### Fish Tank: Device-to-Library Commands
|
|
133
|
+
|
|
134
|
+
| Purpose | Data Header | Data Payload | Bytes |
|
|
135
|
+
|-|-|-|-|
|
|
136
|
+
| Sensor Attribute Response | `0xFC` | *To be decided* | |
|
|
137
|
+
| Calibration Finished | `0xFB` | 1-Byte Sensor Address | 2 |
|
|
138
|
+
|
|
139
|
+
### Fish Tank: Data Sub-Header
|
|
140
|
+
|
|
141
|
+
> [!NOTE]
|
|
142
|
+
> Values marked with *(decimal value)* are multiplied by 100 before transmission then divided by 100 when being interpreted. This only gives an accuracy of up to 2 decimal places.
|
|
143
|
+
|
|
144
|
+
| Sub-Header | Data Field | Data Type |
|
|
145
|
+
|-|-|-|
|
|
146
|
+
| `0xFF` | Temperature *(decimal value)* | `int16_t` |
|
|
147
|
+
| `0xFE` | Humidity *(decimal value)* | `uint16_t` |
|
|
148
|
+
| `0xFD` | pH *(decimal value)* | `uint16_t` |
|
|
149
|
+
| `0xFC` | Oxidation-Reduction Potential | `int16_t` |
|
|
150
|
+
| `0xFB` | Dissolved Oxygen | `int16_t` |
|
|
151
|
+
| `0xFA` | Conductivity | `int16_t` |
|
|
152
|
+
| `0x00` | Data End | (None/Blank) |
|
|
153
|
+
|
|
154
|
+
### Fish Tank: Sensor Attribute Aquisition
|
|
155
|
+
|
|
156
|
+
During the devices startup it allocates a unique I2C address and stores the device type for all devices on the I2C bus. For later use (like calibration) we will need to store this information so we know what types of devices are currently associated with the device.
|
|
157
|
+
|
|
158
|
+
#### Sensor Attribute Response Command
|
|
159
|
+
|
|
160
|
+
I2C Address:Device Type pair can repeat as many times as required.
|
|
161
|
+
|
|
162
|
+

|
|
163
|
+
|
|
164
|
+
#### Sensor Device Types
|
|
165
|
+
|
|
166
|
+
> [!NOTE]
|
|
167
|
+
> A Fish Tank device can have multiple of the same type of device so its important to sort by their I2C address not.
|
|
168
|
+
|
|
169
|
+
| Code | Device Type |
|
|
170
|
+
|-|-|
|
|
171
|
+
| `0xFF` | Temeprature Sensor |
|
|
172
|
+
| `0xFE` | Humidity Sensor |
|
|
173
|
+
| `0xFD` | pH Sensor |
|
|
174
|
+
| `0xFC` | Oxidation-Reduction Potential Sensor |
|
|
175
|
+
| `0xFB` | Dissolved Oxygen Sensor |
|
|
176
|
+
| `0xFA` | Conductivity Sensor |
|
|
177
|
+
|
|
178
|
+
### Fish Tank: Calibration
|
|
179
|
+
|
|
180
|
+
#### Calibration Values Command
|
|
181
|
+
|
|
182
|
+
#### Calibration Finished Command
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["scikit-build-core", "pybind11"]
|
|
3
|
+
build-backend = "scikit_build_core.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "MeTIOT"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Cody Jackson", email="cjackson@micr.com.au" }
|
|
10
|
+
]
|
|
11
|
+
description = "Communication handling for all MeT IOT devices"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
|
|
15
|
+
[tool.scikit-build]
|
|
16
|
+
cmake.version = ">=3.15"
|