github.com/simpleiot/simpleiot@v0.18.3/docs/ref/serial.md (about) 1 # Serial Devices 2 3 **Contents** 4 5 <!-- toc --> 6 7 (see also [user documentation](../user/mcu.md) and 8 [SIOT Firmware](https://github.com/simpleiot/firmware/tree/master/Arduino)) 9 10 It is common in embedded systems architectures for a MPU (Linux-based running 11 SIOT) to be connected via a serial link (RS232, RS485, CAN, USB serial) to an 12 MCU. 13 14  15 16 See 17 [this article](http://bec-systems.com/site/1540/microcontroller-mcu-or-microprocessor-mpu) 18 for a discussion on the differences between a MPU and MCU. These devices are not 19 connected via a network interface, so can't use the [SIOT NATS API](api.md#nats) 20 directly, thus we need to define a proxy between the serial interface and NATS 21 for the MCU to interact with the SIOT system. 22 23 State/config data in both the MCU and MPU systems are represented as nodes and 24 points. An example of nodes and points is shown below. These can be arranged in 25 any structure that makes sense and is convenient. Simple devices may only have a 26 single node with a handful of points. 27 28  29 30 SIOT does not differentiate between state (ex: sensor values) and config (ex: 31 pump turn-on delay) -- it is all points. This simplifies the transport and 32 allows changes to be made in multiple places. It also allows for the granular 33 transmission and synchronization of data -- we don't need to send the entire 34 state/config anytime something changes. 35 36 SIOT has the ability to log points to InfluxDB, so this mechanism can also be 37 used to log messages, events, state changes, whatever -- simply use an existing 38 point type or define a new one, and send it upstream. 39 40 ## Data Synchronization 41 42 By default, the serial client synchronizes any extra points written to the 43 serial node. The serial UI displays the extra points as shown below: 44 45 <img src="./assets/image-20231031115204490.png" alt="image-20231031115204490" style="zoom:50%;" /> 46 47 Alternatively, there is an option for the serial client to sync its parent's 48 points to the serial device. When this is selected, any points received from the 49 serial device are posted to the parent node, and any points posted to the parent 50 node that were not sent by the serial device are forwarded to the serial client. 51 52 ## Protocol 53 54 The SIOT serial protocol mirrors the NATS 55 [PUB message](https://docs.nats.io/reference/reference-protocols/nats-protocol#pub) 56 with a few assumptions: 57 58 - we don't have mirrored nodes inside the MCU device 59 - the number of nodes and points in a MCU is relatively small 60 - the payload is always an array of points 61 - only the following [SIOT NATS API](api.md#nats) subjects are supported: 62 - blank (assumes ID of Serial MCU client node 63 - `p.<id>` (used to send node points) 64 - `p.<id>.<parent>` (used to send edge points) 65 - `phr` (specifies high-rate payload) 66 - we don't support NATS subscriptions or requests -- on startup, we send the 67 entire dataset for the MCU device in both directions (see On connection 68 section), merge the contents, and then assume any changes will get sent and 69 received after that. 70 71 `subject` can be left blank when sending/receiving points for the MCU root node. 72 This saves some data in the serial messages. 73 74 The point type `nodeType` is used to create new nodes and to send the node type 75 on connection. 76 77 All packets are ack'd (in both directions) by an empty packet with the same 78 sequence number and subject set to 'ack'. If an ack is not received in X amount 79 of time, the packet is retried up to 3 times, and then the other device is 80 considered "offline". 81 82 ### Encoding 83 84 #### Packet Frame 85 86 All packets between the SIOT and serial MCU systems are framed as follows: 87 88 ``` 89 sequence (1 byte, rolls over) 90 subject (16 bytes) 91 payload (Protobuf Point array or HR repeated point payload) 92 crc (2 bytes) (Currently using CRC-16/KERMIT) (not included on log messages) 93 ``` 94 95 Protocols like RS232 and USB serial do not have any inherent framing; therefore, 96 this needs to be done at the application level. SIOT encodes each packet using 97 [COBS (Consistent Overhead Byte Stuffing)](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing). 98 99 #### Log payload 100 101 The log message is specified with `log` in the packet frame subject. The payload 102 is ASCII characters and CRC not included. 103 104 #### Protobuf payload 105 106 The `serial` protobuf type is used to transfer these messages: 107 108 ``` 109 message SerialPoint { 110 string type = 2; 111 float value = 4; 112 int64 time = 16; 113 float index = 13; 114 string text = 8; 115 string key = 11; 116 int32 tombstone = 12; 117 } 118 ``` 119 120 Protobuf can be used for low-rate samples, config, state, etc. 121 122 Protobuf is used to encode the data on the wire. Find protobuf files 123 [here](https://github.com/simpleiot/simpleiot/tree/master/internal/pb). 124 [nanopb](https://github.com/nanopb/nanopb) can be used to generate C-based 125 protobuf bindings that are suitable for use in most MCU environments. 126 127 #### High-rate payload 128 129 A simple payload encoding for high-rate data can be used to avoid the overhead 130 of protobuf encoding and is specified with `phr` in the packet frame subject. 131 132 ``` 133 type (16 bytes) point type 134 key (16 bytes) point key 135 starttime (uint64) starting time of samples in ns since Unix Epoch 136 sampleperiod (uint32) time between samples in ns 137 data (variable, remainder of packet), packed 32-bit floating point samples 138 ``` 139 140 This data bypasses most of the processing in SIOT and is sent to a special 141 [`phr` NATS subject](api.md). Clients that are interested in high-rate data 142 (like the InfluxDB client) can listen to these subjects. 143 144 #### File payload 145 146 This payload type is for transferring files in blocks. These files may be used 147 for firmware updates or other transfers where large amounts of data need to be 148 transferred. An empty block with index set to -1 is sent at the end of the 149 transfer. 150 151 ``` 152 name (16 bytes) filename 153 index (4 bytes) file block index 154 data (variable, remainder of packet) 155 ``` 156 157 ### On connection 158 159 On initial connection between a serial device and SIOT, the following steps are 160 done: 161 162 - the MCU sends the SIOT system an empty packet with its root node ID 163 - the SIOT systems sends the current time to the MCU (point type `currentTime`) 164 - the MCU updates any "offline" points with the current time (see offline 165 section). 166 - the SIOT acks the current time packet. 167 - all of the node and edge points are sent from the SIOT system to the MCU, and 168 from the MCU to the SIOT system. Each system compares point time stamps and 169 updates any points that are newer. Relationships between nodes are defined by 170 edge points (point type `tombstone`). 171 172 ### Timestamps 173 174 The Simple IoT uses a 64-bit ns since Unit epoch value for all timestamps. 175 176 ### Fault handling 177 178 Any communication medium has the potential to be disrupted (unplugged/damaged 179 wires, one side off, etc). Devices should continue to operate and when 180 re-connected, do the right thing. 181 182 If a MCU has a valid time (RTC, sync from SIOT, etc), it will continue 183 operating, and when reconnected, it will send all its points to re-sync. 184 185 If a MCU powers up and has no time, it will set the time to 1970 and start 186 operating. When it receives a valid time from the SIOT system, it will compute 187 the time offset from the SIOT time and its own 1970 based time, index through 188 all points, add the offset to any points with time less than 2020, and then send 189 all points to SIOT. 190 191 When the MCU syncs time with SIOT, if the MCU time is ahead of the SIOT system, 192 then it set its time, and look for any points with a time after present, and 193 reset these timestamps to the present. 194 195 ## RS485 196 197 Status: Idea 198 199 RS485 is a half duplex, prompt response transport. SIOT periodically prompts MCU 200 devices for new data at some configurable rate. Data is still COBS encoded so 201 that is simple to tell where packets start/stop without needing to rely on dead 202 space on the line. 203 204 Simple IoT also supports modbus, but the native SIOT protocol is more capable -- 205 especially for structured data. 206 207 Addressing: TODO 208 209 ## CAN 210 211 Status: Idea 212 213 CAN messages are limited to 8 bytes. The J1939 Transport Protocol can be used to 214 assemble multiple messages into a larger packet for transferring up to 1785 215 bytes. 216 217 ## Implementation notes 218 219 Both the SIOT and MCU side need to store the common set of nodes and points 220 between the systems. This is critical as the point merge algorithm only uses an 221 incoming point if the incoming point is newer than the one currently stored on 222 the device. For SIOT NATS clients, we use the NodeEdge data structure: 223 224 ```go 225 type NodeEdge struct { 226 ID string 227 Type string 228 Parent string 229 Points Points 230 EdgePoints Points 231 Origin string 232 } 233 ``` 234 235 Something similar could be done on the MCU. 236 237 If new nodes are created on the MCU, the ID must be a UUID, so that it does not 238 conflict with any of the node IDs in the upstream SIOT system(s). 239 240 On the SIOT side, we keep a list of Nodes on the MCU and periodically check if 241 any new Nodes have been created. If so, we send the new Nodes to the MCU. 242 Subscriptions are set up for points and edges of all nodes, and any new points 243 are sent to the MCU. Any points received from the MCU simply forwarded to the 244 SIOT NATS bus. 245 246 ## DFU 247 248 Status: Idea 249 250 For devices that support USB Device Firmware Upgrade (DFU), SIOT provides a 251 mechanism to do these updates. A node that specifies USB ID and file configures 252 the process. 253 254 - [DFU Specification](https://www.usb.org/sites/default/files/DFU_1.1.pdf) 255 - [Windows Implementation](https://docs.microsoft.com/en-us/windows-hardware/drivers/stream/device-firmware-update-for-usb-devices-without-using-a-co-installer)