https://vesc-project.com/node/281


In VESC, the packet(payload) structure on UART communication


The packet is a uint8_t byte stream.

  • First byte(header):

    • 0x02 for payload length of 256 bytes, the next one byte is for the payload length

    • 0x03 for > 256 byte payload length, the next 2 bytes for the payload length

  • Payload(communication command + data)

  • The following 2 bytes after the payload are the checksum.

  • The byte stream it terminated with a 0x03 (footer).


"header(2 | 3) + 1-2 byte length + 2 byte crc + footer(3)"


The following function, packet_process_byte() shows how to process a packet stream.


packet.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
void packet_process_byte(uint8_t rx_data, int handler_num) {
    switch (handler_states[handler_num].rx_state) {
    case 0:
        if (rx_data == 2) {
            // 1 byte PL len
            handler_states[handler_num].rx_state += 2;
            handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
            handler_states[handler_num].rx_data_ptr = 0;
            handler_states[handler_num].payload_length = 0;
        } else if (rx_data == 3) {
            // 2 byte PL len
            handler_states[handler_num].rx_state++;
            handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
            handler_states[handler_num].rx_data_ptr = 0;
            handler_states[handler_num].payload_length = 0;
        } else {
            handler_states[handler_num].rx_state = 0;
        }
        break;
 
    case 1:
        handler_states[handler_num].payload_length = (unsigned int)rx_data << 8;
        handler_states[handler_num].rx_state++;
        handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        break;
 
    case 2:
        handler_states[handler_num].payload_length |= (unsigned int)rx_data;
        if (handler_states[handler_num].payload_length > 0 &&
                handler_states[handler_num].payload_length <= PACKET_MAX_PL_LEN) {
            handler_states[handler_num].rx_state++;
            handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        } else {
            handler_states[handler_num].rx_state = 0;
        }
        break;
 
    case 3:
        handler_states[handler_num].rx_buffer[handler_states[handler_num].rx_data_ptr++= rx_data;
        if (handler_states[handler_num].rx_data_ptr == handler_states[handler_num].payload_length) {
            handler_states[handler_num].rx_state++;
        }
        handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        break;
 
    case 4:
        handler_states[handler_num].crc_high = rx_data;
        handler_states[handler_num].rx_state++;
        handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        break;
 
    case 5:
        handler_states[handler_num].crc_low = rx_data;
        handler_states[handler_num].rx_state++;
        handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        break;
 
    case 6:
        if (rx_data == 3) {
            if (crc16(handler_states[handler_num].rx_buffer, handler_states[handler_num].payload_length)
                    == ((unsigned short)handler_states[handler_num].crc_high << 8
                            | (unsigned short)handler_states[handler_num].crc_low)) {
                // Packet received!
                if (handler_states[handler_num].process_func) {
                    handler_states[handler_num].process_func(handler_states[handler_num].rx_buffer,
                            handler_states[handler_num].payload_length);
                }
            }
        }
        handler_states[handler_num].rx_state = 0;
        break;
 
    default:
        handler_states[handler_num].rx_state = 0;
        break;
    }
}
cs


First byte of payload is command. All commands(38) supported by VESC firmware is shown bellow:


datatypes.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// Communication commands
typedef enum {
    COMM_FW_VERSION = 0,
    COMM_JUMP_TO_BOOTLOADER,
    COMM_ERASE_NEW_APP,
    COMM_WRITE_NEW_APP_DATA,
    COMM_GET_VALUES,
    COMM_SET_DUTY,
    COMM_SET_CURRENT,
    COMM_SET_CURRENT_BRAKE,
    COMM_SET_RPM,
    COMM_SET_POS,
    COMM_SET_HANDBRAKE,
    COMM_SET_DETECT,
    COMM_SET_SERVO_POS,
    COMM_SET_MCCONF,
    COMM_GET_MCCONF,
    COMM_GET_MCCONF_DEFAULT,
    COMM_SET_APPCONF,
    COMM_GET_APPCONF,
    COMM_GET_APPCONF_DEFAULT,
    COMM_SAMPLE_PRINT,
    COMM_TERMINAL_CMD,
    COMM_PRINT,
    COMM_ROTOR_POSITION,
    COMM_EXPERIMENT_SAMPLE,
    COMM_DETECT_MOTOR_PARAM,
    COMM_DETECT_MOTOR_R_L,
    COMM_DETECT_MOTOR_FLUX_LINKAGE,
    COMM_DETECT_ENCODER,
    COMM_DETECT_HALL_FOC,
    COMM_REBOOT,
    COMM_ALIVE,
    COMM_GET_DECODED_PPM,
    COMM_GET_DECODED_ADC,
    COMM_GET_DECODED_CHUK,
    COMM_FORWARD_CAN,
    COMM_SET_CHUCK_DATA,
    COMM_CUSTOM_APP_DATA,
    COMM_NRF_START_PAIRING
} COMM_PACKET_ID;
 
cs


The processing for each command is excuted by 'commands_process_packet()' function in command.h and the following shows an example for 'COMM_GET_VALUES' command case.

 

commands.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
case COMM_GET_VALUES:
        ind = 0;
        send_buffer[ind++= COMM_GET_VALUES;
        buffer_append_float16(send_buffer, mc_interface_temp_fet_filtered(), 1e1&ind);
        buffer_append_float16(send_buffer, mc_interface_temp_motor_filtered(), 1e1&ind);
        buffer_append_float32(send_buffer, mc_interface_read_reset_avg_motor_current(), 1e2&ind);
        buffer_append_float32(send_buffer, mc_interface_read_reset_avg_input_current(), 1e2&ind);
        buffer_append_float32(send_buffer, mc_interface_read_reset_avg_id(), 1e2&ind);
        buffer_append_float32(send_buffer, mc_interface_read_reset_avg_iq(), 1e2&ind);
        buffer_append_float16(send_buffer, mc_interface_get_duty_cycle_now(), 1e3&ind);
        buffer_append_float32(send_buffer, mc_interface_get_rpm(), 1e0&ind);
        buffer_append_float16(send_buffer, GET_INPUT_VOLTAGE(), 1e1&ind);
        buffer_append_float32(send_buffer, mc_interface_get_amp_hours(false), 1e4&ind);
        buffer_append_float32(send_buffer, mc_interface_get_amp_hours_charged(false), 1e4&ind);
        buffer_append_float32(send_buffer, mc_interface_get_watt_hours(false), 1e4&ind);
        buffer_append_float32(send_buffer, mc_interface_get_watt_hours_charged(false), 1e4&ind);
        buffer_append_int32(send_buffer, mc_interface_get_tachometer_value(false), &ind);
        buffer_append_int32(send_buffer, mc_interface_get_tachometer_abs_value(false), &ind);
        send_buffer[ind++= mc_interface_get_fault();
        commands_send_packet(send_buffer, ind);
        break;
cs


please refer to https://github.com/RollingGecko/VescUartControl



Posted by Nature & Life