Skip to content

CDNET Intro and Demo

dukelec edited this page Apr 3, 2026 · 67 revisions

Different devices - such as stepper motors, brushless motors, and serial cameras - 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.

Only Level 0 of the protocol is demonstrated here. Level 0 is sufficient for most common use cases.

Devices using CDNET typically use a fixed communication rate of 115200 bps for a short period after power-on, and then switch to the user-configured rate. If the configured rate is forgotten, connection can be restored during this initial period by using 115200 bps and broadcast address.

Protocol Format

This protocol uses little-endian mode with 8N1 UART configuration. The protocol format is as follows:

_cdnet_l0

Address Description:

SRC_MAC is the sender's address, and DST_MAC is the receiver's address.

All addresses except the broadcast address ff are normal unicast addresses. (If multicast is used, any normal address may be assigned as a multicast address.)

In all examples in this document, the host address defaults to 00, and the device address defaults to fe.

In responses, the source and destination addresses are swapped. For broadcast or multicast packets, the response source address is the responder’s unicast address.

Length Description:

LEN indicates the length of the following data (2 ~ 253 bytes), excluding the trailing CRC.

Port Description:

SRC_PORT is the source port, and DST_PORT is the destination port.

The MSB of both the source port and destination port is fixed to 0 (corresponding to CDNET L0 simplified communication).

For commands:

  • The source port is a temporary port (default ≥ 0x40) and may change for each transmission.
    (To reduce protocol overhead, some function ports select different behaviors based on certain bits of the temporary port; see the description of each function port for details.)
  • The destination port is a function port (default < 0x40). See the definitions of the function port numbers.

In responses, the source and destination ports are swapped.

When a slave reports an event, it is equivalent to the slave sending a command to the master. The master usually does not respond.

Port Data:

PAYLOAD is the port data, with a length of 0 ~ 251 bytes (LEN - 2).

CRC Checksum:

The checksum uses the Modbus CRC algorithm. The header is included in the calculation.

Info String Query #01

Basic String Query Function: Write an empty packet to function port 01. The data returned by this port is the string to be queried.

Example of a complete command and response (M = Master, S = Slave):

_p1

  • $\color{#58d68d}{①}$:Source address
  • $\color{#58d68d}{②}$:Destination address
  • $\color{#58d68d}{③}$:Length
  • $\color{#5dade2}{④}$:Source port
  • $\color{#5dade2}{⑤}$:Destination port
  • $\color{#58d68d}{⑥}$:CRC
  • $\color{#af7ac5}{⑦}$:Port data

In the response, the port data is a string. For example: "M: cdstep; S: 6f0089000350523250333420; SW: v5.1", whose hexadecimal representation is $\texttt{\color{#af7ac5}{4d 3a 20 63 64 ... 2e 31}}$.
(The string content is vendor-defined, e.g.: M indicates the model, S the serial number, and SW the software version. In bootloader mode, (bl) is appended after cdstep to allow the host to identify the current mode.)

Implementation of the Information String Query command in this section is optional (but recommended). Alternatively, version and related information may be provided in the register list described in the next section for host queries.

Register Read & Write #05

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 for register access is 05. The CDNET payload format is:

read:     [0x00, offset_16, len_8] | return: [ret_8, data]
read_dft: [0x01, offset_16, len_8] | return: [ret_8, data]
write:    [0x20, offset_16, data]  | return: [ret_8]
write_nr: [0xa0, offset_16, data]  | no return

There are four subcommands:

  • read – read current value
  • read_dft – read default value
  • write – write value
  • write_nr – write without response

Status code ret_8:

  • Bit 7: Set if any global error is present
  • Bit 6: Set if any global warning is present
  • Bit [5:4]: Reserved, always 0
  • Bit [3:0]: Execution status of the current command:
    • 0: No error
    • 1: Read/write error

Read Example:

_p5_read

  • $\color{#5dade2}{①}$:Destination port is function port 05
  • $\color{#af7ac5}{②}$:Subcommand read
  • $\color{#af7ac5}{③}$:Target register address 0x0124
  • $\color{#af7ac5}{④}$:Read length: 4 bytes (multiple registers can also be accessed at once)
  • $\color{#af7ac5}{⑤}$:0 indicates no error
  • $\color{#af7ac5}{⑥}$:Returned register data is 0x01020304

Write Example:

_p5_write

  • $\color{#af7ac5}{①}$:Subcommand write
  • $\color{#af7ac5}{②}$:Target register address 0x0124
  • $\color{#af7ac5}{③}$:Write variable-length data; here the data is 0x01020304
  • $\color{#af7ac5}{④}$:0 indicates no error occurred

Register Map Example:

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:

_p5_gui

To rotate a stepper motor:

  1. Enable motor (write 1 to the state register):

_p5_eg1

  1. Write target position to tp_pos (tp: trapezoidal planner), e.g., 0x01020304:

_p5_eg2

Fast Register Read/Write #06

For applications such as motor control, where parameters like target position, speed, and acceleration need to be updated frequently, including the register address in every command is inefficient. Instead, target registers can be predefined for fast access, allowing only the data (e.g., position or speed) to be sent.

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 via register read/write (e.g., the qxchg register, where qxchg stands for quick exchange).

The port number for fast register read/write is 06. The CDNET payload 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, then processes it according to the same rules as write. The returned data follows the same definition as write.

Source port number:

  • Bit 4: command type (0 = write, 1 = write_multi)
  • Bit 3: no response if set to 1

Single-axis position and speed write example:

_p6_write

  • $\color{#5dade2}{①}$:Destination port is function port 06
  • $\color{#af7ac5}{②}$:Written target position 0x00020000
  • $\color{#af7ac5}{③}$:Written target speed 0x00010000
  • $\color{#af7ac5}{④}$:Returned motor error flags (this field is also optional and configurable)
  • $\color{#af7ac5}{⑤}$:Actual position 0x00020042
  • $\color{#af7ac5}{⑥}$:Actual speed 0x00010087

Multi-axis position and speed write example:

_p6_write_multi

  • $\color{#58d68d}{①}$:Destination address is a broadcast or multicast address (broadcast address is fixed as 0xff; any other address may be used as a multicast address)
  • $\color{#5dade2}{②}$:When bit 4 of the source port is set, the packet is a write_multi command containing multi-axis data
  • $\color{#af7ac5}{③}$:Target position and target speed of the first axis
  • $\color{#af7ac5}{④}$:Target position and target speed of the second axis
  • $\color{#af7ac5}{⑤⑥⑦}$:The response format is the same as the single-axis packet (all responses use unicast addresses; in this example, the two axes have addresses 0x01 and 0x02, respectively)

IAP Firmware Upgrade #08

The port number for flash operations is 08. The CDNET payload format is as follows:

erase:    [0x2f, addr_32, len_32] | return: [ret_8]
write:    [0x20, addr_32, data]   | return: [ret_8]
write_nr: [0xa0, addr_32, data]   | no return
read:     [0x00, addr_32, len_8]  | return: [ret_8, data]
cal_crc:  [0x10, addr_32, len_32] | return: [ret_8, crc_16] # modbus crc

There are five subcommands: erase, write, write without response, read, and CRC check.

The CRC check is optional. Verification can also be done by reading back all data for comparison, though this is slower.

The address used in these operations is the actual flash address. In addition to firmware updates, this command can also be used to read or write configuration data stored in flash.

The flash data involved in this command can be either plaintext or encrypted.

Status code ret_8:

  • Bit [7:4]: Reserved, always 0
  • Bit [3:0]: Execution status of the current command:
    • 0: No error
    • 1: Erase/write error

Typically, both the bootloader and the app firmware support this protocol, allowing the app to be upgraded from the bootloader, or the bootloader to be upgraded from the app.
The bootloader can be entered by writing 1 to the reboot register (2 enters the app). Alternatively, right after power-up, write 1 to the keep_bl register to keep the device in bootloader mode.
The current mode can be queried via the Info String Query command.

Erase command example:

_p8_erase

  • $\color{#5dade2}{①}$:Destination port is function port 08
  • $\color{#af7ac5}{②}$:Subcommand erase
  • $\color{#af7ac5}{③}$:Erase address 0x08006000
  • $\color{#af7ac5}{④}$:Length 0x00000800 (2 KB)
  • $\color{#af7ac5}{⑤}$:0 indicates no error occurred

Write data example:

_p8_write

  • $\color{#af7ac5}{①}$:Subcommand write data
  • $\color{#af7ac5}{②}$:Write address 0x08006000
  • $\color{#af7ac5}{③}$:Writing 8 bytes of data 0x01 ~ 0x08 (IAP upgrades usually read/write 128 bytes at one time)
  • $\color{#af7ac5}{④}$:0 indicates no error occurred

Read data example:

_p8_read

  • $\color{#af7ac5}{①}$:Subcommand read data
  • $\color{#af7ac5}{②}$:Read address 0x08006000
  • $\color{#af7ac5}{③}$:Length of data to read
  • $\color{#af7ac5}{④}$:0 indicates no error occurred
  • $\color{#af7ac5}{⑤}$:Read data: 0x01 ~ 0x08 (8 bytes)

Read CRC example:

_p8_crc

  • $\color{#af7ac5}{①}$:Subcommand CRC check
  • $\color{#af7ac5}{②}$:Check address 0x08006000
  • $\color{#af7ac5}{③}$:Length of data to check
  • $\color{#af7ac5}{④}$:0 indicates no error occurred
  • $\color{#af7ac5}{⑤}$:CRC value; for data 01 02 03 04 05 06 07 08, CRC = 0xcfb0

Debug Printing #09

Once the board is housed in the enclosure, using UART printing or JTAG debugging is usually inconvenient. In this case, debug messages can be sent via the user UART. The user application must tolerate these messages; otherwise, disable this feature by writing 0 to the dbg_en register.
(Alternatively, a filter can be inserted between the user controller and the target device, so debug messages can be filtered and forwarded to another debugging device.)

Example:

_p9_dbg

  • $\color{#5dade2}{①}$:Destination port is function port 09
  • $\color{#af7ac5}{②}$:Reports the string "D: adc cali: ab 1665 1568, ca 1608 1664, cb 1608 1568", whose hexadecimal representation is $\texttt{\color{#af7ac5}{44 3a 20 61 ... 36 38}}$.

Waveform Debugging #0a

In addition to debug 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 payload format is:

report type 0: [x, d1,d2,d3, d1,d2,d3 ...]
report type 1: [x,d1,d2,d3, x,d1,d2,d3 ...]

The lower 4 bits of the source 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-period 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-period 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 source of the reported data can be fixed in the MCU firmware or configured via register read/write.

Example:

Below is an example of two consecutive type-0 report packets:

_pa_plot

  • $\color{#5dade2}{①}$:The lower 4 bits of the source port store the waveform index; in this example, the index is 0
  • $\color{#5dade2}{②}$:Destination port is function port 0a
  • $\color{#af7ac5}{③}$:x values are uint16_t; in this example, the difference between consecutive x values is 2
  • $\color{#af7ac5}{④⑤⑥}$: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:

x (timestamp) Position Voltage
0x0002 0x01020304 0x24
0x0004 0x01020305 0x22
0x0006 0x01020306 0x21
0x0008 0x01020307 0x20
0x000a 0x01020309 0x1f
0x000c 0x0102030a 0x1e

CDCAM Image Reporting Format #10

The target port number for image reporting is 0x10. The CDNET payload format is:

report: [data]

Source 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 sequentially concatenates the data from a first packet, multiple middle packets, and a last packet to reconstruct a JPG image (or another image format).

Writing 1 to the capture register returns one JPEG image. Writing 255 triggers continuous image streaming; writing 0 again stops the stream.

Image file upload example:

_p10_pic

  • $\color{#5dade2}{①}$:Source port
  • $\color{#5dade2}{②}$:Destination port is function port 0x10
  • $\color{#af7ac5}{③}$:JPG or other image file content

More Resources