Conversation
Spektrum's "Smart Throttle" is SRXL2 on the throttle signal wire: the ESC is an SRXL2 slave (device type 4, IDs 0x40-0x4F) and the receiver, or here the flight controller, is the bus master. Reverse on an Avian is only reachable this way - over plain PWM the ESC has no way to be told - so this is the only route to thrust reverse on INAV. This commit adds the protocol layer alone: framing, CRC, handshake with baud negotiation, Control Data with a channel mask, failsafe, and decoding of STRU_TELE_ESC telemetry. The packet structures are the ones INAV already carries in rx/srxl2_types.h for the receiver side, so both ends of the protocol share a single definition. Two deliberate properties worth not undoing: - The master starts silent. Specification 7.2.1 requires a master sharing its UART with a possible throttle PWM line to wait for a slave handshake rather than speak first, because sending SRXL2 at a non-SRXL2 ESC can cause "unintended movement or erratic behavior". If nothing answers within 200 ms it concludes no SRXL2 device is present and stays quiet. - There is no fallback to PWM. The pin is a UART pin, not a timer output, so "degrade gracefully to PWM" is not available and must not be faked. Channel values use the exact inverse of the scaling rx/srxl2.c already applies when decoding, so 1500 us maps onto 0x8000, the specification's "Servo Center". Not yet wired to motorWritePtr, settings, or esc_sensor; those follow. Builds clean for SITL with -Wall -Wextra -Werror. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reviewing how existing bus-master implementations behave against real Smart ESCs surfaced five things the specification leaves implicit, all of which the first version got wrong: 1. Device ID. We announced ourselves as 0x30, unit ID 0. Specification 7.1.1 gives unit 0 the meaning "send an unprompted handshake at startup", which is slave behaviour: a master doing it collides with the ESC's own announcements. Now 0x31. 2. Never finding a quiet ESC. The master listened and, hearing nothing, stayed silent for good. Only an ESC with unit ID 0 announces itself, so an ESC configured otherwise would never have been found. It now listens first, to keep out of the way of the common case, then polls the ESC device ID. 3. Baud switch losing a frame. serialSetBaudRate() was called immediately after queueing the broadcast that tells the bus to change rate, so the tail of that very frame would have been clocked out at the new rate. The change is now deferred until isSerialTransmitBufferEmpty(). 4. Control Data at the wrong rate. It was emitted per motor update, i.e. at loop rate. The specification has the master send at the rate RF frames arrive, tens of hertz, and 115200 baud could not carry loop rate anyway - one frame is about 1.6 ms on the wire. Now rate-limited to 50 Hz, independent of the mixer. 5. Telemetry requested on every frame. Telemetry is a reply, so asking every frame doubles bus occupancy and forces a half-duplex turnaround each time for values that barely change. Now every fifth frame, giving 10 Hz. Also handles the ESC powering up after the flight controller, which is the normal bench case: the board runs on USB and the ESC only boots with the battery, long after the listen window closed, so its handshake arrives mid-RUNNING and has to be honoured rather than ignored. Builds clean for SITL with -Wall -Wextra -Werror. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three more things from comparing against working bus-master implementations: - RSSI was sent as 0 because we are not an RF device. The field is defined as "best RSSI when sending channel data", and an ESC may reasonably read nothing as a dead link and apply its own failsafe, so 0 is the wrong answer even when technically true. Now 100. - Unused channels are still not padded with centred values, and there is now a comment saying why, because the opposite choice looks tidier. Padding with centre is right for a surface ESC, where centre means stopped. On an aircraft ESC 1500 us is half throttle, so if the ESC read throttle on an index we had not anticipated, padding would spin the motor at 50 percent while sending nothing there leaves it idle. A wrong guess should fail safe. - Documented that the channel scaling deliberately does not reach the ends of the 0..65532 range, since a receiver's full travel decodes to 988..2012 us rather than 1000..2000, and named it as the constant to revisit if an ESC cannot reach full throttle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… full range The previous comment had this backwards, suggesting the constant should be changed if an ESC cannot reach full throttle. Spektrum ESCs learn their throttle endpoints during the ESC/Radio calibration their manual describes, and INAV cannot run that procedure: it requires full throttle to be present as the battery is connected, and INAV outputs mincommand while disarmed. The calibration is therefore performed with a Spektrum transmitter, or left at the factory default, and the range the ESC remembers is a receiver's. Emulating a receiver is the reason this scaling is correct, not a compromise. The roughly 1.2 percent of travel lost at the top is the better side of the trade: stretching 1000..2000 us over the full 0..65532 range would move the centre, and the centre is where an ESC in Reverse brake mode takes zero thrust. A slightly low maximum costs throttle curve; a misplaced centre costs creeping thrust at neutral. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A Spektrum ESC learns its endpoints from the signal present as it powers up:
full throttle, then low within five seconds of the tones that acknowledge it.
INAV could not produce that, because it outputs mincommand while disarmed, so the
documented procedure needs a Spektrum transmitter - which leaves an INAV user who
does not own one unable to calibrate the ESC at all.
esc_calibrate start runs the sequence unattended. The five second window opens at
tones only a person standing there can hear, so rather than ask the operator to
type against a stopwatch, the sequence is timed from the moment the ESC gains
power, which the flight controller can see: either the pack appearing on the
voltage sensor, or the ESC announcing itself on the bus.
esc_calibrate start battery disconnected; plug it in when told
esc_calibrate off abort
esc_calibrate high/low by hand, for boards with no voltage sensing
One of these phases commands full throttle with the aircraft disarmed, so:
- it refuses to start with the battery already connected. The ESC only reads its
endpoints as it powers up, so starting with it live would achieve nothing, and
it would mean presenting full throttle to an ESC able to act on it. Every other
safeguard here is a latch; this one removes the hazard.
- it refuses while armed, in the command and again in the driver rather than
trusting the caller, and arming cancels a sequence in progress.
- every phase leaves on a deadline, so nothing can strand the output high.
- the override is applied where the packet is built, not where values are staged,
so no mixer path can write over it in between.
- the help leads with the propeller.
The automatic mode declines up front when there is no battery voltage sensing,
instead of starting a sequence that could never advance, and points at the manual
phases.
The three second settle after power-up is taken from the published tone timings
rather than measured, and is the first thing to adjust if an ESC refuses the
calibration.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Spektrum's reference application advances its SRXL2 state machine on a 5 ms tick, and a master has to run several times faster than its own Control Data interval for replies to be collected promptly on a half-duplex wire. TASK_PWMDRIVER, which already exists to drive the SBUS servo output, runs at 200 Hz and matches that exactly - worth writing down before the task is wired up, because the number is not obvious from either end. Also confirms the device ID chosen here: Spektrum's own example configuration uses 0x31, flight controller type with unit ID 1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e RSSI value
Both confirmed against Spektrum's own published code rather than reasoned about.
The reference implementation sets the reply ID to zero when it emits failsafe
channel data - a device being told the link is gone has nothing useful to answer
with - so the telemetry request is now suppressed while failsafe is announced.
The RSSI comment now carries its evidence. Spektrum's receiver code reads a
received zero as loss of link:
if (channelData->rssi == 0) { globalResult = RX_FRAME_FAILSAFE; }
so sending 0, which this driver originally did on the grounds that we are not an
RF device, would have announced a dead link on every frame.
Also notes that the bus arbitrates mastership by device ID, lowest winning, and
why this driver does not implement standing down: the port is a dedicated link to
an ESC, and 0x40 cannot outrank 0x31. It would be wrong on a bus shared with a
Spektrum receiver, which would be master at 0x21.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The protocol has a failsafe channel-data command that a receiver sends when its RF link is gone, so each device applies its own failsafe. Transcribing it here was faithful to the protocol and wrong for this architecture. On this bus the master is the flight controller and there is no RF link - the link is a wire. INAV handles failsafe by substituting channel values and continuing to fly: LAND and RTH command the motors all the way down, and DROP_IT holds throttle at neutral. Telling the ESC the link had failed would hand throttle authority to the ESC's own behaviour in the middle of INAV's landing, so the two would fight over the motor during the one manoeuvre that most needs a single authority. What protects against this wire actually dying is the ESC's own receive timeout, which needs no cooperation from us: if the flight controller stops sending, the ESC falls back on its own. That is the case where the ESC's failsafe is the right authority, and it works without being told. Removes srxl2MotorSetFailsafe() rather than leaving it unused, and records the reasoning where the command constant used to be, since the protocol offering a failsafe command invites putting it back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Caught by running the driver against Spektrum's own library acting as a Smart ESC. The calibration sequence left its wait-for-battery phase when either the pack appeared or escDeviceId was non-zero. The second test was meant to read as "the ESC just announced itself, so it has just powered up", but escDeviceId is persistent state held from the last time the ESC was seen, not an event. On any board whose ESC had already completed a handshake - which is every board in normal use - the wait was skipped immediately and the sequence ran through without the operator having connected anything. Both conditions are now events: the pack appearing, or the handshake count moving from where it stood when the phase began. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lter motor_pwm_protocol gains SRXL2, appended rather than inserted so existing stored values keep their meaning. Selecting it points motorWritePtr at the SRXL2 driver, the same hook DSHOT uses. If the port was never assigned the pointer is left null and the motor stays unwritten: the pin is a UART pin, so there is no PWM to fall back to and pretending otherwise would be worse than doing nothing. TASK_PWMDRIVER, which already exists for the SBUS servo output, gets enabled for this protocol too and runs the master. Its 200 Hz happens to be exactly the 5 ms cadence Spektrum's reference application advances its state machine on. Reverse follows the mixer rather than a mode of its own: when INAV has decided the motor should run backwards, the ESC's reverse channel is armed. A separate switch would let the two disagree about direction, which is the one thing that must not happen with thrust reverse. Which auxiliary channel to use is esc_srxl2_reverse_channel, because nothing on the wire advertises how the ESC was programmed. Telemetry is taken in esc_sensor, which means every existing consumer is fed without knowing where the numbers came from - including the RPM filter, which reads getEscTelemetry() and therefore now works on an ESC that is not DSHOT. The wire carries electrical rpm, so it is divided by motorPoleCount/2 exactly as computeRpm() does for the serial backends; getting that wrong would place the notch at the wrong frequency. There is no separate telemetry port to assign, since Smart Throttle carries telemetry on the throttle wire. Enabled by default only on H7 and AT32, where flash is plentiful. It costs about 3.5 KB, and AIKONF7 sits at 93.4% of its flash: measured, that target returns to 459091 bytes, byte-for-byte its size without this feature. A tighter target that wants the protocol can define USE_MOTOR_SRXL2 for itself. docs/Settings.md regenerated for the new setting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit took telemetry from the SRXL2 link whenever that was the motor protocol, with no way to decline. Turning ESC telemetry off needs no setting for a conventional ESC - you leave the port unassigned - but an SRXL2 ESC returns telemetry on the throttle wire, so there is no port to leave unassigned and no other way to say no, which also makes the two halves impossible to separate when something looks wrong on a bench. esc_srxl2_telemetry, default ON, is that switch. It sits in motorConfig beside esc_srxl2_reverse_channel so the two SRXL2 settings live together, it is conditional on USE_MOTOR_SRXL2, and it gates only the SRXL2 path. escSensorConfig and the serial backend are left byte-identical to upstream: a conventional setup gains no setting, sees no new behaviour, and has nothing new to misconfigure. An earlier attempt at this offered AUTO/SERIAL/SRXL2/NONE. The explicit middle values described combinations no real hardware wants - a Smart ESC has no separate telemetry lead, and forcing SRXL2 under a different motor protocol does nothing - so they were settings that could only be set wrong. Also guards motorConfig's new fields behind USE_MOTOR_SRXL2. The settings generator only emits the SETTING_..._DEFAULT macros where the condition holds, so an unguarded initialiser broke every target without the feature. Caught by building AIKONF7, which has it disabled: neither SITL nor the H7 target would have shown it, since SITL excludes esc_sensor.c and the H7 build has the feature on. Measured after: AIKONF7 at 459075 bytes, the same as without this work at all, and TBS_LUCID_H7_WING at 36.15% of flash. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The calibration already existed as a CLI command, but a wizard cannot use it: the Configurator confines CLI to its own tab and no other tab sends CLI lines. MSP2_INAV_ESC_SRXL2_CALIBRATE starts or aborts the sequence. MSP2_INAV_ESC_SRXL2_STATUS reports the current phase and whether an ESC is answering, so a wizard can follow what is happening rather than describing what it hopes is happening. The refusals are not re-implemented here. One of these phases commands full throttle with the aircraft disarmed, and a second copy of the conditions is a second thing to forget to update, so the command calls into the driver and honours what it returns. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A page covering the wiring, which is the part most likely to be got wrong: the signal wire goes to a UART's TX pin rather than a motor pad, and nothing connects to the ESC telemetry pad, because Smart Throttle carries telemetry on the throttle wire and a Smart ESC has no separate telemetry lead. Also covers the calibration sequence, the reverse channel having to match how the ESC was programmed since nothing on the wire advertises it, and why the pole count matters more than usual: the wire carries electrical rpm, so a wrong pole count puts the RPM filter's notch at the wrong frequency, which is worse than no notch. On the single-ESC limit, the page says what is actually true rather than calling the protocol single-motor. A bus can address several ESCs but each needs a unit ID that the specification says cannot be set over SRXL2, so one per bus; this driver drives one, on one port; a motor per port would be reasonable for a twin fixed-wing; and what rules out multirotors is the update rate, tens of hertz by design against DSHOT's kilohertz, not the wiring. SITL now defines USE_MOTOR_SRXL2. It is not covered by the flash-size rule in common.h, and opting in explicitly is both the documented mechanism for any target and useful here: SITL exposes each UART on a TCP port, so a simulated ESC can be attached to the real driver and the path from the Configurator through MSP to the wire exercised without hardware. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One ESC per bus, several buses. The driver held a single set of statics and
srxl2MotorUpdate() took a motor index it then ignored, so a twin with two Avians
folded both motors onto the first port and dropped the second silently. The per-bus
state is now an array, one instance per assigned port, each with its own handshake,
baud negotiation, receive framing and telemetry.
Motors map to ports in port order, which is the only mapping available: nothing on
an SRXL2 bus says which motor an ESC drives. Fewer ports than motors is refused at
init with PWM_INIT_ERROR_NOT_ENOUGH_MOTOR_OUTPUTS rather than absorbed, because a
motor with no port has no timer output to fall back on.
Reverse is armed on every ESC together: the mixer decides a direction for the
aircraft, and reversing one side of a twin and not the other is worth engineering
against. Calibration likewise runs on all of them, since they share a battery and
so power up together. esc_sensor fills escSensorData[i] per port.
Three defects fixed along the way:
- motorProtocolProperties[] had no PWM_TYPE_SRXL2 entry, so asking for its
properties read one past the end of the array. Added unconditionally, because
the value is stored in configuration and a diff restored from a board that has
the protocol would otherwise index out of bounds on one that does not.
- the handshake was re-entered on every handshake frame received. Our finalise
broadcasts, the slave answers the broadcast, and that answer restarted the
sequence - so the two ping-ponged handshakes indefinitely. Control data is sent
on a timer reset on entering RUNNING, so the bus never reached its first control
frame: the link read as established and the motor never turned. On a slow port
it was worse, each restart queueing another broadcast so the transmit buffer
never drained. The negotiation is now entered only from the states that are
still looking for an ESC.
- handshakes were counted per frame, which the calibration uses as its "an ESC
just gained power" event. Counted per negotiation now.
Verified against Spektrum's own SRXL2 library as the counterpart, compiled twice
under renamed symbols so the two simulated ESCs are genuinely independent devices
rather than one library in hub mode: 54 checks, 2175 frames accepted, 0 rejected by
their parser. The harness also models the transmit buffer draining at the wire's
rate instead of claiming it is always empty, which is what exposed the handshake
loop - the deferred baud switch depends on that answer being honest.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reverse on a Smart ESC is a switch, not a throttle value. Spektrum say so in as
many words - "flipping the designated switch reverses motor rotation, throttle
will still control motor speed" - so the throttle goes on meaning throttle and the
reverse channel is a binary arm. That maps onto a mode, which is how every other
pilot-commanded action in INAV works, so there is now a THRUST REVERSE box. It is
offered only when the protocol is SRXL2 and a reverse channel is set, so it never
appears as a mode that silently does nothing.
Deriving reverse from the reversible-motor mixer state, as this did before, was the
wrong model for the aircraft the feature is for. It requires FEATURE_REVERSIBLE_MOTORS,
which recentres the throttle stick so mid-stick is zero thrust: forward thrust then
lives only in the top half of the stick, chopping the throttle on short final
commands reverse in the air, arming needs the stick centred rather than down, and an
ARM switch becomes mandatory. Reverse is also unreachable from every automatic
throttle path, since they all clamp to at least idle. Fine for a 3D model, wrong for
an aeroplane that wants reverse on the landing roll. The mixer state is still
honoured for the models it does suit; either route arms reverse and neither masks
the other.
Two defects fixed:
- a reverse channel of 1 landed on the throttle's own slot, and
srxl2MotorSetReverse() writes its channel at task rate, so it overwrote the
mixer's staged throttle several hundred times a second: the motor held at idle
whenever reverse was released and at full throttle whenever it was armed. The
setting's range cannot express "zero, or five to nine", so the driver refuses
the collision itself.
- reverse was never released on disarm. mixTable() returns early while disarmed
and so stops updating the direction, while the task kept publishing it, leaving
an aircraft that landed under reverse sitting on the ground with the ESC's
reverse channel armed until the throttle next went forward.
Default reverse channel moved from 5 to 7, which is what Spektrum ship: the Avian
160/200HV manual states "It's default setting is channel 7" and the 130 Pro table
marks CH7 likewise. Sending on 5 while the ESC watched 7 meant reverse never
engaged, silently, out of the box.
Documentation rewritten to match: the setting selects a slot on the SRXL2 wire and
not a transmitter channel, the switch is assigned in Modes, the channel must match
the ESC's own Thrust Rev. parameter, and reverse cannot assist an automatic landing.
Oracle: 57 checks, 0 failures, including a new one pinning the throttle-collision
refusal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validateAndFixConfig() forced any protocol above BRUSHED to MULTISHOT on builds without USE_DSHOT. SRXL2 was appended after DSHOT600, so it fell inside that range and was silently rewritten on every boot: the setting could be saved, but the board came back up on MULTISHOT and nothing SRXL2 was reachable - no port function acted on, no THRUST REVERSE mode offered, no ESC block in the Outputs tab. The check is about DSHOT, so it now names DSHOT rather than testing "above BRUSHED". SRXL2 is a UART protocol and a build without DSHOT can drive it perfectly well. Found on SITL, which is built without DSHOT, once the whole path was exercised from the Configurator against a SITL binary carrying this branch. Verified there over MSP: the protocol survives a reboot and the firmware then reports THRUST REVERSE in its mode list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The throttle-range calibration has preconditions - disarmed, an open SRXL2 port, a voltage sensor, and the battery not yet connected - and MSP2_INAV_ESC_SRXL2_CALIBRATE is an IN command, so a refusal reached the caller as a bare error with nowhere to say which one failed. A wizard cannot act on that: it either has to guess or carry on regardless, and carrying on means telling the operator to connect a battery to a sequence that never started, then reporting it finished. The driver now remembers its verdict and MSP2_INAV_ESC_SRXL2_STATUS reports it, alongside the number of open ports. Both are appended, so a reader that expects the old two bytes is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two things a caller could not previously get right. MSP2_INAV_ESC_SRXL2_STATUS now reports the mixer's motor count alongside the number of open ports. These are exactly the two numbers pwmInitMotors() compares to decide whether the board may arm, so a caller no longer has to infer either. The obvious place to look, MSP2_INAV_MIXER, is a trap: its last two bytes are MAX_SUPPORTED_MOTORS and MAX_SUPPORTED_SERVOS - the compile-time ceilings, not the model - so reading a motor count from there reports 12 on any board. SITL now opens its SRXL2 ports. It has no motor output layer at all - the simulator reads the mixer's motor[] array directly, so pwmMotorPreconfigure() never runs and nothing opened them. Since SITL maps every UART onto a TCP port, opening them is what makes the target.h comment true: a simulated ESC can be attached to the real driver, and the handshake, telemetry and calibration paths exercised, with no hardware. Verified against a SITL carrying this branch: assign the function, set the protocol, reboot, and the status reports one open port and accepts a calibration. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
How often the ESC is asked for telemetry was a compile-time constant. The reply shares the throttle wire with the control data, so the right answer depends on the model: the RPM filter wants it fast, everything else is a display and reads fine at a few hertz, and a busy bus wants it slow. esc_srxl2_telemetry_rate now offers 50, 25, 10, 5 and 2 Hz, derived from the 50 Hz control rate. The table lists the default first so that it is index 0, which is not cosmetic. The field is appended to motorConfig_t, but it landed inside the struct's existing padding, so sizeof did not grow and pgLoad()'s memcpy copies the old zero over the reset default - a configuration saved before this existed reads index 0 whatever the default says. Verified on SITL: with a pre-existing eeprom the field read 0 while a fresh one read the default, which with the natural fast-to-slow ordering would have silently moved every upgraded board to a telemetry request on every single control frame. With the default at index 0 both now agree. Worth remembering for the next field: appending to the end of a parameter group is necessary but not sufficient. It is only additive if it actually grows the struct past its padding. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
escSensorGetData() returned NULL unless escSensorPort was open. That was the same question as "is there ESC telemetry" for as long as the only source was a dedicated telemetry lead, but a Spektrum Smart ESC reports over the throttle wire itself, handled by the motor driver, so the port stays NULL while the data is perfectly good. Everything that reads through that function therefore saw nothing on such a board: the OSD's ESC elements, the Blackbox ESC fields, current and voltage estimation, and the Jeti and SBUS2 telemetry backends. The OSD was inconsistent with itself as a result - it checks STATE(ESC_SENSOR_ENABLED), which was set, and then asks a function that answered NULL. It now asks the state flag, which is the question it meant. Nothing changes for a conventional ESC: there the flag is only set once the port has opened. The gyro RPM filter was unaffected throughout - it reads getEscTelemetry() directly and never went through this path - which is why the split was easy to miss. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Spektrum state the behaviour in words: "flipping the designated switch reverses motor rotation, throttle will still control motor speed". So on a Smart ESC reverse is a switch and the throttle goes on meaning throttle, and the THRUST REVERSE mode is the whole of it. Reversible motors models the other arrangement, where the stick centre is zero thrust. It was kept as a second route in case an Avian turned out to work that way, but supporting both means the wrong one is always one checkbox away - and enabling it on a switch-type ESC hands it roughly half throttle at the point the pilot expects the motor stopped, which is the worse of the two errors. FEATURE_REVERSIBLE_MOTORS is therefore cleared when the protocol is SRXL2, next to the line that already does the same for brushed, and the mixer-derived trigger is gone from the task. That also removes getReversibleMotorsThrottleState(), which this branch had added and nothing else needed. The documentation now describes the bench check that tells the two kinds of ESC apart, and says plainly that INAV drives only the switch kind. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rebasing onto maintenance-10.x turned up two collisions that were invisible against the older base, both of which would have been silent. Serial function 28 is already spoken for: the Configurator assigns it to the MassZero thermal camera, whose firmware side is not on this branch yet. Two functions sharing a mask bit means assigning either one enables both. Deferring to the number already published is the safe direction; say so if the camera is going somewhere else and this can move back. The mode box had the same problem - BOXTHRUSTREVERSE at 60 with permanent id 69 collided with AUTO SPEED, TERRAIN AGL HOLD and IN FLIGHT MENU, which arrived on this branch after the work started. It is now 63 with permanent id 72, matching the Configurator. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
motorConfig_t was nine bytes in use plus one of tail padding, and the new fields went straight after it, so srxl2ReverseChannel landed on that tenth byte. pgLoad() applies the reset defaults and then copies MIN(stored, current) bytes over them, which for an existing ten-byte record reaches offset nine - and the record holds zero there, because pgResetInstance() copies the reset template whole and a template's padding is zero. The result was the default of channel 7 being replaced by 0 - reverse disabled - on every board that had been configured before this firmware, while a freshly reset board worked. That is the wrong way round for a defect to present: it looks like the feature does not work, and erasing the configuration appears to fix it. A one-byte member now absorbs that overlap so the three real fields start past the end of the old record. Bumping the group version would also have worked, at the price of discarding everyone's motor settings - protocol, rates, pole count - to add an optional field. Verified by layout: the old record is 10 bytes, the group is now 14, and srxl2ReverseChannel moves from offset 9 to 10, past what pgLoad() copies. SITL builds clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Reverse section treated Brake Type as a parenthetical that applied on some models. Spektrum's programming instructions put it the other way round: Brake Type = Reverse is what enables reversing, and Thrust Rev. only chooses the channel that arms it - "Reverse must set in the Brake Type menu". An ESC with the channel set and Brake Type left at Disabled does nothing, and nothing says why, which is the failure this page exists to prevent. Also carries across their warning that the channel must be one nothing else uses, because sharing it "can cause unexpected behavior in flight" - the reason the setting already refuses the throttle slot - and their recommendation of Brake Force 7 with reverse. Documentation only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both additions come from a first session against a real Avian 70 A Smart Lite rather than from the specification. The calibration was described as something INAV offers. It turns out to be something an Avian needs: uncalibrated, that ESC ignored everything below 1238 us, so the bottom quarter of the throttle range did nothing and said nothing. Calibrated, it answered from 1050 us and tracked the commanded throttle to within a point from 10 % upwards. That is worth stating where someone will read it before flying rather than after. The current field is the open one. The ESC reported 2.85 A while the supply feeding it measured 0.999 A at the same instant at about 40 % throttle, which is what motor current rather than pack current looks like through a converter. A scale error in the ESC's sensor would look the same at one operating point, so the page says which reading to prefer meanwhile instead of picking a conclusion the measurement does not yet support. Documentation only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Control Data carried a single channel, the throttle. An Avian parses such a frame - it reports the throttle back over telemetry, correctly - and then refuses to drive the motor for as long as they keep arriving. Once armed by a wider frame it accepts single-channel ones happily, so the requirement is on arming rather than on running: a link that comes up, reports plausible telemetry, and never turns the propeller. Measured on an Avian 70 A Smart Lite. Eight seconds of minimum throttle on one channel leaves the ESC reporting 0.0 % and the bench supply delivering 58 mA, its own electronics and nothing else. The same minimum on eight channels arms it, and 1300 us then gives 30.0 % and 26400 eRPM at 840 mA. Two channels were enough in every combination tried, so the eight here is margin rather than a threshold, chosen to look like what a receiver sends, and widened further when the reverse channel sits above it - that value has to travel in the frame anyway. The unused channels are filled at their minimum. The comment this replaces argued against padding, and its reasoning was right about centre - 1500 us is half throttle on an aircraft ESC, so a wrong guess about which index carries throttle would spin the motor - but wrong in concluding that sending nothing was therefore safer. Sending nothing means never arming. Minimum padding keeps the safety property and drops the one that was costing everything. The mask is now built rather than accumulated, so the frame does not change shape depending on what has been called since power-up. Two things came out of widening it. srxl2SendFrame() now checks there is room and reports whether the frame went out. A port that has backed up drops what does not fit, which for Control Data is of no consequence - another follows in 20 ms and the ESC tolerates 250 ms of silence - but for the broadcast that moves the bus to a new rate it is the difference between a working link and a dead one. srxl2Finalise() only arms the switch if that broadcast was actually queued. This failure mode is not hypothetical: forcing 400000 on an ESC that had not agreed to it left it unreachable on the bench until the battery came out. The oracle harness needed its starved-wire scenario relaxed from three bytes per tick to eight. At thirty-byte frames three bytes a tick is not a slow link but an impossible one - a real 115200 link has eight times the headroom the frame rate needs - and the scenario exists to hold the transmit buffer busy across ticks, which it still does. Adjusting a test to accommodate a change deserves suspicion, so: the two checks that scenario was written for, that the buffer really was busy and that the rate was never raised with bytes unsent, both still pass. 57 checks, 0 failures, 2371 frames accepted by Spektrum's parser. SITL builds clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A bench session against an Avian 70 A, with the ESC's own telemetry as the instrument, corrected four things this driver believed. Telemetry rate. The setting was named for how often INAV asks. The ESC answers about two requests in three and rotates its reply between a text page, a battery page and the ESC page, so what arrives is roughly a ninth of what is asked for: asking 25 times a second yields 2.7 ESC frames a second, 10 yields 1.1, 5 yields 0.5, 2 yields 0.2. The values are now named for what arrives, and 50 Hz is gone - asked on every frame, the ESC keeps the link, keeps answering, and stops obeying the throttle, reporting 0.0 % from every stick position. Every second frame restores it immediately, without a power cycle. A floor in the driver means a configuration saved by an older build cannot select it either. Channel count. The frame carried a block of eight because one channel appeared not to arm. That reading was confounded: the two runs also asked for telemetry at different rates. With the rate held fixed, masks of one, two adjacent, two spread, four and eight channels all gave the same 14.0 % and the same 6610 rpm from the same command; asking on every frame, all five gave nothing. So the frame now carries the throttle and the reverse channel, and nothing else - twelve bytes a frame less on a wire the telemetry reply has to share. Calibration timings. Three seconds high and five low, taken from the published tone timings, sound the tones and store nothing. Four and seven store it. The difference is not subtle: uncalibrated the ESC ignores everything below channel value 12220 and is saturated from 50820, so 41 % of the range does nothing and full power arrives at three quarters stick; calibrated it responds from 2687, saturates at 64307, and reports back what was commanded to within a point across the whole range. Finding an ESC. The polling this driver does finds nothing, and the comment now says so. An Avian announces itself six times in the 300 ms after it powers up and is mute from then on: 128 handshakes to its address, 128 broadcasts, 128 spread across the whole ID range and 319 control frames asking for telemetry all drew exactly nothing from a running one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An Avian announces itself for a third of a second after it gains power and is silent afterwards. Miss that window - a board rebooting under a battery that stayed connected, or a bench supply switched on before the flight controller - and the link never forms. Arming then commands a motor that is not listening: the model arms, the telemetry looks sane, and the propeller does not turn. So arming waits for the link rather than for a timer. Where ESC and board come up on the same battery this costs about a second at power-up and is never noticed; where it does not clear, the throttle would have done nothing anyway, and finding that out while disarmed is the point. Verified both ways in SITL against the real ESC: with the wire idle, arming is refused and the OSD reports the hardware; with the ESC linked, the same configuration arms and drives it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing |
PR Summary by QodoAdd SRXL2 motor output for Spektrum Smart ESCs
AI Description
Diagram
High-Level Assessment
Files changed (25)
|
Code Review by Qodo
1.
|
An automated review pass raised ten issues on this PR. Each was checked against the code before being acted on, and each turned out to be real. **The link timeout was coupled to the telemetry rate, and one setting guaranteed a disconnection.** Nothing but a telemetry reply refreshes the receive timer, because a running Avian never speaks unprompted - so the request rate *is* the link-liveness rate. The slowest setting asked once every 500 ms against a 500 ms timeout. Worse, the ESC answers only about two requests in three, and a dropped link is unrecoverable on this hardware: the driver returns to polling, where a running ESC answers nothing at all. The two slow settings are gone rather than papered over with an elastic timeout, which would have hidden the very failure the timeout exists to catch. What remains is one request every five frames at worst - a reply typically every 150 ms, three consecutive misses still inside the window - and the timeout constant now carries that reasoning. **Telemetry aged out faster than it arrived.** The stale window was one second while the ESC's own readings arrive about once a second at the default rate, so a healthy sensor flickered. Now three seconds; what notices a stopped ESC is the link timeout, which invalidates the reading anyway. **Manual calibration could command full throttle into a live ESC.** The unattended sequence refuses to start with a battery present; `esc_calibrate high` did not, which left the guard off the path a person drives by hand. Boards with no voltage sensing report the pack absent and are unaffected - which is the case that path exists for. **A short handshake was parsed.** Any CRC-valid frame five bytes or longer labelled Handshake had its device ID and baud read from whatever the buffer held last. Length is checked before the payload is touched. **Discovery polled one address.** The comment claimed polling was what finds an ESC with a non-zero unit ID; it only ever addressed 0x40, the one ID that announces itself. It now walks 0x40 to 0x4F. **Thrust reverse never reported active over MSP.** The mode was advertised in initActiveBoxIds() but never packed into the outgoing bitmask, so the Configurator showed it off while the driver was acting on it. **`esc_calibrate` was compiled out of most builds.** Both the command and its table entry sat inside `#ifdef USE_USB_MSC`, so the documented command did not exist on any build without mass storage - SITL included. Verified present there now. **A configuration could select a protocol the firmware cannot drive.** Restoring SRXL2 onto a build without the driver left the motor writer null and escaped the insufficient-outputs check, giving a model that arms and does nothing; it now falls back at boot. Reverse on channel 1 aliases the throttle and the driver refuses it, silently - that setting is now corrected to 0 where the user can see it. **Absent telemetry fields arrived as confident zeros.** The wire has a "no data" code per field and the struct carried one valid flag for the frame, so an ESC that does not report current delivered 0.00 A - indistinguishable from a motor at rest, and believed by the battery estimate. Fields are now tracked individually and a missing one leaves the previous reading in place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks - all ten were real, and all ten are fixed in ac906d2. Two of them were serious, so worth spelling out: Slow telemetry dropping the link (1) was worse than the finding says. Nothing but a telemetry reply refreshes the receive timer, because a running Avian never speaks unprompted - so the request rate is the link-liveness rate. On top of the 25-frame case landing exactly on the timeout, this ESC answers only about two requests in three, so the 10-frame setting could also miss for 600 ms. And a dropped link is unrecoverable here: the driver returns to polling, and a running Avian answers no discovery of any kind (measured: 128 handshakes to its address, 128 broadcasts, 128 spread across 0x40..0x4F, 319 control frames requesting telemetry - zero replies). So the timeout could kill a healthy motor in flight with no way back. I removed the two slow settings rather than making the timeout elastic, which would have hidden the failure the timeout exists to catch. The table now stops at one request every five frames - a reply typically every 150 ms, three consecutive misses still inside the 500 ms window - and the constants carry that reasoning. Related, (5): the stale window was 1 s while the ESC's own readings arrive about once a second, so a healthy sensor flickered; it is 3 s now, with the link timeout doing the job of noticing a stopped ESC. Manual calibration into a live ESC (2) - correct, and it was the worse half: the unattended wizard refused a connected battery and the hand-typed command did not. Guarded now, with boards that cannot sense the pack unaffected, which is the case that path exists for. The rest: handshake length checked before the payload is read (10); discovery now walks 0x40..0x4F instead of only the ID that announces itself, which is what the comment claimed it did (4); The PR description also now carries the full bench data behind the recent changes - the rate table, the mask sweep, the calibration band before and after, and the thrust reverse measurements. |
Upstream took MSP2_INAV_MAG_UNALIGNED at 0x2232 while this branch was out of tree, where it was the SRXL2 status command. A released command keeps its number and an unmerged one has no claim on it, so the SRXL2 pair moves up to 0x2233 and 0x2234 instead. Also closes the last open review thread. Reverse was normalised only against channel 1, the throttle alias the driver refuses; channels 2 to 4 were still accepted and still offered a mode no Smart ESC can act on, since Spektrum document the reverse channel as 5 to 9. The setting's range cannot express "zero, or five to nine", so anything between is corrected to zero at boot - verified by saving 3, 7 and 9 and rebooting: 3 comes back as 0, the other two survive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Rebased onto current That also closes the one review thread still open, (8). My earlier pass only normalised channel 1 - the throttle alias the driver refuses outright - and left 2 to 4 accepted, still advertising a reverse mode no Smart ESC can act on. Since the setting's range cannot express "zero, or five to nine", anything between is now corrected to zero at boot, where it is visible rather than silently ineffective. Verified by saving 3, 7 and 9 and rebooting: 3 comes back as 0, 7 and 9 survive. |
The guide weighed the slow telemetry and then concluded that on an aircraft holding cruise throttle it is adequate. That conclusion was reasoning, presented in the same voice as the measurements around it. The measurement is that rpm arrives 2.7 times a second at the fastest rate the link tolerates, and 1.1 at the default - two to three orders of magnitude below bidirectional DSHOT, with no margin to recover, since asking faster stops the ESC obeying the throttle and it advertises no support for 400000 baud. From that, on a multirotor the filter should be off: a notch updated twice a second is mostly in the wrong place, attenuating signal instead of noise. On a fixed wing at steady throttle it may well be fine, and the text now says that this is an argument rather than a flight result, and suggests logging it both ways. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A telemetry gap used to tear the link down: back to 115200, device ID forgotten,
state to POLLING - which stops sending control data altogether. On this hardware
that is unrecoverable, because a running Avian answers no discovery of any kind,
so a gap of half a second cost the motor for the rest of the flight.
Measured on the bench with the supply as the witness rather than the ESC's own
rpm field, which turned out to freeze at its last value when commutation stops:
- no telemetry requested for 3 s, then for 10 s, with control frames still
going out: current held 0.29 to 0.31 A throughout, against 0.30 before. The
motor never faltered. Telemetry has nothing to do with the throttle.
- no frames at all: current falls to the ESC's own 58 mA within about half a
second, and returns to 0.30 A as soon as frames resume - no re-arm, no power
cycle, the throttle picked up where it was left.
So the timeout was causing the outage it existed to detect. It now lets the
telemetry go stale and keeps driving. The teardown remains for the one case that
needs it: a slave that reset comes back at 115200 and announces for 300 ms, which
cannot be heard from 400000 - so it fires only where the baud was actually
raised, which no Avian tested allows.
Arming keeps the old strictness. With the link no longer torn down, a board whose
ESC was unplugged would sit in RUNNING and arm happily, so srxl2MotorIsConnected()
now asks for a recent reply rather than for the state alone. Strict on the ground,
forgiving in the air: two questions that used to share one answer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An Avian announces itself within 300 ms of gaining power, and is not ready to turn the motor for about five seconds after that: it spends them playing its startup tones, the last of which is what a pilot hears as "connected". Timed on the bench with the announcement as the zero and the tones as the reference. Arming now waits six seconds from the link coming up rather than for the handshake alone - a second past the last tone. Where a person powers the aircraft and then arms it this costs nothing, since nobody arms within six seconds of connecting the battery; where something arms sooner, the alternative was commanding a motor that was not yet listening. It rides on srxl2MotorIsConnected(), which the arming check already consults, so this is one more reason for that predicate to say no rather than a new mechanism. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
d6d11da to
e5e121a
Compare
Six seconds was timed against the tones themselves, which leaves very little margin once the announcement jitters. Commanding throttle exactly on that boundary does start the motor cleanly - measured on the bench, it turns 0.35 s after the command and holds - but half a second of headroom costs nothing on a delay nobody waits for in practice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Still a draft, but no longer an untested one: the driver has now been run against a
real Avian, and links, drives the motor and reads telemetry correctly. What remains
open is listed at the end, and one of those items is a measurement that disagrees
with an assumption rather than a gap.
What this adds
Spektrum's "Smart Throttle" is SRXL2 carried on the ESC's throttle wire: the ESC is
an SRXL2 device and the receiver, or here the flight controller, is the bus master.
The same single wire carries throttle one way and telemetry the other.
INAV can now take that master role, which makes two things available that a PWM
connection cannot:
as a conventional PWM ESC there is no way to command reverse at all.
the throttle wire, feeding the OSD, Blackbox, current estimation and the gyro RPM
filter. There is no telemetry lead to run and no ESC telemetry pad to connect.
The ESC's signal wire goes to a UART TX pin, not to a motor pad, and the port is
opened half duplex.
motor_pwm_protocol = SRXL2selects it. One ESC per port, so amodel with several motors needs a port each; the board refuses to arm if there are
fewer ports than motors, because a motor with no port has no timer output to fall
back on.
Reverse is a mode,
THRUST REVERSE. Spektrum describe the ESC's reverse channel asa switch — "flipping the designated switch reverses motor rotation, throttle will
still control motor speed" — so it maps onto a mode rather than onto the
reversible-motor mixer state, which models a centre-zero stick and would hand the
ESC roughly half throttle where the pilot expects the motor stopped.
FEATURE_REVERSIBLE_MOTORSis therefore cleared for this protocol, next to the linethat already does the same for brushed.
There is also a throttle-range calibration, driven from the Outputs tab or
esc_calibratein the CLI, because a Spektrum ESC learns its endpoints from thesignal present as it powers up and that normally needs a Spektrum transmitter.
Off by default except where flash is plentiful: about 3.5 KB, enabled on H7 and AT32
and on SITL, and any other target can opt in with one line in its
target.h.How it is tested
The master is written from the published specification. Spektrum's library
implements the device side only — the bus master is not part of the open release —
so it is validated against that library, compiled as the counterpart:
inav/src/main/io/motor_srxl2.cis built for the host behind thin stubs and wired toSpektrum's
spm_srxl.cconfigured as a Smart ESC. Two ESCs are simulated on twoports as two independent copies of the library, compiled under renamed symbols,
because raising
SRXL_NUM_OF_BUSESturns it into a hub that forwards between busesand two Avians on two wires do no such thing.
57 checks pass, 2175 frames reach an ESC, none rejected by their parser. Covered:
handshake and baud negotiation, throttle scaling, thrust reverse, telemetry decode,
the whole calibration sequence including every refusal, per-instance isolation on a
twin, a silent ESC on one port of two, and a deliberately starved wire that holds
the transmit buffer busy across ticks — that last one exists because the deferred
baud switch depends on
isSerialTransmitBufferEmpty()answering honestly, and aharness that always claims "drained" never tests it. It found two real defects.
Also verified over MSP against a SITL build of this branch: assigning the port,
setting the protocol, rebooting, and reading back one open port and the mode
registered.
Builds:
TBS_LUCID_H7_WING,AIKONF7(feature compiled out) and SITL with-Werror.Measured against an Avian
Confirmed working on the Avian 70 A Smart Lite, part
SPMXAE70C.The 70 A parts are the same controller and differ only in their battery connector -
SPMXAE70Bis this one with an IC3 where theChas an IC5 - so what was measuredhere holds across them.
Wider than that is expectation rather than measurement, though a well founded one:
SRXL2 is a published protocol, nothing in this driver is written for a particular
model, and any Smart ESC that speaks it should work the same way. The one thing this
bench found that genuinely varies between models is the baud negotiation, and the
driver already takes whatever an ESC advertises instead of assuming - which is
exactly why it survived meeting one that advertises nothing.
The driver was not run on a flight controller for this: the same protocol code was
driven from a host over a single-wire adapter, which is why numbers rather than
impressions appear below.
The link works. The ESC is device
0x40, announces itself unprompted atpower-up exactly as specification 7.2.1 describes for a unit id whose low nibble is
zero, and answers a directed handshake. It has to be met while it is still
announcing: one that has sat powered in silence for a while stops asking and then
ignores handshakes until it is power-cycled.
Telemetry decodes as
STRU_TELE_ESCassumes. 77 requests, 77 replies. Sensorid
0x20, pack voltage0x09A8-> 24.72 V on a 6S, FET temperature0x0168->36.0 C, rpm and current zero at rest. Field order, big-endian packing and every
scale are as written. The
0xFFFF/0xFFno-data path is exercised too, by the BECfields this ESC does not populate.
It relays a second sensor. Smart Battery telemetry, sensor id
0x42, arrivesinterleaved with the ESC's own. The handler already checks that byte before
decoding, so nothing changes - but anything else reading this wire needs to.
The motor runs, and the scale is right. Reported throttle tracks commanded
throttle to within a point from 10 % upwards, with a small dead band below, and rpm
rises monotonically across 25 consecutive readings.
srxl2UsToValue()is nowchecked against what the ESC does with the numbers rather than only against
Spektrum's own parser.
Calibration works, and turns out to be required. Uncalibrated, that ESC ignored
everything below 1238 us - the bottom quarter of the range, silently. Calibrated by
this driver's sequence, it answered from 1050 us.
docs/Spektrum Smart ESC.mdnowsays so where someone will read it before flying. The three-second settle taken from
the published tone timings later turned out to need adjustment - see the update
below.
The receive timeout is real. 200 ms of silence survived, 400 ms did not, and the
link recovered by itself afterwards - consistent with the 0.25 s the manual states,
and twelve times the driver's 20 ms interval. Turnaround from request to reply
measured 0.5 to 1.8 ms.
400000 baud does not exist on this ESC, and forcing it is destructive. It
advertises
baudSupported = 0x00. Sending the broadcast at 400000 anyway leaves itunreachable until the battery is pulled. The driver already cannot do this - the
agreed rate is
SRXL2_BAUD_BIT_400K & baudSupported, which is zero here - and thatline turns out to be the one thing standing between this protocol and a dead link in
flight.
Two collisions this rebase turned up
Both were invisible against the older base and both would have been silent.
Serial function bit 28 is already assigned to the MassZero thermal camera in the
Configurator, whose firmware side is not on this branch. SRXL2 takes 29 instead. Say
so if the camera is going elsewhere and this can move back.
BOXTHRUSTREVERSEcollided with AUTO SPEED, TERRAIN AGL HOLD and IN FLIGHT MENU; itis now 63 with permanent id 72.
Still open
2.85 A while the bench supply feeding it measured 0.999 A at the same instant, at
about 40 % throttle - the relationship expected of a converter, where pack current
is roughly motor current times duty. One operating point cannot separate that from
a plain scale error in the ESC's sensor, so this is stated and not concluded. It
matters only for
current_meter_type = ESC; the documentation says to prefer aboard's own shunt meanwhile. Readings across several throttle settings against an
instrumented supply are the next measurement.
ways: "flipping the designated switch reverses motor rotation, throttle will
still control motor speed" in one place, and "when selected transmitter channel
is positioned between 0 - 100% travel, the motor will run in reverse" in the
programming instructions. The driver assumes the first.
docs/Spektrum Smart ESC.mddescribes the bench check, propeller off.never the reply, and this ESC simply ignores
0x50- 16 queries, no answer, withtelemetry still flowing throughout to prove the link was up. Whatever configures
an Avian is not this packet, so nothing in this PR depends on it.
🤖 Generated with Claude Code
Update, 16 September: a longer bench session, with the numbers
Everything below was read off the ESC's own telemetry on an Avian 70 A, motor
connected except where stated. Several of these exist because the first answer was
wrong, and where an earlier claim in this PR is contradicted the measurement that
replaces it is given in full.
Asking for telemetry on every frame stops the throttle
Fixed 1250 us command, two channels in the frame, one power-up, only the request
rate changing:
The link does not drop: at 50 Hz the ESC answers more often than ever and simply
stops obeying. Both the failure and the recovery happened twice in that single
power-up, so it is the rate and not an accident of ordering. The driver now clamps
to every second frame, and
esc_srxl2_telemetry_rateno longer offers 50 Hz.The rate setting was named for the wrong thing
The ESC answers about two requests in three and rotates its reply between three
sensors, so ESC data arrives at roughly a ninth of the request rate. Measured over
20 s per row:
The setting's values are now named for what arrives:
3HZ,2HZ,1HZ,0_5HZ,0_2HZ, default1HZ- which is the same behaviour the old10HZdefault had.The block of eight channels was a confounded measurement
The earlier reading in this PR - one channel does not arm, eight does - varied the
telemetry rate at the same time. Repeated with the rate held fixed at every third
frame, same 1250 us command, one power-up:
The same five masks with telemetry requested on every frame gave 0.0 % and 0 rpm,
the single-channel mask included - the very mask that had appeared to be the cure.
So the frame now carries the throttle and the reverse channel and nothing else,
which is twelve bytes a frame less on a wire the telemetry reply has to share.
Verified through the driver itself, reading the wire between a SITL build and the
real ESC:
esc_srxl2_reverse_channelch1, ch7ch1, ch9ch1ch1The calibration timings were too short
Three seconds high and five low sound the tones and store nothing. Four and seven
store it. Measured by sweeping the raw channel value and reading back the throttle
the ESC reports, motor disconnected:
Uncalibrated the response is linear but displaced - 0.16 % per microsecond, zero
around 1162 us and full scale around 1787 us - so 41 % of INAV's range does nothing
and full power arrives at about three quarters stick, with nothing to say so.
Calibrated, what the ESC reports tracks what INAV commands across the whole range:
SRXL2_CAL_SETTLE_MSis now 4000 andSRXL2_CAL_LOW_MS7000.Driving it as the mixer does
Values as the driver receives them, after calibration:
mincommand, disarmed)throttle_idle8 %)Worth knowing: uncalibrated, this ESC does not turn at INAV's armed idle at all;
calibrated, it does. Arming goes from a still propeller to a turning one, which is
correct and is the point of
throttle_idle, but it changes on the day thecalibration is done.
Thrust reverse, including with the motor running
Reverse engages from rest and at speed. Engaged at 4490 rpm against an unchanged
1100 us command, the motor changed direction: one telemetry sample caught it at
0 rpm during the transition and the next had it back at the same speed the other
way, with no current step large enough to read at that load. Direction was confirmed
by eye, counter-clockwise and clockwise alternating with the channel, four passes.
Through the flight controller rather than the bench harness: with SITL armed and
THRUST REVERSEactive the configured channel goes to 2000 on the wire and back to1000 when released, alongside an unchanged throttle channel.
Finding an ESC: the polling finds nothing
An Avian announces itself six times in the 300 ms after it gains power and is mute
from then on. Against a running one, with the bus otherwise quiet:
So the link is made at power-up or not at all. A board that reboots under a battery
that stayed connected never links, and arming into that state commands a motor that
is not listening - the model arms, the telemetry looks sane, and the propeller does
not turn.
Arming is therefore refused while the link is missing. Where ESC and board come
up together this clears in about a second and is never seen. Verified both ways
against the real ESC: with the wire idle arming is refused and the OSD reports the
hardware; with the ESC linked the same configuration arms and drives it.
What is still not tested
vary across Avians, so nothing here assumes them.
mixer.cis compiled out under
SITL_BUILD, so the throttle path was exercised against theESC directly and only the frame shape, telemetry and reverse paths were checked
through the firmware.
has been run under load.