Arduino ADC resolution

Support for Arduino in combination with Air Manager and Air Player

Moderators: russ, Ralph

Message
Author
ChuckK
Posts: 95
Joined: Sun Nov 06, 2016 2:33 pm

Arduino ADC resolution

#1 Post by ChuckK »

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

User avatar
Sling
Posts: 5237
Joined: Mon Sep 11, 2017 2:37 pm
Contact:

Re: Arduino ADC resolution

#2 Post by Sling »

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.

ChuckK
Posts: 95
Joined: Sun Nov 06, 2016 2:33 pm

Re: Arduino ADC resolution

#3 Post by ChuckK »

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.

User avatar
jph
Posts: 2846
Joined: Fri Apr 10, 2020 12:50 pm
Location: Somewhere over the rainbow..

Re: Arduino ADC resolution

#4 Post by jph »

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
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.
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.

ChuckK
Posts: 95
Joined: Sun Nov 06, 2016 2:33 pm

Re: Arduino ADC resolution

#5 Post by ChuckK »

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

ChuckK
Posts: 95
Joined: Sun Nov 06, 2016 2:33 pm

Re: Arduino ADC resolution in Motorized Trim Wheel

#6 Post by ChuckK »

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

User avatar
Sling
Posts: 5237
Joined: Mon Sep 11, 2017 2:37 pm
Contact:

Re: Arduino ADC resolution

#7 Post by Sling »

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.

ChuckK
Posts: 95
Joined: Sun Nov 06, 2016 2:33 pm

Re: Arduino ADC resolution

#8 Post by ChuckK »

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.

User avatar
Keith Baxter
Posts: 4674
Joined: Wed Dec 20, 2017 11:00 am
Location: Botswana

Re: Arduino ADC resolution

#9 Post by Keith Baxter »

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
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 

User avatar
Ralph
Posts: 7878
Joined: Tue Oct 27, 2015 7:02 pm
Location: De Steeg
Contact:

Re: Arduino ADC resolution

#10 Post by Ralph »

I think because it can be easily done with a timer or a hysteresis.

Post Reply