|
| 1 | +--- |
| 2 | +title: MaixCAM MaixPy Maix Application Communication Protocol |
| 3 | +--- |
| 4 | + |
| 5 | + |
| 6 | +## Communication Protocol Overview |
| 7 | + |
| 8 | +In order for two devices to communicate stably, there are typically several layers, starting from the bottom: |
| 9 | + |
| 10 | +* **Hardware Layer**: For example, `UART` uses three wires: `TX`, `RX`, and `GND`. It can also be wireless, such as with WiFi. |
| 11 | +* **Transport Layer**: Uses transmission control protocols to ensure stable data transmission, such as the `UART` protocol, which specifies baud rate, stop bits, and parity bits to ensure correct data transmission. The `TCP` protocol works similarly. |
| 12 | +* **Application Layer**: The data obtained from the transport layer is stream data (simply put, a long string of data without punctuation). To help the application understand what the data means, the application typically defines its own communication protocol for the application layer to structure the transmitted content (you can think of this as adding punctuation to the stream of data to make it easier for the receiver to understand the sentence structure). |
| 13 | + |
| 14 | +For example: |
| 15 | +The application layer protocol specifies that a data packet begins with a `$` symbol. |
| 16 | +A sends two data packets to B: `$12345$67890`. After receiving it, B knows that A has sent two packets, `12345` and `67890`. Without this protocol, if A sends `12345` followed by `67890`, since the data is transmitted as a stream, B might receive `1234567890`, making it unclear whether one or two packets were sent. |
| 17 | + |
| 18 | +## Character Protocol vs. Binary Protocol |
| 19 | + |
| 20 | +**Character Protocol**: |
| 21 | +In the previous example, we used `$` to mark the beginning of each packet. If we want to send the number `123`, we simply send the string `$123`, which is human-readable. The receiver needs to convert the string `123` to an `int` type. For example, in C language: |
| 22 | +```c |
| 23 | +int value; |
| 24 | +sscanf(buff, "$%d", &value); |
| 25 | +``` |
| 26 | +
|
| 27 | +**Binary Protocol**: |
| 28 | +In a character protocol, sending the number `123` takes 4 bytes, and the receiver has to parse the string to convert it into an integer type. In a binary protocol, we can reduce the number of bytes transmitted and make it easier for the receiver to handle. We can simply send `0x24 0x7B`. `0x24` is the hexadecimal representation of the `$` symbol (refer to the ASCII table), and `0x7B` is the hexadecimal representation of the decimal number `123`. This uses only two bytes to transmit the same information that required 4 bytes in the character protocol. The receiver can directly read the second byte `0x7B` and use its value, such as in C language: |
| 29 | +```c |
| 30 | +uint8_t value = buff[1]; |
| 31 | +``` |
| 32 | + |
| 33 | +This is just a simple explanation to help you understand the two protocols. In practice, each has its advantages depending on the specific use case, and other factors, such as checksum values, may also be considered. You can explore more and learn about it in the Maix Communication Protocol Practice section below. |
| 34 | + |
| 35 | +## Maix Application Communication Protocol |
| 36 | + |
| 37 | +The Maix Application Communication Protocol is an application layer protocol that uses UART or TCP as the transport layer. |
| 38 | + |
| 39 | +It defines how the two parties communicate and the format in which data is transmitted, making it easier to parse and identify information. It is a binary protocol that includes frame headers, data content, checksums, etc. |
| 40 | + |
| 41 | +The complete protocol definition is available in the [Maix Application Communication Protocol Standard](https://wiki.sipeed.com/maixcdk/doc/convention/protocol.html) (included in the MaixCDK documentation because MaixCDK also uses this protocol). |
| 42 | + |
| 43 | +If you have no prior experience with communication protocols, it might seem a bit difficult, but by reviewing the examples below a few times, you should be able to understand it. In `MaixPy`, the API is already encapsulated, making it very simple to use. For other microcontrollers or chips, you may need to implement this protocol yourself, and you can refer to the appendix of the [Maix Application Communication Protocol Standard](https://wiki.sipeed.com/maixcdk/doc/convention/protocol.html) to check for any corresponding implementations. |
| 44 | + |
| 45 | +For example, if we are performing object detection and want to send the detected object information (such as type and coordinates) via UART to another device (e.g., STM32 or Arduino microcontroller), here’s how it can be done. |
| 46 | + |
| 47 | +Full Example: [MaixPy/examples/protocol/comm_protocol_yolov5.py](https://github.com/sipeed/MaixPy/tree/main/examples/protocol/comm_protocol_yolov5.py) |
| 48 | + |
| 49 | +First, we need to detect the objects, based on the `yolov5` detection example. Here, we’ll skip other details and focus on how the detection results look: |
| 50 | +```python |
| 51 | +while not app.need_exit(): |
| 52 | + img = cam.read() |
| 53 | + objs = detector.detect(img, conf_th = 0.5, iou_th = 0.45) |
| 54 | + for obj in objs: |
| 55 | + img.draw_rect(obj.x, obj.y, obj.w, obj.h, color = image.COLOR_RED) |
| 56 | + msg = f'{detector.labels[obj.class_id]}: {obj.score:.2f}' |
| 57 | + img.draw_string(obj.x, obj.y, msg, color = image.COLOR_RED) |
| 58 | + dis.show(img) |
| 59 | +``` |
| 60 | +You can see that `objs` is a list of detection results. Here, we draw bounding boxes on the screen, but we can also send these results via UART. |
| 61 | + |
| 62 | +We don't need to manually initialize the UART; we can directly use the built-in `maix.comm, maix.protocol` modules. By calling `comm.CommProtocol`, the UART is automatically initialized, with a default baud rate of `115200`. Communication protocol settings can be configured in the device's `System Settings -> Communication Protocol`. There might also be other communication methods, such as `TCP`, with the default set to `UART`. You can check the current setting by using `maix.app.get_sys_config_kv("comm", "method")`. |
| 63 | + |
| 64 | +```python |
| 65 | +from maix import comm, protocol, app |
| 66 | +from maix.err import Err |
| 67 | +import struct |
| 68 | + |
| 69 | +def encode_objs(objs): |
| 70 | + ''' |
| 71 | + Encode objects info to bytes body for protocol |
| 72 | + 2B x(LE) + 2B y(LE) + 2B w(LE) + 2B h(LE) + 2B idx ... |
| 73 | + ''' |
| 74 | + body = b"" |
| 75 | + for obj in objs: |
| 76 | + body += struct.pack("<hhHHH", obj.x, obj.y, obj.w, obj.h, obj.class_id) |
| 77 | + return body |
| 78 | + |
| 79 | +APP_CMD_ECHO = 0x01 # Custom command 1, for testing, not used here, just reserved |
| 80 | +APP_CMD_DETECT_RES = 0x02 # Custom command 2, send detected object information |
| 81 | + # You can define more custom commands for your application |
| 82 | + |
| 83 | +p = comm.CommProtocol(buff_size = 1024) |
| 84 | + |
| 85 | +while not app.need_exit(): |
| 86 | + # ... |
| 87 | + objs = detector.detect(img, conf_th = 0.5, iou_th = 0.45) |
| 88 | + if len(objs) > 0: |
| 89 | + body = encode_objs(objs) |
| 90 | + p.report(APP_CMD_DETECT_RES, body) |
| 91 | + # ... |
| 92 | +``` |
| 93 | + |
| 94 | +In the `encode_objs` function, we pack the detected object information into a `bytes` type data. Then, using the `p.report` function, we send the result. |
| 95 | + |
| 96 | +The `body` content is simply defined as `2B x(LE) + 2B y(LE) + 2B w(LE) + 2B h(LE) + 2B idx ...`, where: |
| 97 | + |
| 98 | +* For each object, we send 10 bytes (2 bytes for each of the `x`, `y`, `w`, `h`, and `class_id` coordinates). The `x` coordinate is represented as a short integer, encoded in little-endian (LE) format. |
| 99 | +* The loop encodes all objects and concatenates them into one byte array (`bytes`). |
| 100 | + |
| 101 | +When calling the `report` function, the protocol header, checksum, and other details are automatically added at the lower level, resulting in a complete data frame ready to be received. |
| 102 | + |
| 103 | +On the receiving end, you can decode the data according to the protocol. If the receiver is also using MaixPy, you can directly use: |
| 104 | +```python |
| 105 | +while not app.need_exit(): |
| 106 | + msg = p.get_msg() |
| 107 | + if msg and msg.is_report and msg.cmd == APP_CMD_DETECT_RES: |
| 108 | + print("Received objects:", decode_objs(msg.get_body())) |
| 109 | + p.resp_ok(msg.cmd, b'1') |
| 110 | +``` |
| 111 | + |
| 112 | +If the device is something like an STM32 or Arduino, you can refer to the C language functions in the appendix of the [Maix Application Communication Protocol Standard](https://wiki.sipeed.com/maixcdk/doc/convention/protocol.html) for encoding and decoding. |
0 commit comments