LCOV - code coverage report
Current view: top level - AP_FETtecOneWire - AP_FETtecOneWire.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 24 24 100.0 %
Date: 2026-09-09 11:02:08 Functions: 17 17 100.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             : /* Initial protocol implementation was provided by FETtec */
      17             : /* Strongly modified by Amilcar Lucas, IAV GmbH */
      18             : 
      19             : #pragma once
      20             : 
      21             : #include <AP_HAL/AP_HAL.h>
      22             : 
      23             : #ifndef AP_FETTEC_ONEWIRE_ENABLED
      24             : #define AP_FETTEC_ONEWIRE_ENABLED HAL_PROGRAM_SIZE_LIMIT_KB > 1024
      25             : #endif
      26             : 
      27             : // Support both full-duplex at 500Kbit/s as well as half-duplex at 2Mbit/s (optional feature)
      28             : #ifndef HAL_AP_FETTEC_HALF_DUPLEX
      29             : #define HAL_AP_FETTEC_HALF_DUPLEX 0
      30             : #endif
      31             : 
      32             : // Get static info from the ESCs (optional feature)
      33             : #ifndef HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO
      34             : #define HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO 0
      35             : #endif
      36             : 
      37             : // provide beep support (optional feature)
      38             : #ifndef HAL_AP_FETTEC_ESC_BEEP
      39             : #define HAL_AP_FETTEC_ESC_BEEP 0
      40             : #endif
      41             : 
      42             : // provide light support (optional feature)
      43             : #ifndef HAL_AP_FETTEC_ESC_LIGHT
      44             : #define HAL_AP_FETTEC_ESC_LIGHT 0
      45             : #endif
      46             : 
      47             : #if AP_FETTEC_ONEWIRE_ENABLED
      48             : 
      49             : #define FTW_DEBUGGING 0
      50             : #if FTW_DEBUGGING
      51             : #include <stdio.h>
      52             : #define fet_debug(fmt, args ...)  do {::fprintf(stderr,"FETtec: %s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); } while(0)
      53             : #else
      54             : #define fet_debug(fmt, args ...)
      55             : #endif
      56             : 
      57             : #include <AP_ESC_Telem/AP_ESC_Telem.h>
      58             : #include <AP_Param/AP_Param.h>
      59             : 
      60             : #include <AP_Math/AP_Math.h>
      61             : #include <AP_Math/crc.h>
      62             : 
      63             : class AP_FETtecOneWire : public AP_ESC_Telem_Backend
      64             : {
      65             : 
      66             : public:
      67             :     AP_FETtecOneWire();
      68             : 
      69             :     /// Do not allow copies
      70             :     CLASS_NO_COPY(AP_FETtecOneWire);
      71             : 
      72             :     static const struct AP_Param::GroupInfo var_info[];
      73             : 
      74      140495 :     static AP_FETtecOneWire *get_singleton() {
      75      140495 :         return _singleton;
      76             :     }
      77             : 
      78             :     /// periodically called from SRV_Channels::push()
      79             :     void update();
      80             : 
      81             :     /// called from AP_Arming; should return false if arming should be
      82             :     /// disallowed
      83             :     bool pre_arm_check(char *failure_msg, const uint8_t failure_msg_len) const;
      84             : 
      85             : #if HAL_AP_FETTEC_ESC_BEEP
      86             :     /**
      87             :     makes all connected ESCs beep
      88             :     @param beep_frequency a 8 bit value from 0-255. higher make a higher beep
      89             :     */
      90             :     void beep(const uint8_t beep_frequency);
      91             : #endif
      92             : 
      93             : #if HAL_AP_FETTEC_ESC_LIGHT
      94             :     /**
      95             :     sets the racewire color for all ESCs
      96             :     @param r red brightness
      97             :     @param g green brightness
      98             :     @param b blue brightness
      99             :     */
     100             :     void led_color(const uint8_t r, const uint8_t g, const uint8_t b);
     101             : #endif
     102             : 
     103             : private:
     104             :     static AP_FETtecOneWire *_singleton;
     105             :     AP_HAL::UARTDriver *_uart;
     106             : 
     107             :     AP_Int32 _motor_mask_parameter;
     108             :     AP_Int32 _reverse_mask_parameter;
     109             : #if HAL_WITH_ESC_TELEM
     110             :     AP_Int8 _pole_count_parameter;
     111             : #endif
     112             : 
     113             :     static constexpr uint8_t FRAME_OVERHEAD = 6;          ///< OneWire message frame overhead (header+tail bytes)
     114             :     static constexpr uint8_t MAX_RECEIVE_LENGTH = 12;     ///< OneWire max receive message payload length in bytes
     115             :     static constexpr uint8_t MAX_ESC_COUNT = 24;          ///< Max ESCs supported (will be fewer with telemetry)
     116             : #if HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO
     117             :     static constexpr uint8_t SERIAL_NUMBER_LENGTH = 12;   ///< ESC serial number length in bytes
     118             : #endif
     119             : 
     120             :     /**
     121             :         initialize the device driver: configure serial port, wake-up and configure ESCs
     122             :     */
     123             :     void init();
     124             : 
     125             :     /**
     126             :         initialize the serial port
     127             :     */
     128             :     void init_uart();
     129             : 
     130             :     /**
     131             :         scan the OneWire bus, configure the ESCs requested in the _motor_mask_parameter
     132             :     */
     133             :     void configure_escs();
     134             : 
     135             :     // states configured ESCs can be in:
     136             :     enum class ESCState : uint8_t {
     137             :         UNINITIALISED = 5,  // when we haven't tried to send anything to the ESC
     138             : 
     139             :         WANT_SEND_OK_TO_GET_RUNNING_SW_TYPE = 10,
     140             :         WAITING_OK_FOR_RUNNING_SW_TYPE = 11,
     141             : 
     142             :         WANT_SEND_START_FW = 20,
     143             :         WAITING_OK_FOR_START_FW = 21,
     144             : 
     145             : #if HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO
     146             :         WANT_SEND_REQ_TYPE = 30,
     147             :         WAITING_ESC_TYPE = 31,
     148             : 
     149             :         WANT_SEND_REQ_SW_VER = 40,
     150             :         WAITING_SW_VER = 41,
     151             : 
     152             :         WANT_SEND_REQ_SN = 50,
     153             :         WAITING_SN = 51,
     154             : #endif
     155             : 
     156             : #if HAL_WITH_ESC_TELEM
     157             :         WANT_SEND_SET_TLM_TYPE = 60,
     158             :         WAITING_SET_TLM_TYPE_OK = 61,
     159             : #endif
     160             : 
     161             :         WANT_SEND_SET_FAST_COM_LENGTH = 70,
     162             :         WAITING_SET_FAST_COM_LENGTH_OK = 71,
     163             : 
     164             :         RUNNING = 100,
     165             :     };
     166             : 
     167             :     class ESC {
     168             :     public:
     169             : 
     170             : #if HAL_WITH_ESC_TELEM
     171             :         uint32_t last_telem_us;              ///< last time we got telemetry from this ESC
     172             :         uint16_t unexpected_telem;
     173             :         uint16_t error_count_at_throttle_count_overflow;            ///< overflow counter for error counter from the ESCs.
     174             :         bool telem_expected;                 ///< this ESC is fully configured and is now expected to send us telemetry
     175             :         bool telem_requested;                ///< this ESC is fully configured and at some point was requested to send us telemetry
     176             : #endif
     177             : 
     178             :         uint8_t id;         ///< FETtec ESC ID
     179             :         uint8_t servo_ofs;  ///< offset into ArduPilot servo array
     180             :         bool is_awake;
     181        2017 :         void set_state(ESCState _state) {
     182             :             fet_debug("Moving ESC.id=%u from state=%u to state=%u", (unsigned)id, (unsigned)state, (unsigned)_state);
     183        2017 :             state = _state;
     184        2017 :         };
     185             :         ESCState state = ESCState::UNINITIALISED;
     186             : 
     187             : #if HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO
     188             :         uint8_t serial_number[SERIAL_NUMBER_LENGTH];
     189             :         uint8_t firmware_version;
     190             :         uint8_t firmware_subversion;
     191             :         uint8_t type;
     192             : #endif
     193             :     };
     194             : 
     195             :     uint32_t _min_fast_throttle_period_us;  ///< minimum allowed fast-throttle command transmit period
     196             :     uint32_t _motor_mask;                    ///< an un-mutable copy of the _motor_mask_parameter taken before _init_done goes true
     197             :     uint32_t _reverse_mask;                  ///< a copy of the _reverse_mask_parameter taken while not armed
     198             :     uint32_t _running_mask;                  ///< a bitmask of the actively running ESCs
     199             :     uint32_t _last_transmit_us;             ///< last time the transmit() function sent data
     200             :     ESC *_escs;
     201             :     uint8_t _esc_count;                ///< number of allocated ESCs
     202             :     uint8_t _fast_throttle_byte_count; ///< pre-calculated number of bytes required to send an entire packed throttle message
     203             : 
     204             : #if HAL_AP_FETTEC_HALF_DUPLEX
     205             :     uint8_t _ignore_own_bytes; ///< bytes to ignore while receiving, because we have transmitted them ourselves
     206             :     uint8_t _last_crc;         ///< the CRC from the last sent fast-throttle command
     207             :     bool _use_hdplex;          ///< use asynchronous half-duplex serial communication
     208             : #endif
     209             : 
     210             :     bool _init_done;     ///< device driver is initialized; ESCs may still need to be configured
     211             :     bool _invalid_mask;  ///< true if the mask parameter is invalid
     212             : 
     213             :     enum class FrameSource : uint8_t {
     214             :         MASTER     = 0x01,  ///< master is always 0x01
     215             :         BOOTLOADER = 0x02,
     216             :         ESC        = 0x03,
     217             :     };
     218             : 
     219             :     enum class MsgType : uint8_t
     220             :     {
     221             :         OK                  = 0,
     222             :         BL_PAGE_CORRECT     = 1,  ///< Bootloader only
     223             :         NOT_OK              = 2,
     224             :         BL_START_FW         = 3,  ///< Bootloader only - exit the boot loader and start the standard firmware
     225             :         BL_PAGES_TO_FLASH   = 4,  ///< Bootloader only
     226             : #if HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO
     227             :         REQ_TYPE            = 5,  ///< ESC type
     228             :         REQ_SN              = 6,  ///< serial number
     229             :         REQ_SW_VER          = 7,  ///< software version
     230             : #endif
     231             : #if HAL_AP_FETTEC_ESC_BEEP
     232             :         BEEP                = 13, ///< make noise
     233             : #endif
     234             :         SET_FAST_COM_LENGTH = 26, ///< configure fast-throttle command
     235             :         SET_TLM_TYPE        = 27, ///< telemetry operation mode
     236             : #if HAL_AP_FETTEC_ESC_LIGHT
     237             :         SET_LED_TMP_COLOR   = 51, ///< set ESC's LED color
     238             : #endif
     239             :     };
     240             : 
     241             :     /**
     242             :     a frame looks like:
     243             :     byte 1 = frame header (master is always 0x01)
     244             :     byte 2 = target ID (5bit)
     245             :     byte 3 & 4 = frame type (always 0x00, 0x00 used for bootloader. here just for compatibility)
     246             :     byte 5 = frame length over all bytes
     247             :     byte 6 - X = request type, followed by the payload
     248             :     byte X+1 = 8bit CRC
     249             :     */
     250             :     template <typename T>
     251             :     class PACKED PackedMessage {
     252             :     public:
     253        1268 :         PackedMessage(uint8_t _esc_id, T _msg) :
     254        1268 :             esc_id(_esc_id),
     255        1268 :             msg(_msg)
     256             :         {
     257        1268 :             update_checksum();
     258        1268 :         }
     259             :         uint8_t frame_source { (uint8_t)FrameSource::MASTER };
     260             :         uint8_t esc_id;
     261             :         uint16_t frame_type { 0 };  // bootloader only, always zero
     262             :         uint8_t frame_length {sizeof(T) + FRAME_OVERHEAD};  // all bytes including frame_source and checksum
     263             :         T msg;
     264             :         uint8_t checksum;
     265             : 
     266        1268 :         void update_checksum() {
     267        1268 :             checksum = crc8_dvb_update(0, (const uint8_t*)this, frame_length-1);
     268        1268 :         }
     269             :     };
     270             : 
     271             :     class PACKED OK {
     272             :     public:
     273             :         uint8_t msgid { (uint8_t)MsgType::OK };
     274             :     };
     275             : 
     276             :     class PACKED START_FW {
     277             :     public:
     278             :         uint8_t msgid { (uint8_t)MsgType::BL_START_FW };
     279             :     };
     280             : 
     281             :     class PACKED SET_FAST_COM_LENGTH {
     282             :     public:
     283          65 :         SET_FAST_COM_LENGTH(uint8_t _byte_count, uint8_t _min_esc_id, uint8_t _esc_count) :
     284          65 :             byte_count{_byte_count},
     285          65 :             min_esc_id{_min_esc_id},
     286          65 :             esc_count{_esc_count}
     287          65 :         { }
     288             :         uint8_t msgid { (uint8_t)MsgType::SET_FAST_COM_LENGTH };
     289             :         uint8_t byte_count;
     290             :         uint8_t min_esc_id;
     291             :         uint8_t esc_count;
     292             :     };
     293             : 
     294             : #if HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO
     295             :     class PACKED REQ_TYPE {
     296             :     public:
     297             :         uint8_t msgid { (uint8_t)MsgType::REQ_TYPE };
     298             :     };
     299             : 
     300             :     class PACKED REQ_SW_VER {
     301             :     public:
     302             :         uint8_t msgid { (uint8_t)MsgType::REQ_SW_VER };
     303             :     };
     304             : 
     305             :     class PACKED REQ_SN {
     306             :     public:
     307             :         uint8_t msgid { (uint8_t)MsgType::REQ_SN };
     308             :     };
     309             : 
     310             :     class PACKED ESC_TYPE {
     311             :     public:
     312             :         ESC_TYPE(uint8_t _type) :
     313             :             type{_type} { }
     314             :         uint8_t type;
     315             :     };
     316             : 
     317             :     class PACKED SW_VER {
     318             :     public:
     319             :         SW_VER(uint8_t _version, uint8_t _subversion) :
     320             :             version{_version},
     321             :             subversion{_subversion}
     322             :             { }
     323             :         uint8_t version;
     324             :         uint8_t subversion;
     325             :     };
     326             : 
     327             :     class PACKED SN {
     328             :     public:
     329             :         SN(uint8_t *_sn, uint8_t snlen) {
     330             :             memcpy(sn, _sn, ARRAY_SIZE(sn));
     331             :         }
     332             :         uint8_t sn[SERIAL_NUMBER_LENGTH];
     333             :     };
     334             : 
     335             : #endif  // HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO
     336             : 
     337             :     void pack_fast_throttle_command(const uint16_t *motor_values, uint8_t *buffer, const uint8_t length, const uint8_t esc_id_to_request_telem_from);
     338             : 
     339             : /*
     340             :  * Messages, methods and states for dealing with ESC telemetry
     341             :  */
     342             : #if HAL_WITH_ESC_TELEM
     343             :     void handle_message_telem(ESC &esc);
     344             : 
     345             :     uint16_t _fast_throttle_cmd_count;     ///< number of fast-throttle commands sent by the flight controller
     346             : 
     347             :     /// the ESC at this offset into _escs should be the next to send a
     348             :     /// telemetry request for:
     349             :     uint8_t _esc_ofs_to_request_telem_from;
     350             : 
     351             :     class PACKED SET_TLM_TYPE {
     352             :     public:
     353          65 :         SET_TLM_TYPE(uint8_t _tlm_type) :
     354          65 :             tlm_type{_tlm_type}
     355          65 :         { }
     356             :         uint8_t msgid { (uint8_t)MsgType::SET_TLM_TYPE };
     357             :         uint8_t tlm_type;
     358             :     };
     359             : 
     360             :     class PACKED TLM {
     361             :     public:
     362             :         TLM(int8_t _temp, uint16_t _voltage, uint16_t _current, int16_t _rpm, uint16_t _consumption_mah, uint16_t _tx_err_count) :
     363             :             temp{_temp},
     364             :             voltage{_voltage},
     365             :             current{_current},
     366             :             rpm{_rpm},
     367             :             consumption_mah{_consumption_mah},
     368             :             tx_err_count{_tx_err_count}
     369             :         { }
     370             :         int8_t temp;              // centi-degrees
     371             :         uint16_t voltage;         // centi-Volt
     372             :         uint16_t current;         // centi-Ampere  (signed?)
     373             :         int16_t rpm;              // centi-rpm
     374             :         uint16_t consumption_mah; // milli-Ampere.hour
     375             :         uint16_t tx_err_count;    // CRC error count, as perceived from the ESC receiving side
     376             :     };
     377             : 
     378             : #endif  // HAL_WITH_ESC_TELEM
     379             : 
     380             : #if HAL_AP_FETTEC_ESC_BEEP
     381             :     class PACKED Beep {
     382             :     public:
     383             :         Beep(uint8_t _beep_frequency) :
     384             :             beep_frequency{_beep_frequency}
     385             :         { }
     386             :         uint8_t msgid { (uint8_t)MsgType::BEEP };
     387             :         uint8_t beep_frequency;
     388             :         // add two zeros to make sure all ESCs can catch their command as we don't wait for a response here  (don't blame me --pb)
     389             :         uint16_t spacer = 0;
     390             :     };
     391             : #endif // HAL_AP_FETTEC_ESC_BEEP
     392             : 
     393             : #if HAL_AP_FETTEC_ESC_LIGHT
     394             :     class PACKED LEDColour {
     395             :     public:
     396             :         LEDColour(uint8_t _r, uint8_t _g, uint8_t _b) :
     397             :             r{_r},
     398             :             g{_g},
     399             :             b{_b}
     400             :           { }
     401             :         uint8_t msgid { (uint8_t)MsgType::SET_LED_TMP_COLOR };
     402             :         uint8_t r;
     403             :         uint8_t g;
     404             :         uint8_t b;
     405             :         // add two zeros to make sure all ESCs can catch their command as we don't wait for a response here  (don't blame me --pb)
     406             :         uint16_t spacer = 0;
     407             :     };
     408             : #endif  // HAL_AP_FETTEC_ESC_LIGHT
     409             : 
     410             :     /*
     411             :      * Methods and data for transmitting data to the ESCSs:
     412             :      */
     413             : 
     414             :     /**
     415             :         transmits data to ESCs
     416             :         @param bytes  bytes to transmit
     417             :         @param length number of bytes to transmit
     418             :         @return false there's no space in the UART for this message
     419             :     */
     420             :     bool transmit(const uint8_t* bytes, const uint8_t length);
     421             : 
     422             :     template <typename T>
     423             :     bool transmit(const PackedMessage<T> &msg) {
     424             :         return transmit((const uint8_t*)&msg, sizeof(msg));
     425             :     }
     426             : 
     427             :     /**
     428             :         transmits configuration request data to ESCs
     429             :         @param bytes  bytes to transmit
     430             :         @param length number of bytes to transmit
     431             :         @return false if vehicle armed or there's no space in the UART for this message
     432             :     */
     433             :     bool transmit_config_request(const uint8_t* bytes, const uint8_t length);
     434             : 
     435             :     template <typename T>
     436        1268 :     bool transmit_config_request(const PackedMessage<T> &msg) {
     437        1268 :         return transmit_config_request((const uint8_t*)&msg, sizeof(msg));
     438             :     }
     439             : 
     440             :     /**
     441             :         sends a single fast-throttle frame containing the throttle for all configured OneWire ESCs.
     442             :         @param motor_values a 16bit array containing the throttle values that should be sent to the motors. 0-2000 where 1001-2000 is positive rotation and 999-0 reversed rotation
     443             :     */
     444             :     void escs_set_values(const uint16_t *motor_values);
     445             : 
     446             :     /*
     447             :      * Methods and data for receiving data from the ESCs:
     448             :      */
     449             : 
     450             :     // FIXME: this should be tighter - and probably calculated.  Note
     451             :     // that we can't request telemetry faster than the loop interval,
     452             :     // which is 20ms on Plane, so that puts a constraint here.  When
     453             :     // using fast-throttle with 12 ESCs on Plane you could expect
     454             :     // 240ms between telem updates.  Why you have a Plane with 12 ESCs
     455             :     // is a bit of a puzzle.
     456             :     static const uint32_t max_telem_interval_us = 100000;
     457             : 
     458             :     void handle_message(ESC &esc, const uint8_t length);
     459             : 
     460             :     /**
     461             :         reads data from the UART, calling handle_message on any message found
     462             :     */
     463             :     void read_data_from_uart();
     464             :     union MessageUnion {
     465        2094 :         MessageUnion() { }
     466             :         PackedMessage<OK> packed_ok;
     467             : #if HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO
     468             :         PackedMessage<ESC_TYPE> packed_esc_type;
     469             :         PackedMessage<SW_VER> packed_sw_ver;
     470             :         PackedMessage<SN> packed_sn;
     471             : #endif
     472             : #if HAL_WITH_ESC_TELEM
     473             :         PackedMessage<TLM> packed_tlm;
     474             : #endif
     475             :         uint8_t receive_buf[FRAME_OVERHEAD + MAX_RECEIVE_LENGTH];
     476             :     } u;
     477             : 
     478             :     static_assert(sizeof(u.packed_ok) <= sizeof(u.receive_buf),"packed_ok does not fit in receive_buf. MAX_RECEIVE_LENGTH too small?");
     479             : #if HAL_AP_FETTEC_ONEWIRE_GET_STATIC_INFO
     480             :     static_assert(sizeof(u.packed_esc_type) <= sizeof(u.receive_buf),"packed_esc_type does not fit in receive_buf. MAX_RECEIVE_LENGTH too small?");
     481             :     static_assert(sizeof(u.packed_sw_ver) <= sizeof(u.receive_buf),"packed_sw_ver does not fit in receive_buf. MAX_RECEIVE_LENGTH too small?");
     482             :     static_assert(sizeof(u.packed_sn) <= sizeof(u.receive_buf),"packed_sn does not fit in receive_buf. MAX_RECEIVE_LENGTH too small?");
     483             : #endif
     484             : #if HAL_WITH_ESC_TELEM
     485             :     static_assert(sizeof(u.packed_tlm) <= sizeof(u.receive_buf),"packed_tlm does not fit in receive_buf. MAX_RECEIVE_LENGTH too small?");
     486             : #endif
     487             : 
     488             :     uint16_t _unknown_esc_message;
     489             :     uint16_t _message_invalid_in_state_count;
     490             :     uint16_t _period_too_short;
     491             :     uint16_t crc_rec_err_cnt;
     492             :     uint8_t _receive_buf_used;
     493             : 
     494             :     /// shifts data to start of buffer based on magic header bytes
     495             :     void move_frame_source_in_receive_buffer(const uint8_t search_start_pos = 0);
     496             : 
     497             :     /// cut n bytes from start of buffer
     498             :     void consume_bytes(const uint8_t n);
     499             : 
     500             :     /// returns true if the first message in the buffer is OK
     501             :     bool buffer_contains_ok(const uint8_t length);
     502             : };
     503             : #endif // AP_FETTEC_ONEWIRE_ENABLED

Generated by: LCOV version 1.14