I’ve just finished up building and programming a powered trim wheel for my rig. To run the trim wheel during autopilot usage I do a comparison between The X-Plane trim dataref and the equivalent trim calculated from the position pot tied to the trim wheel. When they get too different I run the trim wheel until the difference is reduced below a threshold, basically minimizing the error between the two.
The arduino has 10 bit (1024) ADC but I’m only seeing about 110 steps in the trim pot values as a move thru the trim limits. This is causing me to have to use a larger error band as explained above resulting in a trim wheel that does not respond to every small trim adjustment only larger trim changes.
I’ve played with the hysteresis and can get a little better performance but still nothing near 1024 steps.
Any thoughts on why I’m not getting full resolution would be appreciated. I’d like to avoid going the message port route to directly read the pot since it would require another arduino.
Thanks
Chuck
Arduino ADC resolution
Re: Arduino ADC resolution
Chuck,
I think the ADC channels may be a bit limited but I haven’t tested this theory. The default is only 50 steps because the default hysteresis is 0.02. To get a 1000 step resolution you would in theory need to set the hysteresis to 0.001. I haven’t tested this so I don’t know how limited this is. Corjan should be able to answer as to how configurable this is using the standard AM API.
I think the ADC channels may be a bit limited but I haven’t tested this theory. The default is only 50 steps because the default hysteresis is 0.02. To get a 1000 step resolution you would in theory need to set the hysteresis to 0.001. I haven’t tested this so I don’t know how limited this is. Corjan should be able to answer as to how configurable this is using the standard AM API.
Air Manager panels at https://www.experimentalsimavionics.com
Youtube Channel https://www.youtube.com/channel/UC8ZqXX ... kfZMq5BKig
Air Manager API Tutorial Video Series https://youtube.com/playlist?list=PLNr0 ... baT4gJKg5D
Youtube Channel https://www.youtube.com/channel/UC8ZqXX ... kfZMq5BKig
Air Manager API Tutorial Video Series https://youtube.com/playlist?list=PLNr0 ... baT4gJKg5D
Re: Arduino ADC resolution
I somewhat figured that’s how it worked. I did install a very high end Bournes pot expecting this. I will start reducing the hysteresis and report back on my findings.
Re: Arduino ADC resolution
Hi Chuck, using a pot with trim wheel is always a bit of a nightmare. It is far better to use an encoder for the trim wheel so that each step can be treated as individual actions.ChuckK wrote: ↑Sun Dec 27, 2020 2:26 am I’ve just finished up building and programming a powered trim wheel for my rig. To run the trim wheel during autopilot usage I do a comparison between The X-Plane trim dataref and the equivalent trim calculated from the position pot tied to the trim wheel. When they get too different I run the trim wheel until the difference is reduced below a threshold, basically minimizing the error between the two.
The arduino has 10 bit (1024) ADC but I’m only seeing about 110 steps in the trim pot values as a move thru the trim limits. This is causing me to have to use a larger error band as explained above resulting in a trim wheel that does not respond to every small trim adjustment only larger trim changes.
I’ve played with the hysteresis and can get a little better performance but still nothing near 1024 steps.
Any thoughts on why I’m not getting full resolution would be appreciated. I’d like to avoid going the message port route to directly read the pot since it would require another arduino.
Thanks
Chuck
The trim wheel should ideally be continuous (as it would be in a stepper situation). Yet the trim INDICATOR read from the data ref should drive the hardware indicator (the position needle) - this ideally should always be read from the sim - never written to. - apart from on start up to set the desired position -
The resolution of the ADC on a normal arduino is a LOT more than your findings and I can only attribute that to the AM program setup (which is probably to do with the read timing and the requirements of AM to not be overloaded with inputs.) . This would be a classic case of using message port where you 'may' not have this issue at all - as far as resolution - but you MAY have an issue with message port update speed - it is a suck it and see and only keep data to a minimum and get rid of extraneous data - such as a pot fluctuation reading.. this can be helped - as you say - by hysteresis or by averaging, or preferably median averaging filtering - whereas an encoder can be controlled. It also then, of course, depends on the resolution of the dataref for the trim wheel - which I havent looked at. If the res of the dataref is high, then you may well hit a wall between the update speed of AM - via normal or message port and the speed of the data feed / resolution - even after filtering compared to the speed of read of AM... which certainly does not appear constant - or, able to catch every input due to loading and buffering.
I suspect that what you are seeing is possibly the inability of AM to keep up with the input data ??? - or, a limitation within AM to avoid overloading when using a pot due to normal fluctuations - hence a very course hysteresis.
imho - a pot is certainly NOT the way to go with trim wheel.
During AP operation, the dataref for the trim indicator (a servo or whatever) is read and the indicator moves as per AP. When AP is off, the encoder for the trim wheel references the dataref for the trim position indicator and adjusts it position reference - ie - it uses the final ref as its count point.
Mostly, ALL arduino / arduino programmable board ADC's are really good. The only one that is total crap is the STM32 ! - avoid this like the plague. it is utterly captain cooked.
Joe.
Joe. CISSP, MSc.
Re: Arduino ADC resolution
Thanks Joe for your insight. You are correct in that the implementation you suggest is the most straight forward. This is similar to Ian's solution from bffsimulation.com.
I am however using real life Piper hardware where the trim indicator is mechanically tied to the wheel. The wheel permits 6 full revolutions stop to stop. So if I used an encoder I would need a home signal. I have the set geared where the 6 turns of the wheel yields 9.33 turns of the 10 turn pot so I should be able to get the needed resolution. I'll report back later today after some testing.
Chuck
I am however using real life Piper hardware where the trim indicator is mechanically tied to the wheel. The wheel permits 6 full revolutions stop to stop. So if I used an encoder I would need a home signal. I have the set geared where the 6 turns of the wheel yields 9.33 turns of the 10 turn pot so I should be able to get the needed resolution. I'll report back later today after some testing.
Chuck
Re: Arduino ADC resolution in Motorized Trim Wheel
I did some experimenting with the hysteresis settings and the error sensitivity setting in the code. With the hysteresis set at .001 the callback was firing continuously, which is surprising for a high quality (45USD) pot. With it set to .002, I'm indeed seeing 512 steps, and with an error band of .005 I get oscillation free motion without missing any AP trim commands. All is well. I've attached the code and renamed the thread for future reference.
Code: Select all
local gbl_ap_mode = 0
local gbl_bus_volts = 0.0
local gbl_trim_sw = false
local gbl_xp_trim = 0.0
local gbl_pot_val = 0.0
local err_band = 0.005
function trim_timer() -- this runs 10x per second, and moves the motor when AP is engaged
if gbl_ap_mode == 2 then
pot_val = hw_adc_input_read(elev_pot_id) --get the current elev pot value
mech_trim = pot2trim(pot_val) --converts the current trim pot value to mechanical trim value
trim_err = mech_trim - gbl_xp_trim --this is how far off the trim wheel is off from the XP trim
--print("ERR: " .. trim_err)
if trim_err > err_band then
hw_output_set(sleep_id, true) --wake up the stepper driver
hw_stepper_motor_position(trim_stepper_id, nil, "ENDLESS_COUNTERCLOCKWISE")
elseif trim_err < -err_band then
hw_output_set(sleep_id, true) --wake up the stepper driver
hw_stepper_motor_position(trim_stepper_id, nil, "ENDLESS_CLOCKWISE")
else
hw_stepper_motor_position(trim_stepper_id, nil, "STOP")
--hw_output_set(sleep_id, false) --sleep the stepper driver
end
end
if gbl_xp_trim >= 0.98 or gbl_xp_trim <= -0.98 then
hw_stepper_motor_position(trim_stepper_id, nil, "STOP")
end
end
timer_start(0, 100, trim_timer) --this will check to trim error 10 times per second
function new_trim(xp_trim, ap_mode, bus_volts)
gbl_xp_trim = xp_trim
if gbl_ap_mode == 2 and ap_mode == 0 then -- this stops the stepper motor when coming off autopilot
hw_stepper_motor_position(trim_stepper_id, nil, "STOP")
hw_output_set(sleep_id, false) --sleep the stepper driver
end
gbl_ap_mode = ap_mode
gbl_bus_volts = bus_volts[1]
end
function new_elev_pot(pot_val)
--pot_val_int = math.floor(pot_val*1024)
--print("ELEV: " .. pot_val_int)
gbl_pot_val = pot_val
if gbl_ap_mode == 0 then -- moving the trim wheel manually or by the trim switches
val = pot2trim(pot_val)
xpl_dataref_write("sim/flightmodel/controls/elv_trim", "FLOAT", val)
end
end
function new_rud_pot(pot_val)
--print("RUD: " .. pot_val)
pot_min = .325
pot_max = .693
trim = ((2/(pot_max - pot_min))*pot_val) - (((2/(pot_max-pot_min))*pot_max)-1) -- solves y=mx+b
trim = var_cap(trim, -1, 1)
xpl_dataref_write("sim/flightmodel/controls/rud_trim", "FLOAT", trim)
end
function trim_up_pressed()
if gbl_bus_volts >= 18.0 and gbl_ap_mode ~= 2 then
hw_output_set(sleep_id, true) --wake up the stepper driver
gbl_trim_sw = true
hw_stepper_motor_position(trim_stepper_id, nil, "ENDLESS_CLOCKWISE")
elseif gbl_ap_mode == 2 then
xpl_dataref_write("sim/cockpit/autopilot/autopilot_mode", "INT", 0) -- disconnect ap if trim switch is pressed
end
end
function trim_up_released()
if gbl_bus_volts >= 18.0 then
gbl_trim_sw = false
hw_stepper_motor_position(trim_stepper_id, nil, "STOP")
hw_output_set(sleep_id, false) --sleep the stepper driver
end
end
function trim_down_pressed()
if gbl_bus_volts >= 18.0 and gbl_ap_mode ~= 2 then
hw_output_set(sleep_id, true) --wake up the stepper driver
gbl_trim_sw = true
hw_stepper_motor_position(trim_stepper_id, nil, "ENDLESS_COUNTERCLOCKWISE")
elseif gbl_ap_mode == 2 then
xpl_dataref_write("sim/cockpit/autopilot/autopilot_mode", "INT", 0) -- disconnect ap if trim switch is pressed
end
end
function trim_down_released()
if gbl_bus_volts >= 18.0 then
gbl_trim_sw = false
hw_stepper_motor_position(trim_stepper_id, nil, "STOP")
hw_output_set(sleep_id, false) --sleep the stepper driver
end
end
xpl_dataref_subscribe("sim/flightmodel/controls/elv_trim", "FLOAT",
"sim/cockpit/autopilot/autopilot_mode", "INT",
"sim/cockpit2/electrical/bus_volts", "FLOAT[6]", new_trim)
elev_pot_id = hw_adc_input_add("ARDUINO_NANO_D_A6", 0.002, new_elev_pot)
rud_pot_id = hw_adc_input_add("ARDUINO_NANO_D_A7", 0.002, new_rud_pot)
sleep_id = hw_output_add("ARDUINO_NANO_D_D2", false)
trim_stepper_id = hw_stepper_motor_add("VID66-06", 800, 15, true, "ARDUINO_NANO_D_D4", "ARDUINO_NANO_D_D3")
trim_up_id = hw_button_add("ARDUINO_MEGA2560_C_D17", trim_up_pressed, trim_up_released)
trim_down_id = hw_button_add("ARDUINO_MEGA2560_C_D18", trim_down_pressed, trim_down_released)
function pot2trim(val)
pot_min = .04
pot_max = .96
trim = ((2/(pot_max - pot_min))*val) - (((2/(pot_max-pot_min))*pot_max)-1) -- solves y=mx+b
trim = var_cap(trim, -1, 1)
return trim
end
-- function GetTrimError()
-- cur_trim = pot2trim() --gets and converts the current trim pot value to xp trim value
-- err = cur_trim - gbl_xp_trim --this is how far off the trim wheel is off from the XP trim
-- return = err
-- end
Re: Arduino ADC resolution
Glad you got something working you are happy with.
For this type of situation and others where the dataref is continually updating I wonder if an update rate argument could be implemented to help us limit the callbacks. We can of course apply some filtering of our own, perhaps in hardware in this case and in code. Doing it in code though will not limit the number of callbacks.
For this type of situation and others where the dataref is continually updating I wonder if an update rate argument could be implemented to help us limit the callbacks. We can of course apply some filtering of our own, perhaps in hardware in this case and in code. Doing it in code though will not limit the number of callbacks.
Air Manager panels at https://www.experimentalsimavionics.com
Youtube Channel https://www.youtube.com/channel/UC8ZqXX ... kfZMq5BKig
Air Manager API Tutorial Video Series https://youtube.com/playlist?list=PLNr0 ... baT4gJKg5D
Youtube Channel https://www.youtube.com/channel/UC8ZqXX ... kfZMq5BKig
Air Manager API Tutorial Video Series https://youtube.com/playlist?list=PLNr0 ... baT4gJKg5D
Re: Arduino ADC resolution
Agreed that would be a nice improvement. That is why I only assign the global variables in the dataref callback. Try to keep is as light as possible. The heavier lifting is limited to 10Hz.
- Keith Baxter
- Posts: 4685
- Joined: Wed Dec 20, 2017 11:00 am
- Location: Botswana
Re: Arduino ADC resolution
Hi
I asked for that here and seemingly it got turned down. Not sure if it was for the reasons I gave though. Maybe Corjan will have a rethink.
viewtopic.php?f=9&t=3900&p=30627#p30623
Keith
I asked for that here and seemingly it got turned down. Not sure if it was for the reasons I gave though. Maybe Corjan will have a rethink.
viewtopic.php?f=9&t=3900&p=30627#p30623
Keith
AMD RYZEN 9 5950X CPU, Corsair H80I cooler, ASUS TUF GAMING B550-PLUS AMD Ryzen Mother Board, 32Gb ram Corsair Vengeance 3000Mh, MSI GTX960 4G graphics card
Re: Arduino ADC resolution
I think because it can be easily done with a timer or a hysteresis.