LCOV - code coverage report
Current view: top level - AP_Torqeedo - AP_Torqeedo_TQBus.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 3 0.0 %
Date: 2026-09-09 11:02:08 Functions: 0 3 0.0 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             :  
      16             : /*
      17             :    This driver supports communicating with Torqeedo motors that implement the "TQ Bus" protocol
      18             :    which includes the Ultralight, Cruise 2.0 R, Cruise 4.0 R, Travel 503, Travel 1003 and Cruise 10kW
      19             : 
      20             :    The autopilot should be connected either to the battery's tiller connector or directly to the motor
      21             :    as described on the ArduPilot wiki. https://ardupilot.club/rover/docs/common-torqeedo.html
      22             :    TQ Bus is a serial protocol over RS-485 meaning that a serial to RS-485 converter is required.
      23             : 
      24             :        Tiller connection: Autopilot <-> Battery (master) <-> Motor
      25             :        Motor connection:  Autopilot (master) <-> Motor
      26             : 
      27             :     Communication between the components is always initiated by the master with replies sent within 25ms
      28             : 
      29             :     Example "Remote (0x01)" reply message to allow tiller to control motor speed
      30             :     Byte        Field Definition    Example Value   Comments
      31             :     ---------------------------------------------------------------------------------
      32             :     byte 0      Header              0xAC
      33             :     byte 1      TargetAddress       0x00            see MsgAddress enum
      34             :     byte 2      Message ID          0x00            only master populates this. replies have this set to zero
      35             :     byte 3      Flags               0x05            bit0=pin present, bit2=motor speed valid
      36             :     byte 4      Status              0x00            0x20 if byte3=4, 0x0 is byte3=5
      37             :     byte 5      Motor Speed MSB     ----            Motor Speed MSB (-1000 to +1000)
      38             :     byte 6      Motor Speed LSB     ----            Motor Speed LSB (-1000 to +1000)
      39             :     byte 7      CRC-Maxim           ----            CRC-Maxim value
      40             :     byte 8      Footer              0xAD
      41             : 
      42             :    More details of the TQ Bus protocol are available from Torqeedo after signing an NDA.
      43             :  */
      44             : 
      45             : #pragma once
      46             : 
      47             : #include "AP_Torqeedo_config.h"
      48             : 
      49             : #if HAL_TORQEEDO_ENABLED
      50             : 
      51             : #include "AP_Torqeedo_Backend.h"
      52             : 
      53             : #define TORQEEDO_MESSAGE_LEN_MAX    35  // messages are no more than 35 bytes
      54             : 
      55             : class AP_Torqeedo_TQBus : public AP_Torqeedo_Backend {
      56             : public:
      57             : 
      58             :     // constructor
      59             :     using AP_Torqeedo_Backend::AP_Torqeedo_Backend;
      60             : 
      61             :     CLASS_NO_COPY(AP_Torqeedo_TQBus);
      62             : 
      63             :     // initialise driver
      64             :     void init() override;
      65             : 
      66             :     // returns true if communicating with the motor
      67             :     bool healthy() override;
      68             : 
      69             :     // clear motor errors
      70           0 :     void clear_motor_error() override { _motor_clear_error = true; }
      71             : 
      72             :     // get latest battery status info.  returns true on success and populates arguments
      73             :     bool get_batt_info(float &voltage, float &current_amps, float &temp_C, uint8_t &pct_remaining) const override;
      74             :     bool get_batt_capacity_Ah(uint16_t &amp_hours) const override;
      75             : 
      76             : private:
      77             : 
      78             :     // consume incoming messages from motor, reply with latest motor speed
      79             :     // runs in background thread
      80             :     void thread_main();
      81             : 
      82             :     // report changes in error codes to user
      83             :     void report_error_codes();
      84             : 
      85             :     // message addresses
      86             :     enum class MsgAddress : uint8_t {
      87             :         BUS_MASTER = 0x00,
      88             :         REMOTE1 = 0x14,
      89             :         DISPLAY = 0x20,
      90             :         MOTOR = 0x30,
      91             :         BATTERY = 0x80
      92             :     };
      93             : 
      94             :     // Remote specific message ids
      95             :     enum class RemoteMsgId : uint8_t {
      96             :         INFO = 0x00,
      97             :         REMOTE = 0x01,
      98             :         SETUP = 0x02
      99             :     };
     100             : 
     101             :     // Display specific message ids
     102             :     enum class DisplayMsgId : uint8_t {
     103             :         INFO = 0x00,
     104             :         SYSTEM_STATE = 0x41,
     105             :         SYSTEM_SETUP = 0x42
     106             :     };
     107             : 
     108             :     // Motor specific message ids
     109             :     enum class MotorMsgId : uint8_t {
     110             :         INFO = 0x00,
     111             :         STATUS = 0x01,
     112             :         PARAM = 0x03,
     113             :         CONFIG = 0x04,
     114             :         DRIVE = 0x82
     115             :     };
     116             : 
     117             :     enum class ParseState {
     118             :         WAITING_FOR_HEADER = 0,
     119             :         WAITING_FOR_FOOTER,
     120             :     };
     121             : 
     122             :     // initialise serial port and gpio pins (run from background thread)
     123             :     // returns true on success
     124             :     bool init_internals();
     125             : 
     126             :     // process a single byte received on serial port
     127             :     // return true if a complete message has been received (the message will be held in _received_buff)
     128             :     bool parse_byte(uint8_t b);
     129             : 
     130             :     // process message held in _received_buff
     131             :     void parse_message();
     132             : 
     133             :     // returns true if it is safe to send a message
     134           0 :     bool safe_to_send() const { return ((_send_delay_us == 0) && (_reply_wait_start_ms == 0)); }
     135             : 
     136             :     // set pin to enable sending a message
     137             :     void send_start();
     138             : 
     139             :     // check for timeout after sending a message and unset pin if required
     140             :     void check_for_send_end();
     141             : 
     142             :     // calculate delay required to allow message to be completely sent
     143             :     uint32_t calc_send_delay_us(uint8_t num_bytes);
     144             : 
     145             :     // record msgid of message to wait for and set timer for reply timeout handling
     146             :     void set_expected_reply_msgid(uint8_t msg_id);
     147             : 
     148             :     // check for timeout waiting for reply
     149             :     void check_for_reply_timeout();
     150             : 
     151             :     // mark reply received. should be called whenever a message is received regardless of whether we are actually waiting for a reply
     152             :     void set_reply_received();
     153             : 
     154             :     // send a message to the motor with the specified message contents
     155             :     // msg_contents should not include the header, footer or CRC
     156             :     // returns true on success
     157             :     bool send_message(const uint8_t msg_contents[], uint8_t num_bytes);
     158             : 
     159             :     // add a byte to a message buffer including adding the escape character (0xAE) if necessary
     160             :     // this should only be used when adding the contents to the buffer, not the header and footer
     161             :     // num_bytes is updated to the next free byte
     162             :     bool add_byte_to_message(uint8_t byte_to_add, uint8_t msg_buff[], uint8_t msg_buff_size, uint8_t &num_bytes) const;
     163             : 
     164             :     // send a motor speed command as a value from -1000 to +1000
     165             :     // value is taken directly from SRV_Channel
     166             :     void send_motor_speed_cmd();
     167             : 
     168             :     // send request to motor to reply with a particular message
     169             :     // msg_id can be INFO, STATUS or PARAM
     170             :     void send_motor_msg_request(MotorMsgId msg_id);
     171             : 
     172             :     // calculate the limited motor speed that is sent to the motors
     173             :     // desired_motor_speed argument and returned value are in the range -1000 to 1000
     174             :     int16_t calc_motor_speed_limited(int16_t desired_motor_speed);
     175           0 :     int16_t get_motor_speed_limited() const { return (int16_t)_motor_speed_limited; }
     176             : 
     177             :     // log TRQD message which holds high level status and latest desired motors peed
     178             :     // force_logging should be true to immediately write log bypassing timing check to avoid spamming
     179             :     void log_TRQD(bool force_logging);
     180             : 
     181             :     // send ESC telemetry
     182             :     void update_esc_telem(float rpm, float voltage, float current_amps, float esc_tempC, float motor_tempC);
     183             : 
     184             :     // members
     185             :     AP_HAL::UARTDriver *_uart;      // serial port to communicate with motor
     186             :     bool _initialised;              // true once driver has been initialised
     187             :     bool _send_motor_speed;         // true if motor speed should be sent at next opportunity
     188             :     int16_t _motor_speed_desired;   // desired motor speed (set from within update method)
     189             :     uint32_t _last_send_motor_ms;   // system time (in millis) last motor speed command was sent (used for health reporting)
     190             :     bool _motor_clear_error;        // true if the motor error should be cleared (sent in "Drive" message)
     191             :     uint32_t _send_start_us;        // system time (in micros) when last message started being sent (used for timing to unset DE pin)
     192             :     uint32_t _send_delay_us;        // delay (in micros) to allow bytes to be sent after which pin can be unset.  0 if not delaying
     193             : 
     194             :     // motor speed limit variables
     195             :     float _motor_speed_limited;     // limited desired motor speed. this value is actually sent to the motor
     196             :     uint32_t _motor_speed_limited_ms; // system time that _motor_speed_limited was last updated
     197             :     int8_t _dir_limit;              // acceptable directions for output to motor (+1 = positive OK, -1 = negative OK, 0 = either positive or negative OK)
     198             :     uint32_t _motor_speed_zero_ms;  // system time that _motor_speed_limited reached zero.  0 if currently not zero
     199             : 
     200             :     // health reporting
     201             :     HAL_Semaphore _last_healthy_sem;// semaphore protecting reading and updating of _last_send_motor_ms and _last_received_ms
     202             :     uint32_t _last_log_TRQD_ms;     // system time (in millis) that TRQD was last logged
     203             : 
     204             :     // message parsing members
     205             :     ParseState _parse_state;        // current state of parsing
     206             :     bool _parse_escape_received;    // true if the escape character has been received so we must XOR the next byte
     207             :     uint32_t _parse_error_count;    // total number of parsing errors (for reporting)
     208             :     uint32_t _parse_success_count;  // number of messages successfully parsed (for reporting)
     209             :     uint8_t _received_buff[TORQEEDO_MESSAGE_LEN_MAX];   // characters received
     210             :     uint8_t _received_buff_len;     // number of characters received
     211             :     uint32_t _last_received_ms;     // system time (in millis) that a message was successfully parsed (for health reporting)
     212             : 
     213             :     // reply message handling
     214             :     uint8_t _reply_msgid;           // replies expected msgid (reply often does not specify the msgid so we must record it)
     215             :     uint32_t _reply_wait_start_ms;  // system time that we started waiting for a reply message
     216             : 
     217             :     // Display system state flags
     218             :     typedef union PACKED {
     219             :         struct {
     220             :             uint8_t set_throttle_stop   : 1;    // 0, warning that user must set throttle to stop before motor can run
     221             :             uint8_t setup_allowed       : 1;    // 1, remote is allowed to enter setup mode
     222             :             uint8_t in_charge           : 1;    // 2, master is in charging state
     223             :             uint8_t in_setup            : 1;    // 3, master is in setup state
     224             :             uint8_t bank_available      : 1;    // 4
     225             :             uint8_t no_menu             : 1;    // 5
     226             :             uint8_t menu_off            : 1;    // 6
     227             :             uint8_t reserved7           : 1;    // 7, unused
     228             :             uint8_t temp_warning        : 1;    // 8, motor or battery temp warning
     229             :             uint8_t batt_charge_valid   : 1;    // 9, battery charge valid
     230             :             uint8_t batt_nearly_empty   : 1;    // 10, battery nearly empty
     231             :             uint8_t batt_charging       : 1;    // 11, battery charging
     232             :             uint8_t gps_searching       : 1;    // 12, gps searching for satellites
     233             :             uint8_t gps_speed_valid     : 1;    // 13, gps speed is valid
     234             :             uint8_t range_miles_valid   : 1;    // 14, range (in miles) is valid
     235             :             uint8_t range_minutes_valid : 1;    // 15, range (in minutes) is valid
     236             :         };
     237             :         uint16_t value;
     238             :     } DisplaySystemStateFlags;
     239             : 
     240             :     // Display system state
     241             :     struct DisplaySystemState {
     242             :         DisplaySystemStateFlags flags;  // flags, see above for individual bit definitions
     243             :         uint8_t master_state;           // deprecated
     244             :         uint8_t master_error_code;      // error code (0=no error)
     245             :         float motor_voltage;            // motor voltage in volts
     246             :         float motor_current;            // motor current in amps
     247             :         uint16_t motor_power;           // motor power in watts
     248             :         int16_t motor_rpm;              // motor speed in rpm
     249             :         uint8_t motor_pcb_temp;         // motor pcb temp in C
     250             :         uint8_t motor_stator_temp;      // motor stator temp in C
     251             :         uint8_t batt_charge_pct;        // battery state of charge (0 to 100%)
     252             :         float batt_voltage;             // battery voltage in volts
     253             :         float batt_current;             // battery current in amps
     254             :         uint16_t gps_speed;             // gps speed in knots * 100
     255             :         uint16_t range_miles;           // range in nautical miles * 10
     256             :         uint16_t range_minutes;         // range in minutes (at current speed and current draw)
     257             :         uint8_t temp_sw;                // master PCB temp in C (close to motor power switches)
     258             :         uint8_t temp_rp;                // master PCB temp in C (close to reverse voltage protection)
     259             :         uint32_t last_update_ms;        // system time that system state was last updated
     260             :     } _display_system_state;
     261             : 
     262             :     // Display system setup
     263             :     struct DisplaySystemSetup {
     264             :         uint8_t flags;              // 0 : battery config valid, all other bits unused
     265             :         uint8_t motor_type;         // motor type (0 or 3:Unknown, 1:Ultralight, 2:Cruise2, 4:Cruise4, 5:Travel503, 6:Travel1003, 7:Cruise10kW)
     266             :         uint16_t motor_sw_version;  // motor software version
     267             :         uint16_t batt_capacity;     // battery capacity in amp hours
     268             :         uint8_t batt_charge_pct;    // battery state of charge (0 to 100%)
     269             :         uint8_t batt_type;          // battery type (0:lead acid, 1:Lithium)
     270             :         uint16_t master_sw_version; // master software version
     271             :     } _display_system_setup;
     272             : 
     273             :     // Motor status
     274             :     struct MotorStatus {
     275             :         union PACKED {
     276             :             struct {
     277             :                 uint8_t temp_limit_motor    : 1;    // 0, motor speed limited due to motor temp
     278             :                 uint8_t temp_limit_pcb      : 1;    // 1, motor speed limited tue to PCB temp
     279             :                 uint8_t emergency_stop      : 1;    // 2, motor in emergency stop (must be cleared by master)
     280             :                 uint8_t running             : 1;    // 3, motor running
     281             :                 uint8_t power_limit         : 1;    // 4, motor power limited
     282             :                 uint8_t low_voltage_limit   : 1;    // 5, motor speed limited because of low voltage
     283             :                 uint8_t tilt                : 1;    // 6, motor is tilted
     284             :                 uint8_t reserved7           : 1;    // 7, unused (always zero)
     285             :             } status_flags;
     286             :             uint8_t status_flags_value;
     287             :         };
     288             :         union PACKED {
     289             :             struct {
     290             :                 uint8_t overcurrent         : 1;    // 0, motor stopped because of overcurrent
     291             :                 uint8_t blocked             : 1;    // 1, motor stopped because it is blocked
     292             :                 uint8_t overvoltage_static  : 1;    // 2, motor stopped because voltage too high
     293             :                 uint8_t undervoltage_static : 1;    // 3, motor stopped because voltage too low
     294             :                 uint8_t overvoltage_current : 1;    // 4, motor stopped because voltage spiked high
     295             :                 uint8_t undervoltage_current: 1;    // 5, motor stopped because voltage spiked low
     296             :                 uint8_t overtemp_motor      : 1;    // 6, motor stopped because stator temp too high
     297             :                 uint8_t overtemp_pcb        : 1;    // 7, motor stopped because pcb temp too high
     298             :                 uint8_t timeout_rs485       : 1;    // 8, motor stopped because Drive message not received for too long
     299             :                 uint8_t temp_sensor_error   : 1;    // 9, motor temp sensor is defective (motor will not stop)
     300             :                 uint8_t tilt                : 1;    // 10, motor stopped because it was tilted
     301             :                 uint8_t unused11to15        : 5;    // 11 ~ 15 (always zero)
     302             :             } error_flags;
     303             :             uint16_t error_flags_value;
     304             :         };
     305             :     } _motor_status;
     306             :     uint32_t _last_send_motor_status_request_ms;    // system time (in milliseconds) that last motor status request was sent
     307             : 
     308             :     // Motor params
     309             :     struct MotorParam {
     310             :         int16_t rpm;            // motor rpm
     311             :         uint16_t power;         // motor power consumption in Watts
     312             :         float voltage;          // motor voltage in volts
     313             :         float current;          // motor current in amps
     314             :         float pcb_temp;         // pcb temp in C
     315             :         float stator_temp;      // stator temp in C
     316             :         uint32_t last_update_ms;// system time that above values were updated
     317             :     } _motor_param;
     318             :     uint32_t _last_send_motor_param_request_ms;     // system time (in milliseconds) that last motor param request was sent
     319             : 
     320             :     // error reporting
     321             :     DisplaySystemStateFlags _display_system_state_flags_prev;   // backup of display system state flags
     322             :     uint8_t _display_system_state_master_error_code_prev;       // backup of display system state master_error_code
     323             :     uint32_t _last_error_report_ms;                             // system time that flag changes were last reported (used to prevent spamming user)
     324             :     MotorStatus _motor_status_prev;                             // backup of motor status
     325             : 
     326             :     // returns a human-readable string corresponding the passed-in
     327             :     // master error code (see page 93 of https://media.torqeedo.com/downloads/manuals/torqeedo-Travel-manual-DE-EN.pdf)
     328             :     // If no conversion is available then nullptr is returned
     329             :     const char *map_master_error_code_to_string(uint8_t code) const;
     330             : };
     331             : 
     332             : #endif // HAL_TORQEEDO_ENABLED

Generated by: LCOV version 1.14