Follow our illustrated blog on Embedded Software Architecture

How to build a hybrid solar/wind energy harvester?

Filtering sensor data

Filtering sensor data, 1.0 out of 10 based on 1 rating
VN:F [1.9.22_1171]
Rating: 1.0/10 (1 vote cast)

The ADC is a much used peripheral in embedded systems. This is not different in our project. We extensively use it to read out – for example – voltage and amperage data. Many people are still unaware of the fact that it is still just sampling: One gets a digital value which is the result of a readout – a translation of an analog voltage to a digital number – at a particular moment in time. Room temperature is not something that you expect to jump around a lot in a small time interval, but voltage and current can change quickly, very quickly in short intervals, especially when switching contacts (mosfets) with PWM.

sensor readings in a dynamic environment

What is the battery voltage? In a static environment, this is very simple to determine with an ADC. But it gets complicated when the same question is asked while the battery is being charged with PWM current pulses and a load (a lamp e.g.) is using up some energy from the battery. Without filtering, the measured voltages will jump up and down. Sometimes, one can even temporarily ‘detect‘ 6V or 19V on a 12V battery because of the generated switch pulses and the noise at the measuring point. A lot of parasitic parameters in wiring and system will disturb the accuracy. However, it is still possible to get a reliable battery voltage state when applying filters on the gathered data.


There are different types of filters. Without realizing it, you probably already know very common and simple filter methods such as ‘averaging’, e.g.:

Do not take one sample, but take hundred samples and calculate the average over that.

In fact, the moving average filter is a very simple Finite Impulse Response filter. It could look like this:

void MovAvgInit(MovAvgFilterPtr pF, SDWORD nInitValue, WORD maxitems)
  pF->maxitems = maxitems;
  pF->currentitems = 1;
  pF->avg = nInitValue;

SDWORD MovAvgNext(MovAvgFilterPtr pF, SDWORD nNewValue)
   * MA_at_time_t = MA_at_time_t_minus_1
   * + NewSample_at_time_t
   * - ( MA_at_time_t_minus_1 / Number_of_samples )
   * where MA is the moving average multiplied by the Number of samples,
   * the averaged value is then MA_at_time_t
   * divided by the Number of samples
  if (pF->currentitems < pF->maxitems) {
    pF->avg = pF->avg*pF->currentitems + nNewValue;
  else {
    pF->avg = pF->avg*(pF->currentitems-1) + nNewValue;
  pF->avg = pF->avg / pF->currentitems;
  return pF->avg;

It is simple, but it only takes the history into account over the specified number of samples: Fluctuations that happen over a period longer than the sample window, will result in sudden jumps and dips for the averaged result. For the battery voltage, we take samples every millisecond and average them for every 128 samples (128 milliseconds) which seems more than adequate. It provides a very stable and accurate reading. Moreover, because first, an Infinite Response Filter is applied.


Infinite Response Filters are supposed to be more efficient in implementation, but they might be a little bit more difficult to get it right for the application. The idea remains to smooth out sudden erratic changes in data, but still retain a good and proper response on valid changes and trends.

A fixed point efficient implementation (because of division by shift) could look like this:

void IirInit(IirFilterPtr pF, SDWORD nInitValue, BYTE shift)
  pF->filter = nInitValue;
  pF->shift = shift;

SDWORD IirNext(IirFilterPtr pF, SDWORD nNewValue)
   * F_at_time_t == F_at_time_minus_1 
   *                + FFunction(NewSampleValue - F_at_time_minus_1)
  pF->filter = pF->filter + ((nNewValue - pF->filter) >> pF->shift);
  return pF->filter;

The higher the value of “shift”, the less the influence of the new value into the filter, and the longer (the more iterations) it takes to react to fundamental changes. Filter design can be complicated and requires some experience, but -if possible- trial and error works fine.

VN:F [1.9.22_1171]
Rating: 1.0/10 (1 vote cast)
About is a joined initiative from Gert Boddaert and Pieter Beyens.
More info: about us, our mission, contact us.

Speak Your Mind