Hey,
I wrote a custom Tiny USB driver to send data to and fro of RP2040 based Pico. The driver is not behaving properly and is dying randomly after running for a while. I have considered all the factors like callling tud_task() sufficient times, putting appropriate amounts of sleep at required places, writing to TX buffer only when there is space available for storing the data. Still I cannot reach any conclusion for this observed random behaviour. The host is sending data to Pico at 10Hz and Pico is also sending data back to host at around the same rate. This application is really critical and data based as Pico will be deployed away from easy reach and any failure may render it useless and problematic.
I am attaching the usb driver C code below. Please go through it. Any help will be highly appreciated.Thanks a lot!
I wrote a custom Tiny USB driver to send data to and fro of RP2040 based Pico. The driver is not behaving properly and is dying randomly after running for a while. I have considered all the factors like callling tud_task() sufficient times, putting appropriate amounts of sleep at required places, writing to TX buffer only when there is space available for storing the data. Still I cannot reach any conclusion for this observed random behaviour. The host is sending data to Pico at 10Hz and Pico is also sending data back to host at around the same rate. This application is really critical and data based as Pico will be deployed away from easy reach and any failure may render it useless and problematic.
I am attaching the usb driver C code below. Please go through it. Any help will be highly appreciated.
Code:
#ifndef USB_DRIVER_H#define USB_DRIVER_H#include <tusb.h>#include <stdarg.h>#include "TogglePower.h"/// TUSB Message IDs#define TUSB_MSG_ID_THRUSTER_SEND_PWMS ((uint8_t)0x01)#define TUSB_MSG_ID_THRUSTER_SEND_INDIVIDUAL_PWM ((uint8_t)0x02)#define TUSB_MSG_ID_THRUSTER_KILL_UNKILL ((uint8_t)0x03)#define TUSB_MSG_ID_THRUSTER_CHANGE_LIMITS ((uint8_t)0x04)#define TUSB_MSG_ID_THRUSTER_CHANGE_PWM_TIMEOUT ((uint8_t)0x05)#define TUSB_MSG_ID_SERVO_ACTUATE ((uint8_t)0x06)#define TUSB_MSG_ID_SERVO_RESET ((uint8_t)0x07)#define TUSB_MSG_ID_SERVO_SEND_CUSTOM_PWM ((uint8_t)0x08)#define TUSB_MSG_ID_DATA_IMU ((uint8_t)0x09)#define TUSB_MSG_ID_DATA_PS ((uint8_t)0x0A)#define TUSB_MSG_ID_AUV_STATE ((uint8_t)0x0B)#define TUSB_MSG_ID_LOG_STATEMENTS ((uint8_t)0x0C)#define TUSB_MSG_ID_GET_COMMANDS ((uint8_t)0x0D)#define TUSB_MSG_ID_BATTERY_WARNING ((uint8_t)0x0E)#define TUSB_MSG_ID_TEMPERATURE_WARNING ((uint8_t)0x10)#define TUSB_MSG_ID_TOGGLE ((uint8_t)0x11)#define TUSB_MSG_ID_UNIX_TIME ((uint8_t)0x12)#define TUSB_MSG_ID_MISSION_START ((uint8_t)0x13)#define TUSB_MSG_ID_RESET_IMU ((uint8_t)0x14)#define TUSB_MSG_ID_CURRENT_TIME_REQUEST ((uint8_t)0x15)#define TUSB_MSG_ID_LOCALISATION_RESET ((uint8_t)0x16)#define TUSB_MSG_ID_REBOOT_PICO ((uint8_t)0x17)/// TUSB message lengths /// Log messages lengths are variable/// Still have to fill message lengths for IMU, PS#define TUSB_MSG_LEN_THRUSTER_SEND_PWMS 8#define TUSB_MSG_LEN_THRUSTER_SEND_INDIVIDUAL_PWM 2#define TUSB_MSG_LEN_THRUSTER_KILL_UNKILL 1#define TUSB_MSG_LEN_THRUSTER_CHANGE_LIMITS 2#define TUSB_MSG_LEN_THRUSTER_CHANGE_PWM_TIMEOUT 1#define TUSB_MSG_LEN_SERVO_ACTUATE 2#define TUSB_MSG_LEN_SERVO_RESET 1#define TUSB_MSG_LEN_SERVO_SEND_CUSTOM_PWM 3#define TUSB_MSG_LEN_DATA_IMU 16#define TUSB_MSG_LEN_DATA_PS 4#define TUSB_MSG_LEN_GET_COMMANDS 1#define TUSB_MSG_LEN_AUV_STATE 1#define TUSB_MSG_LEN_BATTERY_VOLTAGE 1#define TUSB_MSG_LEN_BATTERY_WARNING 1#define TUSB_MSG_LEN_TEMPERATURE_READING 1#define TUSB_MSG_LEN_TEMPERATURE_WARNING 1#define TUSB_MSG_LEN_TOGGLE 2#define TUSB_MSG_LEN_UNIX_TIME 5 #define TUSB_MSG_LEN_MISSION_START 1#define TUSB_MSG_LEN_RESET_IMU 1#define TUSB_MSG_LEN_LOCALISATION_RESET 1#define TUSB_MSG_LEN_REBOOT_PICO 1/// Reserved Bytes (Not to be used anywhere in communication including payload)#define TUSB_MSG_START ((uint8_t)0xF2)#define TUSB_MSG_STOP ((uint8_t)0xEB)#define TUSB_MSG_ACK ((uint8_t)0xF6)#define TUSB_MSG_NACK ((uint8_t)0xE3)// Payload message IDs of Verbose, Debug, and MOSFETs#define TOGGLE_ID_VERBOSE ((uint8_t)0x2A)#define TOGGLE_ID_DEBUG ((uint8_t)0x2B)#define TOGGLE_ID_SBC_POWER ((uint8_t)0x20)#define TOGGLE_ID_DVL_POWER ((uint8_t)0x21)#define TOGGLE_ID_SPARE_5V ((uint8_t)0x22)#define TOGGLE_ID_THRUSTER_RELAY ((uint8_t)0x23)#define TOGGLE_ID_SPARE_16V_1 ((uint8_t)0x24)#define TOGGLE_ID_SPARE_16V_2 ((uint8_t)0x25)#define TOGGLE_ID_LED_STRIP ((uint8_t)0x26)#define TOGGLE_ID_SCREEN ((uint8_t)0x27)#define TOGGLE_ID_SERVO_POWER ((uint8_t)0x28)#define GET_COMMAND_ID_BATTERY_VOLTAGE_THRUSTERS ((uint8_t)0x04)#define GET_COMMAND_ID_BATTERY_VOLTAGE_STACK ((uint8_t)0x05)#define GET_COMMAND_ID_SECOND_PICO_CONNECTED ((uint8_t)0x06)#define SOFT_KILL ((uint8_t)0x01)#define UNKILL ((uint8_t)0x02)#define CHANGE_THRUSTER_MAX ((uint8_t)0x01)#define CHANGE_THRUSTER_MIN ((uint8_t)0x02)#define MARKER_DROPPER 1#define TORPEDO_SHOOTER 2#define GRIPPER 3#define MAX_PACKET_SIZE 59int parse_int(uint8_t* unparsed_int);bool send_usb_data(uint8_t message_id, int message_length, uint8_t* data){ if (!tud_cdc_connected()){ return false; } tud_task(); uint8_t message_buffer[64]; uint32_t packet_length = message_length + 5; // Start byte, Message ID, Length, Payload, Checksum, Stop byte // packet: [START][MSG_ID][LENGTH][PAYLOAD = {LENGTH} NUMBER OF BYTES][CHECKSUM][STOP] message_buffer[0] = TUSB_MSG_START; message_buffer[1] = message_id; message_buffer[2] = (uint8_t)message_length; for (int i = 0; i < message_length; i++) { message_buffer[3 + i] = data[i]; } // Compute XOR checksum uint8_t checksum = 0; checksum ^= message_id; checksum ^= (uint8_t)message_length; for (int i = 0; i < message_length; i++) { checksum ^= data[i]; } // Append checksum before the STOP byte message_buffer[3 + message_length] = checksum; message_buffer[4 + message_length] = TUSB_MSG_STOP; if(tud_cdc_write_available() > packet_length){ tud_cdc_write(message_buffer, packet_length); // tud_cdc_write_flush(); sleep_ms(1); } else{ tud_task(); tud_cdc_write_flush(); // tud_cdc_write_clear(); sleep_ms(2); return false; } // tud_cdc_n_read_flush(0); tud_task(); sleep_ms(1); return true; }#define USB_BUFFER_SIZE 256 // 256 just in case it is high speed//possible problem: incomplete packet received, more on the way, but half of it extracted. Current handling: ignore packetvoid read_usb_buffer() { uint8_t usb_buffer[USB_BUFFER_SIZE]; if (!tud_cdc_connected()) { return; } tud_task(); int received_length = tud_cdc_available(); if (received_length > 0) { tud_cdc_read(usb_buffer, received_length); // sleep_ms(1); // tud_cdc_n_read_flush(0); // Process all available packets int index = 0; while (index < received_length) { if (received_length - index < 5) { break; } // Validate START byte if (usb_buffer[index] != TUSB_MSG_START) { index++; continue; } uint8_t message_id = usb_buffer[index + 1]; uint8_t message_length = usb_buffer[index + 2]; // Validate message length and STOP byte position if (message_length + 5 > received_length - index || usb_buffer[index + 3 + message_length + 1] != TUSB_MSG_STOP) { index++; continue; } // Compute and validate XOR checksum uint8_t checksum = message_id ^ message_length; for (int i = 0; i < message_length; i++) { checksum ^= usb_buffer[index + 3 + i]; } if (checksum != usb_buffer[index + 3 + message_length]) { index++; continue; } // Process the packet uint8_t* payload = &usb_buffer[index + 3]; switch (message_id) { case TUSB_MSG_ID_THRUSTER_SEND_PWMS: if (message_length == TUSB_MSG_LEN_THRUSTER_SEND_PWMS){ int pwms[8]; for (int i = 0; i < message_length; i++){ pwms[i] = 1100 + 4 * int(*(payload + i * sizeof(uint8_t))); } if (VERBOSE) { log_message("Received thruster pwms: \n"); log_message("%d %d %d %d %d %d %d %d\n", pwms[0], pwms[1], pwms[2], pwms[3], pwms[4], pwms[5], pwms[6], pwms[7]); } set_thrusters(thrusters, pwms); } break; case TUSB_MSG_ID_THRUSTER_SEND_INDIVIDUAL_PWM: if (message_length == TUSB_MSG_LEN_THRUSTER_SEND_INDIVIDUAL_PWM){ int thruster_num = int(*(payload)); int pwm = 1100 + 4 * int(*(payload + sizeof(uint8_t))); if (VERBOSE) log_message("Receivd PWM for thruster %d: %d\n", thruster_num + 1, pwm); thrusters[thruster_num]->set_manual_pwm(pwm); } break; case TUSB_MSG_ID_THRUSTER_KILL_UNKILL: if (message_length == TUSB_MSG_LEN_THRUSTER_KILL_UNKILL){ if (*payload == SOFT_KILL){ if (VERBOSE) log_message("Soft Kill command received\n"); soft_kill(true); } else if (*payload == UNKILL){ if (VERBOSE) log_message("Thruster unkill command received\n"); unkill(); } } break; case TUSB_MSG_ID_THRUSTER_CHANGE_LIMITS: if (message_length == TUSB_MSG_LEN_THRUSTER_CHANGE_LIMITS){ int pwm = 1100 + 4 * int(*(payload + sizeof(uint8_t))); if (*payload == CHANGE_THRUSTER_MAX){ if (VERBOSE) log_message("Received set thruster pwm max to %d\n", pwm); thruster::set_max_thruster_pwm(pwm); } else if (*payload == CHANGE_THRUSTER_MIN){ if (VERBOSE) log_message("Received set thruster pwm min to %d\n", pwm); thruster::set_min_thruster_pwm(pwm); } } break; case TUSB_MSG_ID_THRUSTER_CHANGE_PWM_TIMEOUT: if (message_length == TUSB_MSG_LEN_THRUSTER_CHANGE_PWM_TIMEOUT){ int timeout = int(*payload); if (timeout == 0) { PWM_TIMEOUT_ENABLED = false; if (VERBOSE) { log_message("Disabled PWM timeout\n"); } } else if (timeout <= 10) { PWM_TIMEOUT_ENABLED = true; PWM_TIMEOUT = timeout; if (VERBOSE) { log_message("PWM Timeout set to %d seconds.\n", PWM_TIMEOUT); } } } break; case TUSB_MSG_ID_SERVO_ACTUATE: if (message_length == TUSB_MSG_LEN_SERVO_ACTUATE){ int actuator_type = int(*payload); int state = int(*(payload + sizeof(uint8_t))); switch (actuator_type){ case MARKER_DROPPER: marker_dropper.drop(state); break; case TORPEDO_SHOOTER: torpedo_shooter.shoot(state); break; case GRIPPER: gripper_arm.grip(); break; default: break; } } break; case TUSB_MSG_ID_SERVO_RESET: if (message_length == TUSB_MSG_LEN_SERVO_RESET){ switch (int(*payload)){ case MARKER_DROPPER: marker_dropper.reset(); break; case TORPEDO_SHOOTER: torpedo_shooter.reset(); break; case GRIPPER: gripper_arm.reset(); break; default: break; } } break; case TUSB_MSG_ID_SERVO_SEND_CUSTOM_PWM: if (message_length == TUSB_MSG_LEN_SERVO_SEND_CUSTOM_PWM){ int pwm = 64 * *(payload + 1 * sizeof(uint8_t)) + *(payload + 2 * sizeof(uint8_t)); switch (int(*payload)){ case MARKER_DROPPER: marker_dropper.set_manual_pwm(pwm); break; case TORPEDO_SHOOTER: torpedo_shooter.set_manual_pwm(pwm); break; case GRIPPER: gripper_arm.set_manual_pwm(pwm); break; default: break; } } break; case TUSB_MSG_ID_AUV_STATE: if (message_length == TUSB_MSG_LEN_AUV_STATE){ // actuate, reach, scan_align, raw_navigate // kill/unkill done separately // reach = 1 // scan_align = 2 // raw_navigate = 3 // actuate done otherwise uint8_t data_received = *payload; if (VERBOSE){ log_message("Received AUV state: %d\n", AUV_STATE); } if (data_received >= 1 && data_received <= 3){ if (PWM_ENABLE) AUV_STATE = data_received; } else{ log_message("Invalid auv_state received."); } } break; // For sending back battery voltage, temperature readings, current sensor readings, etc case TUSB_MSG_ID_GET_COMMANDS: if (message_length == TUSB_MSG_LEN_GET_COMMANDS){ uint8_t get_command_id = *payload; switch (get_command_id){ case GET_COMMAND_ID_BATTERY_VOLTAGE_STACK: break; case GET_COMMAND_ID_BATTERY_VOLTAGE_THRUSTERS: break; case GET_COMMAND_ID_SECOND_PICO_CONNECTED: break; default: break; } } break; case TUSB_MSG_ID_TOGGLE: if (message_length == TUSB_MSG_LEN_TOGGLE){ uint8_t toggle_id = *payload; int value = int(*(payload + sizeof(uint8_t))); switch (toggle_id){ case TOGGLE_ID_DEBUG: DEBUG_MODE = bool(value); log_message("Debug mode set to %d\n", DEBUG_MODE); break; case TOGGLE_ID_VERBOSE: VERBOSE = bool(value); log_message("Verbose set to %d\n", VERBOSE); break; case TOGGLE_ID_DVL_POWER: toggle_power(DVL_PIN, value); if (VERBOSE) { log_message("DVL power set to %d\n", value); } break; case TOGGLE_ID_LED_STRIP: if (VERBOSE) { log_message("LED strip power set to %d\n(not yet implemented)\n", value); } break; case TOGGLE_ID_SBC_POWER: if (VERBOSE) { log_message("SBC power set to %d\n(not yet implemented)\n", value); } break; case TOGGLE_ID_SCREEN: if (VERBOSE) { log_message("Screen power set to %d\n(not yet implemented)\n", value); } break; case TOGGLE_ID_SERVO_POWER: if (VERBOSE) { log_message("Servo power set to %d\n(not yet implemented)\n", value); } break; case TOGGLE_ID_SPARE_16V_1: if (VERBOSE) { log_message("Spare16V 1 power set to %d\n(not yet implemented)\n", value); } break; case TOGGLE_ID_SPARE_16V_2: if (VERBOSE) { log_message("Spare16V 2 power set to %d\n(not yet implemented)\n", value); } break; case TOGGLE_ID_SPARE_5V: if (VERBOSE) { log_message("Spare5V power set to %d\n(not yet implemented)\n", value); } break; case TOGGLE_ID_THRUSTER_RELAY: toggle_power(THRUSTER_RELAY_PIN, value); if (VERBOSE) { log_message("Thruster relay power set to %d\n", value); } break; default: break; } } break; case TUSB_MSG_ID_UNIX_TIME: if (message_length == TUSB_MSG_LEN_UNIX_TIME){ uint8_t unparsed_unix[5]; for (int i = 0; i < 5; i++){ unparsed_unix[i] = *(payload + i * sizeof(uint8_t)); } UNIX_TIME = parse_int(unparsed_unix); LAST_UNIX_UPDATE_TIME = time_us_64(); if (VERBOSE){ unix_to_datetime(UNIX_TIME); } } break; case TUSB_MSG_ID_RESET_IMU: if (message_length == TUSB_MSG_LEN_RESET_IMU){ hal_hardwareReset(); log_message("IMU has been reset"); } break; case TUSB_MSG_ID_LOCALISATION_RESET: if (message_length == TUSB_MSG_LEN_LOCALISATION_RESET){ LOCALISATION_RESET = true; log_message("Localisation reset"); } break; case TUSB_MSG_ID_REBOOT_PICO: if (message_length == TUSB_MSG_LEN_REBOOT_PICO){ watchdog_enable(1, 1); while(true); } break; default: // Unknown message ID, ignore packet break; } // Move to the next packet index += 5 + message_length; // Skip current packet } } // tud_cdc_n_read_flush(0); tud_task(); sleep_ms(1); return;}void log_message(const char* format, ...) { char buffer[256]; // Large enough to hold most messages va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); tud_cdc_write_flush(); size_t message_len = strlen(buffer); size_t offset = 0; while (offset < message_len) { size_t chunk_size = (message_len - offset > MAX_PACKET_SIZE) ? MAX_PACKET_SIZE : (message_len - offset); send_usb_data(TUSB_MSG_ID_LOG_STATEMENTS, chunk_size, (uint8_t*)(buffer + offset)); offset += chunk_size; } tud_cdc_write_flush(); tud_task();}#endifStatistics: Posted by AnirudhJ — Tue Jul 22, 2025 10:52 am