CDNET Intro and Demo - dukelec/cdnet GitHub Wiki
Different devices - such as stepper motors, brushless motors, and vibratory bowl controllers - may use different protocols depending on their application. However, the underlying message format remains consistent.
Two basic protocols recommended for all device types are: Device Info Query
and Register Read/Write
.
This document focuses on these two functions to introduce the details of the CDNET protocol.
Only Level 0 of the protocol is demonstrated here. Level 0 is sufficient for most common use cases.
The CDNET protocol is an upper-layer protocol built on CDBUS. CDNET packets are carried within CDBUS data frames.
For example, here is a full command to query the device's info string:
The first 3 bytes (
-
$\texttt{\color{#58d68d}{00}}$ = host address (default) -
$\texttt{\color{#58d68d}{fe}}$ = device address (default, configurable; broadcast address is ff) -
$\texttt{\color{#58d68d}{02}}$ = data length -
$\texttt{\color{#58d68d}{44 28}}$ = CRC checksum (MODBUS CRC)
The CDNET L0 packet is
-
$\texttt{\color{#5dade2}{40}}$ = source port (for CDNET Level 0, bit 7 is always 0) -
$\texttt{\color{#5dade2}{01}}$ = destination port (command ID) (bit 7 is reserved as 0)
This command has no user data. It simply asks port 01 (Info Query command) for a response.
The source port 0x40 can be any value and is often treated as a command sequence ID. Using values ≥ 0x40 is recommended.
The device replies with:
Decoded, this is the ASCII string:
M: cdstep; S: 6f0089000350523250333420; SW: v5.1-15-gb706258
The string format is defined by the manufacturer.
This example uses the following definition:
- M = model
- S = serial number
- SW = software version (git-generated: 15 commits after tag v5.1, commit hash b706258)
The hexadecimal representation of the string is:
In bootloader mode, (bl)
is added to the model name for the host to recognize the mode.
Note: the source and destination addresses (CDBUS) and ports (CDNET) are swapped in the reply.
This Info Query command is optional - users may also place version or ID info into registers (explained in the next section). However, using a string is more flexible and strongly recommended.
Similar to protocols like MODBUS, CDNET also supports a register map for configuration, control, and device status queries.
In CDNET, register addresses are actual memory offsets. For example, a uint32_t variable at address 0x0124 occupies 0x0124 to 0x0127.
The port number (or command ID) for register access is 5. The CDNET user payload format is:
read: 0x00, offset_16, len_8 | return [0x00, data]
read_dft: 0x01, offset_16, len_8 | return [0x00, data]
write: 0x20, offset_16 + [data] | return [0x00] on success
There are three subcommands:
- read – read current value
- read_dft – read default value
- write – write value (optional: if bit7 of write is set, no response is returned)
Example: Read a uint32_t at 0x0124 (multiple registers can also be accessed at once):
Breakdown:
-
$\texttt{\color{#5dade2}{40}}$ = source port (or command sequence ID) -
$\texttt{\color{#5dade2}{05}}$ = destination port (main command) -
$\texttt{\color{#af7ac5}{00}}$ = subcommand: read -
$\texttt{\color{#af7ac5}{24 01}}$ = address: 0x0124 -
$\texttt{\color{#af7ac5}{04}}$ = read 4 bytes -
$\texttt{\color{#58d68d}{60 a0}}$ = CRC
Response:
-
$\texttt{\color{#af7ac5}{00}}$ = no error (The high bits typically signal global errors like over-temperature, while the low 4 bits reflect command-specific errors) -
$\texttt{\color{#af7ac5}{04 03 02 01}}$ = value: 0x01020304
Note: CDBUS source/destination addresses and CDNET ports are swapped in the reply.
Example: Write 0x01020304 to 0x0124:
-
$\texttt{\color{#af7ac5}{20}}$ = subcommand: write - The data to write follows directly after register address 0x0124, with as many bytes as needed.
Response:
-
$\texttt{\color{#af7ac5}{00}}$ success, no error - No return data
Each device may define different registers, but common ones typically include settings like device address, baud rate, reset, and config save.
You can check specific device documentation or view registers using the GUI tool. For example, here's the register map of a stepper motor:
To rotate a stepper motor:
- Enable motor (write 1 to the state register):
- Write target position to tc_pos (trapezoidal motion profile), e.g., 0x01020304:
For applications like motor control where parameters such as target position, speed, and acceleration need to be updated frequently, it's inefficient to include the register address in every command. Instead, we can predefine the target registers for fast access, allowing we to send only the data (e.g., position or speed).
This feature is optional and not required for applications that don’t need high-speed data exchange.
The target registers for fast read/write can be hardcoded or configured in advance using standard register access (see previous section). For example, the qxchg settings (short for quick exchange) allow configuration of multiple non-contiguous registers for simultaneous read/write.
The port number (or command ID) for fast register read/write is 6. The CDNET user data format is as follows:
write: [data_w] | return [data_r]
write_multi: [data_w0, data_w1, data_w2 ...] | return [data_r]
- write: Sends data to the predefined write registers. Returns predefined read data (e.g., error flags, encoder position, bus voltage). If the command fails, an empty response is returned.
- write_multi: Used for multi-axis writes. Each device extracts its portion of the data based on predefined offset and length. The behavior is the same as write.
Optional behaviors:
- If bit 4 of the original port number is 0, the packet is treated as a write; if 1, it's a write_multi.
- If bit 3 is 1, no response is sent.
The port number (or command ID) for flash operations is 8. The CDNET user data format is as follows:
erase: 0x2f, addr_32, len_32 | return [0x00] on success
write: 0x20, addr_32 + [data] | return [0x00] on success
read: 0x00, addr_32, len_8 | return [0x00, data]
cal crc: 0x10, addr_32, len_32 | return [0x00, crc_16] # modbus crc
There are four subcommands: erase, write, read, and CRC check. (Optional: if bit7 of the subcommand is set, no response will be sent.)
The CRC check is optional — the same result can be achieved by reading back all data and comparing it, though this is less efficient.
The address used in these operations is the actual flash address. This command can be used in the bootloader to update the application firmware, or in the application to update the bootloader.
In addition to firmware updates, this command can also be used to read or write configuration data stored in flash.
By default, flash data accessed through this command is not encrypted. If encryption is required, the protocol can remain unchanged or extended with encrypted variants of the subcommands.
Once the device is enclosed, it may no longer be convenient to use a physical UART port or JTAG for debugging. In such cases, debug logs can be sent over the user serial interface. The user application should either tolerate or disable such output.
(Alternatively, a filter can be added between the host MCU and the target device to route debug messages to a separate debugging tool.)
The debug print target port number is 9. Example data packet:
Port 9 does not send any response.
For example, a debug message like:
"D: adc cali: ab 1665 1568, ca 1608 1664, cb 1608 1568"
would be transmitted as the following hex data:
In addition to serial printing, waveform visualization is often needed to debug motor control loops (e.g., current, speed, and position loops).
The target port number for waveform reporting is 0x0A. The CDNET user data format is:
report type 0: x, d1,d2,d3, d1,d2,d3 ...
report type 1: x,d1,d2,d3, x,d1,d2,d3 ...
Port 0x0A does not have a response. The lower 4 bits of the port number specify the waveform index. For example, index 0 might represent current loop data, and index 1 might represent speed loop data, corresponding to different waveform windows on the host side.
x is the timestamp for the waveform, used as the X-axis on the plot. For example, with a 20kHz current loop, x increments by 1 each loop cycle.
- Type 0 reports are used for fixed-interval data. The first x value corresponds to the first data group, and subsequent x values are inferred using a constant step.
- Type 1 reports are used for variable-interval data. Each group of data includes its own x value.
The report type and the structure of each data group are configured on the host side; the command itself carries no such metadata. The reported data can be hardcoded in MCU firmware or configured via register read/write.
Below is an example of two consecutive type-0 report packets:
Here, index = 0. x is a uint32_t. Each group of data has an x increment of 2. Each group includes:
- One int32_t value (e.g., position)
- One uint8_t value (e.g., voltage)
The two packets include 6 groups of data total. A partial table:
x (timestamp) | Position | Voltage |
---|---|---|
0x00000002 | 0x01020304 | 0x24 |
0x00000004 | 0x01020305 | 0x22 |
0x00000006 | 0x01020306 | 0x21 |
0x00000008 | 0x01020307 | 0x20 |
0x0000000a | 0x01020309 | 0x1f |
0x0000000c | 0x0102030a | 0x1e |
Waveform configuration in the device config file would be:
"I2.iB - N, position, voltage"
Explanation:
- I → x is a uint32_t
- 2 → report type 0 with interval 2
- iB → each group includes an int32_t followed by a uint8_t
- N, position, voltage → names of the X-axis and data values
Refer to the Python struct module documentation for format character details.
The target port number for image reporting is 0x10. The CDNET user data format is:
report: [data]
Port format:
Bits [5:4]: Fragment type:
- 00: reserved
- 01: first packet
- 10: middle packet
- 11: last packet
Bits [3:0]: packet index (starting from 0, incrementing by 1)
The host reconstructs a full JPEG image by concatenating data from one first packet, zero or more middle packets, and one last packet.
Writing 1 to the capture register returns one JPEG image. Writing 255 triggers continuous image streaming; writing 0 again stops the stream.
- CDNET Protocol:https://github.com/dukelec/cdnet
- CDBUS Protocol:https://cdbus.org