How to fix filtered value

Hi how to fix filtered value
In this DC blocking code Serial.println(filtered_value); has zero reading should be similar to Serial.print(readingx);

#include "HX711.h"
const int LOADCELL_DOUT_x = PB4;
const int LOADCELL_SCK_x = PB5;
const int LOADCELL_DOUT_y = PB4;
const int LOADCELL_SCK_y = PB5;
HX711 scalex;
HX711 scaley;
///////////////////
int readingx;
int sample = 0;
int last_sample = 0;
double a = 0;

long shifted_filter = -10000;
//long shifted_filter = -1;
//////////////////
void setup() {
  Serial.begin(115200);
  scalex.begin(LOADCELL_DOUT_x, LOADCELL_SCK_x);
  scaley.begin(LOADCELL_DOUT_y, LOADCELL_SCK_y);
}

void loop() {
  /////////////////
 last_sample = sample;
//a+=0.1; sample = analogRead(PA0);
  a+=0.1; sample = readingx;

  long shiftedFCL = shifted_filter + (long)((sample-last_sample)<<8);
  shifted_filter = shiftedFCL - (shiftedFCL>>8);
  long filtered_value = (shifted_filter+128)>>8;
  ///////////////
  long readingx = scalex.read();
  long readingy = scaley.read();
  
  Serial.print("x = ");
  Serial.print(readingx);

  //  Serial.print(sample);
  Serial.print("  DCx =  ");
  Serial.println(filtered_value); // on monitor is  = 0  ???


}

A guess, sorry, I'm not in the lab.

  long readingx = scalex.read();
  long readingy = scaley.read();

Those are new local variables and do not retain their value across calls to the function they are in.

Declare global variables and lose the long where you read the scale.

Or make them static, in which case they will retain there value

  static long readingx;
  static long readingy;

  long readingx = scalex.read();
  long readingy = scaley.read();

Hope that's it, and all.

a7

this one has a long delay

#include "HX711.h"
const int LOADCELL_DOUT_x = PB4;
const int LOADCELL_SCK_x = PB5;
const int LOADCELL_DOUT_y = PB4;
const int LOADCELL_SCK_y = PB5;
HX711 scalex;
HX711 scaley;
///////////////////

int sample = 0;
int last_sample = 0;
double a = 0;
long shifted_filter = -10000;

//static long readingx;
//static long readingy;

void setup() {
  Serial.begin(115200);
  scalex.begin(LOADCELL_DOUT_x, LOADCELL_SCK_x);
  scaley.begin(LOADCELL_DOUT_y, LOADCELL_SCK_y);
}

void loop() {
  long readingx = scalex.read();
  long readingy = scaley.read();
  /////////////////
  last_sample = sample;
  //a+=0.1; sample = analogRead(PA0);
  a += 0.1; sample = readingx;

  long shiftedFCL = shifted_filter + (long)((sample - last_sample) << 8);
  shifted_filter = shiftedFCL - (shiftedFCL >> 8);
  long filtered_value = (shifted_filter + 128) >> 8;
  ///////////////


  Serial.print("x = ");
  Serial.print(readingx);

  //  Serial.print(sample);
  Serial.print("  DCx =  ");
  Serial.println(filtered_value); // on monitor is  = 0  ???


}

static is giving zeros


void loop() {

static long readingx;
static long readingy;
 // long readingx = scalex.read();
 // long readingy = scaley.read();

Any variable declared outside pf your methods (setup, loop) will keep there value for the life of your program. These are global variables.

Any variables you declare inside your methods (setup, loop) are local variables. Anytime the method ends these variables will lose there value.

If you have a variable declared as a local variable with the same name as a global variable inside a method, the method will refer to the local one.

You have a global variable
Int readingx

In the loop method you have
sample = readingx;
Since the readingx hasnt even been defined yet inside your loop as a local variable (it is further down), it sets sample to the the global readingx

Further down in your loop you have
long readingx = scalex.read();
You have defined a local variable called readingx and given it a value of scalex.read. your not retaining this value as its a local variable

So if you need to retain yhe value of readingx dont redeclare it. Make it

readingx = scalex.read();

1 Like

Those lines with no context do not adequately present your experiment. You commented out something that looks essential, and we can't see where you did that.

Your second complete sketch looks like you are addressing some of the scope issues @theboot has elaborated upon.

What is the roll of a, and where did you get the filter maths?

  a+=0.1; sample = readingx;

  long shiftedFCL = shifted_filter + (long)((sample-last_sample)<<8);
  shifted_filter = shiftedFCL - (shiftedFCL>>8);
  long filtered_value = (shifted_filter+128)>>8;

I suggest printing out intermediate values here, it's easy to imagine those expressions not doing what you think.

a7

Filter is taken from here
https://docs.openenergymonitor.org/electricity-monitoring/ctac/digital-filters-for-offset-removal.html
it is removing DC nicely without delays

int sample = 0;
int last_sample = 0;
double a = 0;

long filtered_value = 0;

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

void loop()
{
  // Generate a test signal
  last_sample = sample;
  //a+=0.1; sample = 512 + sin(a) * 100;
    a+=0.1; sample = analogRead(PA0);

  long n = filtered_value + sample - last_sample;
  filtered_value = ((n<<8)-n)>>8;

  Serial.print(sample);
  Serial.print(' ');
  //Serial.println(filtered_value);
   Serial.println(filtered_value +130);
  delay(1);
}

I don't see explanation for it in linked pages

You wrote

has zero reading should be similar to Serial.print(readingx);

but the filter is to remove DC, one would expect then values to be moving around zero, and be the filtered version of the signal, with DC removed.

But look at your readinx values, they are all over the map and don't look right at all.

I'll let the filter gurus weigh in, but I don't see the need for a high pass filter here, and I don't think you need to write this code for speed over obscurity.

In fact I was going to recommend switching to a "leaky integrator", which the linked pages uses

filtered_value = last_filtered_value + 0.004 * (sample - last_filtered_value);

and is written for speed, no need in the 21st century and this application. It is identically

filtered_value = 0.996 * last_filtered_value + 0.004 * sample;

which is obvsly

mostly the old filtered value, plus a bit of the new sample.

0.996 and 0.004 add up to 1.000, any pair that do can be used and that controls the convergence rate for the filter. Try 0.9 and 0.1 &c.

Please see this post


It's obvsly part of something you aren't doing at all, or anymore. Read the code.

It was written to use an artificially generated signal, and woukd have better been

  last_sample = sample;

  // Generate a test signal
  //a+=0.1; sample = 512 + sin(a) * 100;

  // use a signal from the ADC
    sample = analogRead(PA0);

where you were to uncomment one of the two lines that has an assignment to sample.

HTH

a7

1 Like

That it is for low pass filter

Yes. I said

I'll let the filter gurus weigh in, but I don't see the need for a high pass filter here, and I don't think you need to write this code for speed over obscurity.

I should have said the rest of my remarks concern your use also of a low pass filter, from the linked pages.

I think the leaky integrator low pass filter is what and all you need to do to the raw load cell data in order to get usable stable reading.

I think the linked pages filtering is interesting, but way too optimised to make sense of what is actually happening, compared to simple expressions of the same thing.

Why do you think you need a high pass filter? Why are you trying to remove DC bias?

It's time for you to say what you are trying to accomplish.

Meanwhile, have you started printing values copiously to see if the intermediate expressions make sense? You may have to pull apart some of the complicated ones to get at what all the terms are doing.

Can you post a few dozens line of raw values so we can see what you are dealing with? The ones you printed look like nonsense. A loose wire or something. All over the place.

a7

1 Like

You mean serial monitor readings?

I am thinking to use HX711 for ECG monitor.

What is the rate at which those numbers are piling out?

What is the signal source for those numbers?

a7

Signal generator 50 Hz, sine, serial monitor Serial.begin(115200);

The code from that site just happens to have a fast initial response due to the int math interacting with its simulated data:

int sample = 0;
int last_sample = 0;
double a = 0;

long shifted_filter = -10000;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  // Generate a test signal
  last_sample = sample;
  a+=0.1; sample = 512 + sin(a) * 100;

  long shiftedFCL = shifted_filter + (long)((sample-last_sample)<<8);
  shifted_filter = shiftedFCL - (shiftedFCL>>8);
  long filtered_value = (shifted_filter+128)>>8;

  Serial.print(sample);
  Serial.print(' ');
  Serial.println(filtered_value);
  delay(50);
}

The int math in your first post discards the high bits:

If you promoted sample to long in the site's code, you don't get the apparent fast initial response:

long sample = 0;
...

What is removing the mean is doing the first difference operation on the samples in the right hand term of this line:

If sample included a constant mean offset (anyConstant) then with a first difference it would simplify away as:

   (sample+AnyConstant) -(last_sample+AnyConstant)

it would simplify as:

   sample + AnyConstant -last_sample - AnyConstant
   sample - last_sample + AnyConstant - AnyConstant
   sample - last_sample

The implemented filter seems fraught with problems. What's the magic 128 number in (x+128)>>8 supposed to do?

On way to get a Fast Initial Response with a leaky integrator filter / ExponentiallyWeightedMovingAverage is to initialize the filter with the first observation.

cutely adds 1/2 LSB.

1 Like

removing DC offset

What is the rate at which those numbers are piling out?

You have a 50 Hz signal, but the code is effectively sampling and printing at another totally unrelated frequency.

Printing the value of millis() along with the sampled value would reveal that information. And change it, too, as the rate seems to be determined by time spent doing stuff - more printing, slower sampling.

You could also write code that would sample the input at a known rate.

Why not use something that is more like the signal you want to capture and work with? Even a sine wave at 1 Hz would be more like it.

a7

Enabling the timestamp in Serial Monitor would also illuminate.
Although, the granularity of that timestamp is, at least, irritating.

No. It's not big enough to do that.

I'd agree with this:

The removing of the DC offset in the original codes comes from the offset being conveniently sized at 512<<8 = 0 in site's code's int16_t arithmetic for the first observation, and then by taking the first difference and then by reconstructing the signal summing the series of first differences through the previous step's shifted_filter (effectively initialized at 0). The <<8 and >>8 factors in the operations make the successive operations include 255/256=99.61% of the input change and 0.39% of the prior changes. So you have a very slow low-pass filter tracking the mean, but then only 99.6% of the amplitude of signal.

The source filter is a complicated, fragile, and architecture-dependent mess that likely doesn't work as advertised.

What signal is a 'fixed' filter supposed to deliver?