SERVOx_MAX lower than 2000 when running DShot?

I am running a foam plane with Arduplane 4.6.2 on a Matek H743Mini using MatekH743-bdshot firmware, an AM32-based 45A ESC (Tekko32 F4), 2316 1250kv motor, 4S pack and 9x5 prop. Communication protocol between FC and ESC is DShot. Not bidirectional for now though (I do not get current/voltage readings from the ESC using bidirectional DShot, also not with SERVO_DSHOT_ESC=3)

Now, the combination 4S, 1250kv and the large-ish prop is a bit too much. Thus, I would like to scale the output a little, in all modes including manual and acro.

Setting SERVO1_MAX to a number lower than 2000 does exactly this. If I set this number to 1250 and force arm the plane in manual mode the full throttle stick range outputs 1000-1250 to the ESC, which is the intended behaviour.

However, as soon as the FC is rebooted this SERVO1_MAX number changes back to 2000 again. Is this intended behaviour?

Relevant parameters:

Use TKOFF_THR_MAX or THR_MAX to limit the throttle.

Voltage and current readings will only come back if the ESC supports the telemetry with bidirectional dshot. You should be getting RPM info.

1 Like

THR_MAX works in every flying mode EXCEPT manual (and probably training). This is not what I want since I intend to use manual mode. I know I can also do the manual mode throttle scaling on the transmitter, which is not a preferred solution although it is a solution. Simply using an 8" prop works fine too.

Servo limits work in every mode including manual and takeoff, which is what I want to accomplish.

Setting min and max limits work for actual servos. And it also works for the throttle channel directly after setting the parameter in Mission Planner. It just gets reset to SERVOx_MAX=2000 after a reboot (or now I think about it, after reconnecting Mission Planner).

This differs from the behaviour of regular servo channels and I just wonder if this is intentional behaviour. If it is not this might be a bug. And in that case I might be able to help doing some debugging.

Regarding extended DShot telemetry and AM32: regular bidirectional DShot and AM32 for getting RPM values works fine. It is the EDT extension that does not work with my particular AM32 ESC. I have seen other people having issues with EDT so I don’t want to waste your time on this. I mentioned it to clarify the combination -bdshot firmware and not actually using bdshot.
As a workaround I provided an extra wire from the telemetry output of the ESC to a serial input on the FC and use regular ESC telemetry. This works well, no issues there.

When using Dshot the min max are over ridden to the 1000/2000 settings. So if you’re set on limiting it by the SERVOx_MAX then you should not use Dshot, but just go back to normal PWM. If ESC has a separate telemetry lead then you should still get RPM data.

Honestly for a simple plane there isn’t a lot to gain from Dshot so PWM will probably work just fine.

2 Likes

What we gain from Dshot on a small plane is not performance, but the quality of life features Dshot offer. The beeper, RPM telemetry over the standard 3-pin servoconnector, no ESC calibration required, and the ESC won’t spin the motor until valid Dshot frames arrive.

I will switch to PWM. In this case I only lose the beeper, which is acceptable.

Just curious: why is it that the min/max are overridden when using Dshot but not when using PWM? When SERVOx_MAX is set on a Dshot output once the FC is booted it seems to work as expected. Until it gets overridden again.

1 Like

Just a quick followup for future reference / other people searching.

I have flown many hours with scaling on a DShot throttle channel. Works completely as expected and although 99% of the functionality provided by this script is provided by Arduplane itself it works consistently the same in the FBWA/ACRO/MANUAL modes I am using most.
DShot allows the use of the DShot beeper saving weight, bulk and wiring on small foam planes and in general my AM32 ESCs seems to perform a bit more consistent when driven with DShot.

This with Arduplane 4.7dev. Of course there is no guarantee for the future since doing this on a dshot channel is unsupported.

Code used:
(disclaimer: I am not a good programmer and no lua expert)

local PARAM_TABLE_KEY = 12
assert(param:add_table(PARAM_TABLE_KEY, "BATTSCALER_", 2), 'could not add BATTSCALER_param table')
assert(param:add_param(PARAM_TABLE_KEY, 1,  'TVOLT', 15), 'could not add param TARGETVOLT')

bs_ctr=1000;
bs_thrmax = 1750;

-- Fetch target voltage.
local value = param:get('BATTSCALER_TVOLT')
if value then
  targetvolt = value
else
  error('LUA: get BATTSCALER_TVOLT failed')
end

-- find throttle channel
local thrchannel = SRV_Channels:find_channel(70)
if not thrchannel then
  error('LUA: no throttle channel found' .. tostring(thrchannel))
end

-- Create parameter object for throttle servo 
local THR_SERVO_MAX = Parameter()
THR_SERVO_MAX:init('SERVO' .. tostring(thrchannel+1) .. '_MAX') 
local THR_SERVO_MIN = Parameter()
THR_SERVO_MIN:init('SERVO' .. tostring(thrchannel+1) .. '_MIN') 
local THR_SERVO_TRIM = Parameter()
THR_SERVO_TRIM:init('SERVO' .. tostring(thrchannel+1) .. '_TRIM') 


-- constrain a value between limits
function constrain(v, vmin, vmax)
   if v < vmin then
      v = vmin
   end
   if v > vmax then
      v = vmax
   end
   return v
end

-- periodic function that will be called
function update ()
  local batvolt = battery:voltage(0)
  local new_thrmax = 2000
  if (batvolt > 11) then
    -- Scale throttle max, but keep 50% throttle as the minimum (safeguard)
    new_thrmax = constrain(1000+(1000*(targetvolt/batvolt)), 1500, 2000)
	bs_thrmax = bs_thrmax + ((new_thrmax - bs_thrmax) / 10)
	THR_SERVO_MAX:set(math.floor(bs_thrmax));
	if (arming:is_armed()) then
	  THR_SERVO_MIN:set(1060)
	  THR_SERVO_TRIM:set(1060)
	else
	  THR_SERVO_MIN:set(1000)
	  THR_SERVO_TRIM:set(1000)
	end
  end
  
  bs_ctr = bs_ctr+1
  if (bs_ctr > 400) then
	  bs_ctr = 0
    gcs:send_text(6, "battvoltage " .. tostring(batvolt) .. " targetvolt " .. tostring(targetvolt) .. " thrmax " .. tostring(new_thrmax))
  end
  return update, 250 -- request "update" to be rerun again 100 milliseconds from now
end

return update, 250   -- request "update" to be the first time 500 milliseconds (0.1 second) after script is loaded