arduino uno oversampling (making steering wheel]

hi, i am making a 900° steering wheel for pc using arduino i mount a multiturn potentiometer and all is working but with [u]8 bits[/u] of analog input (value from 0 to 255) the wheel turn is not really smoth (no continues turn) so i need more bits (12 bits or 14 bits). i heard about oversampling but i dont know how it work, so, i need your help thank you.

try this (from the archives)

//
//    FILE: analogReadN.pde
//  AUTHOR: Rob Tillaart
//    DATE: 2012-05-10
//
// PURPOSE: higher precision analogRead()
//
// http://www.atmel.com/Images/doc8003.pdf 
//

void setup()
{
  Serial.begin(115200);

  for (int b=10; b< 17; b++)
  {
    unsigned int val = analogReadN(A0, b);
    Serial.println(val, DEC); 
  }
}

void loop()
{
}

uint16_t analogReadN(uint8_t pin, uint8_t bits)
{
  bits = constrain(bits, 10, 16) -10;

  int samples = 1 << (bits << 1);

  uint32_t sum=0;
  for (int i=0; i< samples; i++) sum += analogRead(pin);     
  return sum >> bits;                             
}

note the number of samples increase quite a bit…

Oversampling is related to the sample rate (the speed at which you sample), which is not an issue here.

I'm not sure what robtillaart's code is doing, but as far as I know the ATmega analog-to-digital converter only has 10-bits. I'd be surprised if you can get better than 10-bit resolution...

but with 8 bits of analog input (value from 0 to 255)

With 10 bits, you can get values between zero and 1023 (if you turn the pot all the way).

With 900 degrees and 1023 counts, that's about 1 count per degree, and that might be enough resolution. The problem is, your 10-turn pot turns 3600 degrees...

There are a couple of solutions (before you resort to an external ADC with higher resolution). You can increase the voltage on the pot, so that 0-900 degrees gives you 0-5V. (You'd might need to protect the Arduino from excessive voltage in case the steering wheel somehow gets turned too far.)

Or, switch to the optional 1.1V ADC reference. The ADC should max-out (1023) at slightly less than 900 degrees giving you slightly less useable rotation, but about 5 times the resolution. in this case, you don't have to worry about protection... You can go over 1.1V (up to 5V) and the ADC will simply stay pegged at 1023.

P.S. If you add a resistor in series with the pot to make a [u]voltage divider[/u], you can make 900 degrees put-out 1.1V, and then you'd get the full 900 degree range.

@DVDdoug: you can read about how to increase the effective ADC resolution by oversampling in this Atmel application note.

It order to make oversampling work, you need to introduce noise to the input. It is best if you can use a synchronous noise so that you can filter it out later with a notch filter. It also is best if the A/D also has good differential non-linearity. If it isn't good it will just add noise. In a real quiet A/D, oversampling usually has no advantage. The data will still step at the same rate but with synchronous noise, such a A/D can often be expanded quite a bit. Do realize that if the noise is at a frequency higher than 1/2 the sampling rate that it will reflect into the sampled data that once acquired can not be removed. Input filtering is a must. The A/D used in the Arduino does us a sample/hold input. This helps to reduce spurious noise but also means that it as a secondary sampling rate to consider that can also add noise from vary high frequencies, such as the processors clock or digital lines running near your inputs. Tinker Dwight

thanks you for all your reply, i will try your solution

DVDdoug: Oversampling is related to the sample rate (the speed at which you sample), which is not an issue here.

I'm not sure what robtillaart's code is doing, but as far as I know the ATmega analog-to-digital converter only has 10-bits. I'd be surprised if you can get better than 10-bit resolution... ...

The oversampling only works if the voltage is stable as it takes samples over a much longer period. From the AVR notes, every bit extra needs 4x as much samples.

So the accuracy in amplitude is "paid for" in duration

10 bit -> 1x ==> ~0.12 millisec (~8KHz) 12 bit -> 16x ==> ~20 millisec. (~500Hz) 16 bit -> 1024x ==> ~ 0.13 seconds. (~7Hz)

The actual resolution can never be better than the accuracy of the highest bit. That is why you can not "cascade" two 8 bit D/A to make one 16 bit D/A.

robtillaart: The oversampling only works if the voltage is stable as it takes samples over a much longer period. From the AVR notes, every bit extra needs 4x as much samples.

So the accuracy in amplitude is "paid for" in duration

10 bit -> 1x ==> ~0.12 millisec (~8KHz) 12 bit -> 16x ==> ~20 millisec. (~500Hz) 16 bit -> 1024x ==> ~ 0.13 seconds. (~7Hz)

Did you read the article posted about over sampling. It only works if you have noise added to the input. You want the input or the reference to have some noise to work. Tinker Dwight

AVR121: Enhancing ADC resolution by oversampling - http://www.atmel.com/Images/doc8003.pdf -

1 Introduction Atmel’s AVR controller offers an Analog to Digital Converter with 10-bit resolution. In most cases 10-bit resolution is sufficient, but in some cases higher accuracy is desired. Special signal processing techniques can be used to improve the resolution of the measurement. By using a method called ‘Oversampling and Decimation’ higher resolution might be achieved, without using an external ADC. This Application Note explains the method, and which conditions need to be fulfilled to make this method work properly.

3.2 Noise To make this method work properly, the signal-component of interest should not vary during a conversion. However another criteria for a successful enhancement of the resolution is that the input signal has to vary when sampled. This may look like a contradiction, but in this case variation means just a few LSB. The variation should be seen as the noise-component of the signal. When oversampling a signal, there should be noise present to satisfy this demand of small variations in the signal. The quantization error of the ADC is at least 0.5LSB. Therefore, the noise amplitude has to exceed 0.5 LSB to toggle the LSB. Noise amplitude of 1-2 LSB is even better because this will ensure that several samples do not end up getting the same value. Criterias for noise, when using the decimation technique: • The signal-component of interest should not vary significantly during a conversion. • There should be some noise present in the signal. • The amplitude of the noise should be at least 1 LSB. Normally there will be some noise present during a conversion. The noise can be thermal noise, noise from the CPU core, switching of I/O-ports, variations in the power supply and others. This noise will in most cases be enough to make this method work. In specific cases though, it might be necessary to add some artificial noise to the input signal.

So yes

plz,
i am using yhis sketch to use arduino as joystick

#undef DEBUG

#define NUM_BUTTONS  40 // you don't need to change this value
#define NUM_AXES  8 // 6 axes to UNO, and 8 to MEGA. If you are using UNO, don't need to change this value.

typedef struct joyReport_t {
int16_t axis[NUM_AXES];
uint8_t button[(NUM_BUTTONS+7)/8]; // 8 buttons per byte
} joyReport_t;

joyReport_t joyReport;

uint8_t btn[12];
int fulloff = 0;
void setup(void);
void loop(void);
void setButton(joyReport_t *joy, uint8_t button);
void clearButton(joyReport_t *joy, uint8_t button);
void sendJoyReport(joyReport_t *report);

void setup() 
{ 
//set pin to input Button 
for( int portId = 02; portId < 13; portId ++ )
{
pinMode( portId, INPUT_PULLUP);
}
Serial.begin(115200);
delay(200);

for (uint8_t ind=0; ind<8; ind++) {
joyReport.axis[ind] = ind*1000;
}
for (uint8_t ind=0; ind<sizeof(joyReport.button); ind++) {
joyReport.button[ind] = 0;
}
}

// Send an HID report to the USB interface
void sendJoyReport(struct joyReport_t *report)
{
#ifndef DEBUG
Serial.write((uint8_t *)report, sizeof(joyReport_t));
#else
// dump human readable output for debugging
for (uint8_t ind=0; ind<NUM_AXES; ind++) {
Serial.print("axis[");
Serial.print(ind);
Serial.print("]= ");
Serial.print(report->axis[ind]);
Serial.print(" ");
}
Serial.println();
for (uint8_t ind=0; ind<NUM_BUTTONS/8; ind++) {
Serial.print("button[");
Serial.print(ind);
Serial.print("]= ");
Serial.print(report->button[ind], HEX);
Serial.print(" ");
}
Serial.println();
#endif
}

// turn a button on
void setButton(joyReport_t *joy, uint8_t button)
{
uint8_t index = button/8;
uint8_t bit = button - 8*index;

joy->button[index] |= 1 << bit;
}

// turn a button off
void clearButton(joyReport_t *joy, uint8_t button)
{
uint8_t index = button/8;
uint8_t bit = button - 8*index;

joy->button[index] &= ~(1 << bit);
}

/* 
* Read Digital port for Button
* Read Analog port for axis
*/
void loop() 
{ 
// Create Matriz with value off switch button 
for(int bt = 1; bt < 13; bt ++)
{
btn[bt]= digitalRead(bt+1);
} 
// Power ON button if it is press. buton 17 at 28
for(int on=01; on < 13; on++)
{
if (btn[on] == LOW)
{
setButton(&joyReport, on+16);
// Serial.println("botao ON");
// Serial.println(on+16);
}else{
fulloff = fulloff++;
} //Power off all Button, if all button are not press
if(fulloff == 13)
{
for(uint8_t cont=0; cont<40; cont++)
{ 
clearButton(&joyReport, cont);
}
// Serial.println("Every buttons is off");
}
}

/* Move all of the Analogic axis */
/* Arduino UNO have only 6 port. Will be necessary set to 0; the not used port */
/* In this example, i will use just 3 axis. X, Y, Z */
/* if you will use all 6 analog ports, set axis < 3, to axis < 5 */
for (uint8_t axis = 0; axis < 3; axis++) {
joyReport.axis[axis] = map(analogRead(axis), 0, 1023, -32768,32767 );
}
//Set to 0; the not used analog port.
//if you will use all 6 analog ports, exclude the lines, axis[3] at axis [5] */
joyReport.axis[3] = 0;
joyReport.axis[4] = 0;
joyReport.axis[5] = 0;
joyReport.axis[6] = 0;
joyReport.axis[7] = 0;
joyReport.axis[8] = 0;



//Send Data to HID
sendJoyReport(&joyReport);

delay(100);
fulloff = 0;
}

can you help me to add smooth function to this sketch

It's probably a good idea to start using code tags

And CTRL-T to auto-indent the code BEFORE copying

Should anyone want to make a white noise generator, here is a way to do it. We used to use this method to determine S/N in receivers and look at bandwidth ( with things like AVC turned on ). It seems that zeners have two modes of operation, one is a knee, which is a soft change from conducting to not conducting. Typically zeners below about 6.8V are knee type action. Above this above, it tends to be avalanche breakdown. This means the voltage will go a little above the zener voltage and will then conduct. The voltage will snap back to the zener voltage. It turns out that if you run a 6.8V zener with a light current, you can have it dancing back and forth, making nice noise. It may need a little amplification for the A/D use. One can play with the bias current to get the maximum amplitude. Using such noise has many purposes. Making truly random numbers is one of them. Another source of such a zener is the reverse bias of the emitter base junction of a silicon transistor. So, should one ever need noise, you now know how to find it. Dwight

hi, i plugged a multi turn pot to arduino(10turn) with a full angle of 3600° which make read of value really bad knowing arduino has only 10bits ADC (3600°-1023) there is anyway to have better resolution like 14 or 16bits?? thanks you

Yes, an external ADC IC.

Many are capable of thousands of samples a second and can be I2C interfaced, direct digital output (which you could use a parallel to serial IC) etc.

how to connect the external ADC?? do i have to enter the code every time i connect arduino to pc?? after that will i just have to use arduino as usual and having better resolution??

lamineovich: how to connect the external ADC??

Totally depends on the ADC... But if I do a quick search on eBay (you could have done the same ;) ) I find for example the ADS1115. Nice and easy breakout board and interfaces with I2C.

lamineovich: do i have to enter the code every time i connect arduino to pc??

What code? If you mean the program code, no. Why would that be any different then a blink a led program?

lamineovich: after that will i just have to use arduino as usual and having better resolution??

Don't understand the question... I you mean you can use analogRead() then no. You have to interface with the external ADC. Mayby/probably there's a library that can help you. Otherwise you have to check the datasheet and if it's a I2C device the Wire library.

thanks man for the last question , i am making a steering wheel for pc and for that i need a 12 bits resolution and i need to update a sketch to arduino before update firmware in order to use arduino as a joystick(with unojoy or megajoy) so,will i be able to do that after adding the external ADC

The whole firmware thing is for the ATmega16U2, the chip that communicates with the computer. It has nothing to do with the ATmega32p which you program with the Arduino IDE. So the program wou make in the Arduino IDE will stay in the arduino even when you switch the ATmega16U2 to UnoJoy mode.