Shift what?

Hello guys.I have been on this forum for some time now and i am learning the programming doing different sketches.I found some lines of code in the free code of the Multiwii copter that i am interested in and i do not understand what is going on there.

#define VBAT              // comment this line to suppress the vbat code
#define VBATSCALE     126 // change this value if readed Battery voltage is different than real voltage 
#define VBATLEVEL1_3S 103 // 10,3V

static uint32_t vbatRaw = 0;       //used for smoothing voltage reading
static uint8_t vbat;               //battery voltage in 0.1V steps
static uint8_t buzzerFreq;         //delay between buzzer ring



#if defined(VBAT)
    vbatRaw = (vbatRaw*15 + analogRead(V_BATPIN)*16)>>4; // smoothing of vbat readings  
    vbat = vbatRaw / VBATSCALE;                          // result is Vbatt in 0.1V steps
     
    if (vbat>VBATLEVEL1_3S) {
      buzzerFreq = 0; buzzerState = 0; BUZZERPIN_OFF;
    else
      buzzerFreq = 4;

what i cant understand is after getting the value from the analogRead,what is the >>4 doing there? I know it should be shift right with 4 elements but where?inside the value?The other part of the code i get it all.
Thank you
Daniel

The expression inside the brackets would be evaluated and the result right-shifted.

The >>4 is equivalent to dividing by 16. The previous average is multiplied by 15, the new reading is multiplied by 16 and added, and the result is divided by 16 to produce a new average.

I am still having trouble understanding the equation.So bare with me please and correct me where i am wrong.
Suppose i have the analogRead to 1023(5V to the input pin).
First time i get vbatRaw=0
so ((0 x 15)+(1023 x 16) >>4 that makes (16368)>>4 3
so the shift what does here? it takes the point 4 times to the left? Is the result 1.6368 ?
Thank you for patience and understanding.
Daniel

I suggest that you re-read reply # 4.

ok i understood it is divided again by 16.But with every cycle the value of vbarRaw increase,even if i have constant analogRead of my analog pin.
Is it suppose to be like this or i am still missing something?

I'm not sure why the current reading is multiplied by 16. It doesn't look like it should be. (15 times the previous value + the current value) divided by 16 would yield a new average. Looks like a flaw in that code.

well there still is something spookie in this formula.The real thing is that in testing it works.I have a 5V power supply connected to A0 pin.I defined the VBATSCALE 50 (as it say in 0.1V steps). i can see the analogRead function reading correctly the pin.I have initialized the led on pin 13 and VBATLEVEL1_3S to 45.
With the potmeter if i turn down the supply to less than 4.5Volts on the analog pin the led start blinking.i tryed different voltage levels and it works perfectly.I just dont understand the math under that formula.Still must be something with that shift.

Here is the complete code i am using.Using Analog3 as input on Arduino Pro mini,16Mhz and 5V supply.
Digital 8 as output to my led(the code is using a buzzer but i dont have one).The entire code is working,just i still not understand the math of how it retrieves the voltage in 0.1V steps.I perfectly understand the map function of the arduino but i dont get this.ANy help is appreciated.

#define BUZZERPIN_PINMODE          pinMode (8, OUTPUT);
#define BUZZERPIN_ON               PORTC |= 1<<6;
#define BUZZERPIN_OFF              PORTC &= ~1<<6;

#define VBAT              // comment this line to suppress the vbat code
#define VBATSCALE     50 // change this value if readed Battery voltage is different than real voltage 
#define VBATLEVEL1_3S 45 // 4.5V

#define V_BATPIN 3

static uint32_t currentTime;
static uint32_t vbatRaw = 0;       //used for smoothing voltage reading
static uint8_t vbat;               //battery voltage in 0.1V steps
static uint8_t buzzerFreq;         //delay between buzzer ring
static uint8_t  buzzerState = 0;
static uint32_t buzzerTime;

void setup()
{   
BUZZERPIN_PINMODE  
}
void loop()
{
currentTime = micros();

 #if defined(VBAT)
    vbatRaw = (vbatRaw*15 + analogRead(V_BATPIN)*16)>>4; // smoothing of vbat readings  
    vbat = vbatRaw / VBATSCALE;                          // result is Vbatt in 0.1V steps
     
    if (vbat>VBATLEVEL1_3S) 
    {
      buzzerFreq = 0; buzzerState = 0; BUZZERPIN_OFF;
    }
     else 
        buzzerFreq = 1;
     if (buzzerFreq)
       {
         if (buzzerState && (currentTime > buzzerTime + 250000) )
           {
             buzzerState = 0;BUZZERPIN_OFF;buzzerTime = currentTime;
           }
         else if ( !buzzerState && (currentTime > (buzzerTime + (2000000>>buzzerFreq))) )
           {
             buzzerState = 1;BUZZERPIN_ON;buzzerTime = currentTime;
           }
      }
      #endif
 }

Thank you!
Daniel

You're taking your current average and taking fifteen sixteenths of it, then adding in one sixteenth of your most recent reading.

The generation of the average reading is fine but the line:-
vbat = vbatRaw / VBATSCALE;
Is not going to work given that VBATSCALE = 50, and you have a 5V reference voltage and no potential divider on the input.
However is one of those things is different we need to know so we can explain where it goes right.

At the moment suppose you had an input voltage of 2.5V then the reading (smoothed or not) would be 512. If you then do an integer division by 50 you get 10 not the 25 you say you are getting.

AWOL:
You're taking your current average and taking fifteen sixteenths of it, then adding in one sixteenth of your most recent reading.

That would make perfect sense. But actually, it looks like you're taking your current average and taking fifteen sixteenths of it, then adding in sixteen sixteenths of your most recent reading. The raw reading will continuously increase (until it overflows)- looks like a bug.

The raw reading will continuously increase (until it overflows)

Don't forget this is an integer division we are talking about, so you are not going to accumulate errors like that because an integer division truncated the result.

You are perfectly right Myke.The original code is using a resistor divider to get the voltage of a 12V battery reading.It divides so that at 12.6Volts it gets 5V at analog pin.I just took the part of the code so i could read 5V directly in input.However the code using the divider it works perfectly fine.The VBATSCALE is defined 126 and i use VBATLEVEL_1 to define the voltage i want to monitor,for example 90 if the voltage drop below 9Volt.
But in my case i want to use the same code to measure a real 5V input,and trigger the led if it drops below 3V for example.I know i could already use the map function or something more simple,but i just wanted to understand how is this math used in this equation,especially the sift to the right.
I hope i dont make anyone mad about this,but making all the math into paper get me always to the overflow.

It works like this:-
The analogue to digital converter has a reference voltage which defines the full scale reading. By default this is the 5V supply of the arduino. If you put 5V into the analogue input you get a reading of 1023 "units" therefore each of these units corresponds to :-
5 / 1024 = 4.88 mV (note 1024 because there are 1024 intervals between readings of 0 to 1023)
So if you want to convert this number into a voltage in volts then multiply the reading by 0.00488 or divide by 204.8
However that code uses integer division and that truncates the values.
Supposed you want the units to be 0.1V then divide by 20.48, in practice this will be dividing by 20 and getting a value roughly in 0.1V units.

Hope that helps.

#define BUZZERPIN_PINMODE          pinMode (8, OUTPUT);
#define BUZZERPIN_ON               PORTC |= 1<<6;
#define BUZZERPIN_OFF              PORTC &= ~1<<6;

#define VBAT              // comment this line to suppress the vbat code
#define VBATSCALE     50 // change this value if readed Battery voltage is different than real voltage 
#define VBATLEVEL1_3S 45 // 4.5V

#define V_BATPIN 3

static uint32_t currentTime;
static uint32_t vbatRaw = 0;       //used for smoothing voltage reading
static uint8_t vbat;               //battery voltage in 0.1V steps
static uint8_t buzzerFreq;         //delay between buzzer ring
static uint8_t  buzzerState = 0;
static uint32_t buzzerTime;

void setup()
{   
BUZZERPIN_PINMODE  
}
void loop()
{
currentTime = micros();

 #if defined(VBAT)
    vbatRaw = (vbatRaw*15 + analogRead(V_BATPIN)*16)>>4; // smoothing of vbat readings  
    vbat = vbatRaw / VBATSCALE;                          // result is Vbatt in 0.1V steps
     
    if (vbat>VBATLEVEL1_3S) 
    {
      buzzerFreq = 0; buzzerState = 0; BUZZERPIN_OFF;
    }
     else 
        buzzerFreq = 1;
     if (buzzerFreq)
       {
         if (buzzerState && (currentTime > buzzerTime + 250000) )
           {
             buzzerState = 0;BUZZERPIN_OFF;buzzerTime = currentTime;
           }
         else if ( !buzzerState && (currentTime > (buzzerTime + (2000000>>buzzerFreq))) )
           {
             buzzerState = 1;BUZZERPIN_ON;buzzerTime = currentTime;
           }
      }
      #endif
 }

You put 5V on V_BATPIN and how does vbatRaw do anything but increase until it overflows its 32-bit self?

effectively in terms of vbatRaw you have

vbatRaw = 0;
while (0) {
vbatRaw = (vbatRaw15 + 102316)>>4;
}

Is there something to do with the 15 not being 15UL and the 16 not being 16UL?
Is there some other code that changes vbatRaw?

Hello again guys.Thank you for your time and effort helping me.Thank you Mike,that had more sense to me now about how analog to digital works.
However i think i found why the code is not working singularly.I brake my head all night trying to make it work,but as i told before,the piece of code is taken from a larger part from multiwii forum.Now the battery monitor on the entire code is working great.I just wanted to use that code singularly.
I seen that this code is part of a function that is called every main loop,so probably the vbatRaw is reset to 0 every function call.I hope i am right.
Here is the entire function where the code is implemented.

void annexCode() { //this code is excetuted at each loop and won't interfere with control loop if it lasts less than 650 microseconds
  static uint32_t serialTime;
  static uint32_t buzzerTime;
  static uint32_t calibrateTime;
  static uint32_t calibratedAccTime;
  static uint8_t  buzzerState = 0;
  static uint32_t vbatRaw = 0;       //used for smoothing voltage reading
  static uint8_t buzzerFreq;         //delay between buzzer ring
  uint8_t axis;
  uint8_t prop1,prop2;

  for(axis=0;axis<2;axis++) {
    //PITCH & ROLL dynamic PID adjustemnt, depending on stick deviation
    prop1 = 100-min(abs(rcData[axis]-1500)/5,100)*rollPitchRate/100;
    //PITCH & ROLL only dynamic PID adjustemnt,  depending on throttle value
    if (rcData[THROTTLE]<1500)                               prop2 = 100;
    else if (rcData[THROTTLE]>1499 && rcData[THROTTLE]<2000) prop2 = 100 - (rcData[THROTTLE]-1500) * dynThrPID/500;
    else                                                     prop2 = 100 - dynThrPID;
    dynP8[axis] = P8[axis]*prop1/100*prop2/100;
    dynD8[axis] = D8[axis]*prop1/100*prop2/100;
  }
  
  //YAW dynamic PID adjustemnt
  prop1 = 100-min(abs(rcData[YAW]-1500)/5,100)*yawRate/100;
  dynP8[YAW] = P8[YAW]*prop1/100;
  dynD8[YAW] = D8[YAW]*prop1/100;

  #if defined(VBAT)
    vbatRaw = (vbatRaw*15 + analogRead(V_BATPIN)*16)>>4; // smoothing of vbat readings  
    vbat = vbatRaw / VBATSCALE;                          // result is Vbatt in 0.1V steps
     
    if (vbat>VBATLEVEL1_3S) {
      buzzerFreq = 0; buzzerState = 0; BUZZERPIN_OFF;
    } else if (vbat>VBATLEVEL2_3S)
      buzzerFreq = 1;
    else if (vbat>VBATLEVEL3_3S)
      buzzerFreq = 2;
    else
      buzzerFreq = 4;
    if (buzzerFreq) {
      if (buzzerState && (currentTime > buzzerTime + 250000) ) {
        buzzerState = 0;BUZZERPIN_OFF;buzzerTime = currentTime;
      } else if ( !buzzerState && (currentTime > (buzzerTime + (2000000>>buzzerFreq))) ) {
         buzzerState = 1;BUZZERPIN_ON;buzzerTime = currentTime;
      }
    }
  #endif

  if ( (currentTime > calibrateTime + 100000)  && ( (calibratingA>0 && (nunchukPresent == 1 || accPresent == 1)) || (calibratingG>0) ) ) {  // Calibration phasis
    LEDPIN_SWITCH
    calibrateTime = currentTime;
  } else if ( (calibratingA==0) || (calibratingG==0 && !(nunchukPresent == 1 || accPresent == 1)) ) {
    if (armed) LEDPIN_ON
    else if (calibratedACC == 1) LEDPIN_OFF
  }
  
  if ( currentTime > calibratedAccTime + 500000 ) {
    if ( (nunchukPresent == 1 || accPresent == 1) && (abs(accADC[ROLL])>50 || abs(accADC[PITCH])>50 || abs(accADC[YAW])>400) ) {
      calibratedACC = 0; //the multi uses ACC and is not calibrated or is too much inclinated
      LEDPIN_SWITCH
      calibratedAccTime = currentTime;
    } else
      calibratedACC = 1;
  }
  if (currentTime > serialTime + 20000) { // 50Hz
    serialCom();
    serialTime = currentTime;
  }
  for(axis=0;axis<2;axis++)
    rcCommand[axis]   = (rcHysteresis[axis]-MIDRC) * (rcFactor2 + rcFactor1*square((rcHysteresis[axis]-MIDRC)));
  rcCommand[THROTTLE] = (MAXTHROTTLE-MINTHROTTLE)/(2000.0-MINCHECK) * (rcHysteresis[THROTTLE]-MINCHECK) + MINTHROTTLE;
  rcCommand[YAW]      = rcHysteresis[YAW]-MIDRC;
}

Now i hope this make more sense now,and if does,how can i implement it alone?
Thank you guys again.
Daniel

It works just as AWOL said - each time the code is called, it smooths the readings by taking 15/16 of the current smoothed value and adding a 16th of the new reading. The complication that had been confusing me was the multiplication of the analog reading by 16. vbatRaw maintains a smoothed average, not of the ADC analog reading, but analog reading*16. I assume this is in order to reduce the impact of integer division causing a loss of accuracy. Accuracy is indeed poor for small values of analogread, but in that range, the battery you're measuring is way beyond dead so it doesn't matter. As analogread values rise, the smoothed average tends towards 16 times their value, not quite ever making it, but it does eventually settle at a constant value.

So in short, it works as is.