Butterworth Filter 2 CH

Hi. In this program, when Uxf or Uyf are displayed separately on the serial plotter, they are displayed correctly, but when they are displayed simultaneously, the filtered Uxf and Uyf values ​​are the same, where not filtered Ux Uy are different. What is wrong with the code?

 Ux =  -1145.39  Uxf =  -782.38 Uy =  -409.36  Uyf =  -782.36
 Ux =  -1145.32  Uxf =  -782.36 Uy =  -409.38  Uyf =  -782.36
 Ux =  -1145.46  Uxf =  -782.37 Uy =  -409.43  Uyf =  -782.37
 Ux =  -1145.47  Uxf =  -782.38 Uy =  -409.44  Uyf =  -782.38
 Ux =  -1145.34  Uxf =  -782.39 Uy =  -409.43  Uyf =  -782.40

/*
  //PINS generator 3,12, 13, 15,21,
  4 Tx
  0 comp
  32 0 deg
  33 90 deg

  21 Tx dedtime
  //PINS analog 32,33,35, 36, 39,
  32 detector x
  33 amp x
  35 detector y
  34 amp y
  36 mA
  39 bat
  ADS1220
  23 mosi
  19 miso
  27 csx
  33 csy
*/
#include <Filters.h>
#include <AH/Timing/MillisMicrosTimer.hpp>
#include <Filters/Butterworth.hpp>

#include <ADS1220_WE.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <driver/ledc.h>
#include <Preferences.h>
Preferences preferences;

#define TFT_MOSI 23  // Data out
#define TFT_SCLK 18  // Clock out
#define TFT_CS   22// 
#define TFT_DC    21 //
#define TFT_RST   -1  // pin# 2

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
//================================ ADS  =============================
// Pin definitions
#define SPI_MISO_PIN 19
#define ADS1220_1_CS_PIN    25   // Chip Select
#define ADS1220_1_DRDY_PIN  34   // Data Ready

#define ADS1220_2_CS_PIN    26   // Chip Select
#define ADS1220_2_DRDY_PIN  35   // Data Ready

#define SPI_INITIALIZED true
#define SPI_NOT_INITIALIZED false
////////

/* Create ADS1220 objects */
ADS1220_WE ads1 = ADS1220_WE(ADS1220_1_CS_PIN, ADS1220_1_DRDY_PIN);
ADS1220_WE ads2 = ADS1220_WE(ADS1220_2_CS_PIN, ADS1220_2_DRDY_PIN);


//===============================  end ADS  ======================
void setup() {
  Serial.begin(115200);
  //===========================  ADS  =============================
  while (!Serial);

  /* Choose SPI clock speed here. */
  // ads1.setSPIClockSpeed(4000000); // set SPI clock speed, default is 4 MHz
  // ads2.setSPIClockSpeed(4000000);

  digitalWrite(ADS1220_1_CS_PIN, HIGH);
  pinMode(ADS1220_1_CS_PIN, OUTPUT);
  digitalWrite(ADS1220_2_CS_PIN, HIGH);
  pinMode(ADS1220_2_CS_PIN, OUTPUT);

  if (ads1.init()) {
  }
  if (ads2.init()) {
  }

  ads1.setGain(ADS1220_GAIN_1);
  ads1.setDataRate(ADS1220_DR_LVL_2);
  ads1.setConversionMode(ADS1220_CONTINUOUS);

  ads2.setGain(ADS1220_GAIN_1);
  ads2.setDataRate(ADS1220_DR_LVL_2);
  ads2.setConversionMode(ADS1220_CONTINUOUS);

  float vRef1 = ads1.getVRef_V();
  float vRef2 = ads2.getVRef_V();
  //===========================  end ADS  =============================

  pinMode(22, OUTPUT);

  pinMode(5, INPUT_PULLUP);  //amp ++
  pinMode(17, INPUT_PULLUP);  // amp --
  pinMode(12, INPUT_PULLUP);  //phase ++
  pinMode(14, INPUT_PULLUP);  // phase --
  pinMode(2, INPUT_PULLUP); // sw

  /////////////////
  ledc_timer_config_t timerConfig = {
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .duty_resolution = LEDC_TIMER_10_BIT,
    .timer_num = LEDC_TIMER_0,
    .freq_hz = 15300,
    .clk_cfg = LEDC_AUTO_CLK
  };
  ledc_timer_config(&timerConfig);

  //0°,F1
  ledc_channel_config_t channelConfig1 = {
    .gpio_num = 32,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_0,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 0
  };
  ledc_channel_config(&channelConfig1);

  //90°, F2
  ledc_channel_config_t channelConfig2 = {
    .gpio_num = 33,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_1,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 256
  };
  ledc_channel_config(&channelConfig2);
  //==========================================

  //F3 , Tx GB
  ledc_channel_config_t channelConfig3 = {
    .gpio_num = 4,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_0,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 0
  };
  ledc_channel_config(&channelConfig3);
  //++++++++++++++++++++++++++++++++++++++++

  // F4 comp
  ledc_channel_config_t channelConfig4 = {
    .gpio_num = 0,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_0,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 0
  };
  ledc_channel_config(&channelConfig4);
  //===========================================

  //GB, F4
  ledc_channel_config_t channelConfig5 = {
    //.gpio_num = 32,
    //.gpio_num = 16,
    // .gpio_num = 4,
    .gpio_num = 15,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_0,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 0
  };
  ledc_channel_config(&channelConfig5);

}
///////////////////////////
const double f_s = 100; // Hz
// Cut-off frequency (-3 dB)
const double f_c = 12; // Hz
// Normalized cut-off frequency
const double f_n = 2 * f_c / f_s;

// Sample timer
Timer<micros> timer = std::round(1e6 / f_s);

// Sixth-order Butterworth filter
auto filter = butter<6>(f_n);


////////////////////////////
void loop()
{
  //if (timer)

  ads1.setCompareChannels(ADS1220_MUX_0_1);
  float Ux = ads1.getVoltage_mV();
  //  Ux = ads1.getVoltage_muV();

  ads2.setCompareChannels(ADS1220_MUX_0_1);
  float Uy = ads2.getVoltage_mV();
  // Uy = ads2.getVoltage_muV();


  Serial.print(" Ux =  ");
  Serial.print(Ux + 5 );
  Serial.print("  Uxf =  ");
  Serial.print(filter(Ux));

  Serial.print(" Uy =  ");
  Serial.print(Uy + 5 );
  Serial.print("  Uyf =  ");
  Serial.print(filter(Uy));

  Serial.println();

}

This does not change the value of Ux..
Maybe you mean:

Serial.print(Ux += 5);

Or maybe better:

Ux=Ux+5;
Serial.print(Ux);

5 is to move Ux up to see easier Ux and Uxf.

It seems not only is Uxf = Uyf , but both of them are completely inconsistent with values Ux and Uy.
Looks like you filter outputs random noise. (see below)

Addition
Everything became clear as soon as I looked at the code. For two series of values, you need TWO SEPARATE filters.

Currently, your values ​​Ux and Uy are treated as a single data serie, and the filter produces the average between them.

1 Like

I was using this sample

https://www.google.com/search?q=butterworth+filter+2+channels+arduino&sca_esv=5bde5639e0944a75&sxsrf=AE3TifOTnGbEto

The differences between the two channels only occur in the void() loop

I created separate channels before void loop() and program is working properly.
Thanks for suggestion.

const double fx_s = 100; // Hz
// Cut-off frequency (-3 dB)
const double fx_c = 25; // Hz
// Normalized cut-off frequency
const double fx_n = 2 * fx_c / fx_s;

// Sample timer
//Timer<micros> timer = std::round(1e6 / f_s);

// Sixth-order Butterworth filter
auto filterx = butter<6>(fx_n);

// Sampling frequency
const double fy_s = 100; // Hz
// Cut-off frequency (-3 dB)
const double fy_c = 25; // Hz
// Normalized cut-off frequency
const double fy_n = 2 * fy_c / fy_s;

// Sample timer
//Timer<micros> timer = std::round(1e6 / fy_s);

// Siyth-order Butterworth filter
auto filtery = butter<6>(fy_n);

After adding DC removing filters it is occurred that Ux (green ) is not filtered by LP filter.


#include <Filters.h>
#include <AH/Timing/MillisMicrosTimer.hpp>
#include <Filters/Butterworth.hpp>

#include <ADS1220_WE.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <driver/ledc.h>
#include <Preferences.h>
Preferences preferences;

#define TFT_MOSI 23  // Data out
#define TFT_SCLK 18  // Clock out
#define TFT_CS   22// 
#define TFT_DC    21 //
#define TFT_RST   -1  // pin# 2

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
//================================ ADS  =============================
// Pin definitions
#define SPI_MISO_PIN 19
#define ADS1220_1_CS_PIN    25   // Chip Select
#define ADS1220_1_DRDY_PIN  34   // Data Ready

#define ADS1220_2_CS_PIN    26   // Chip Select
#define ADS1220_2_DRDY_PIN  35   // Data Ready

#define SPI_INITIALIZED true
#define SPI_NOT_INITIALIZED false
////////

/* Create ADS1220 objects */
ADS1220_WE ads1 = ADS1220_WE(ADS1220_1_CS_PIN, ADS1220_1_DRDY_PIN);
ADS1220_WE ads2 = ADS1220_WE(ADS1220_2_CS_PIN, ADS1220_2_DRDY_PIN);


//===============================  end ADS  ======================

//===============================  hp filter  ======================
float inputSignalx;
float filteredSignalx;
float previousInputSignalx;
float previousFilteredSignalx = 0;
float alphax = 0.98; // Adjust for

float inputSignaly;
float filteredSignaly;
float previousInputSignaly;
float previousFilteredSignaly = 0;
float alphay = 0.98; // Adjust for cutoff frequency (closer to 1 for higher cutoff)
//=============================== end hp filter  ======================

void setup() {
  Serial.begin(115200);
  //===========================  ADS  =============================
  while (!Serial);

  /* Choose SPI clock speed here. */
  // ads1.setSPIClockSpeed(4000000); // set SPI clock speed, default is 4 MHz
  // ads2.setSPIClockSpeed(4000000);

  digitalWrite(ADS1220_1_CS_PIN, HIGH);
  pinMode(ADS1220_1_CS_PIN, OUTPUT);
  digitalWrite(ADS1220_2_CS_PIN, HIGH);
  pinMode(ADS1220_2_CS_PIN, OUTPUT);

  if (ads1.init()) {
  }
  if (ads2.init()) {
  }

  ads1.setGain(ADS1220_GAIN_1);
  ads1.setDataRate(ADS1220_DR_LVL_2);
  ads1.setConversionMode(ADS1220_CONTINUOUS);

  ads2.setGain(ADS1220_GAIN_1);
  ads2.setDataRate(ADS1220_DR_LVL_2);
  ads2.setConversionMode(ADS1220_CONTINUOUS);

  float vRef1 = ads1.getVRef_V();
  float vRef2 = ads2.getVRef_V();
  //===========================  end ADS  =============================

  pinMode(22, OUTPUT);

  pinMode(5, INPUT_PULLUP);  //amp ++
  pinMode(17, INPUT_PULLUP);  // amp --
  pinMode(12, INPUT_PULLUP);  //phase ++
  pinMode(14, INPUT_PULLUP);  // phase --
  pinMode(2, INPUT_PULLUP); // sw

  /////////////////
  ledc_timer_config_t timerConfig = {
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .duty_resolution = LEDC_TIMER_10_BIT,
    .timer_num = LEDC_TIMER_0,
    .freq_hz = 15300,
    .clk_cfg = LEDC_AUTO_CLK
  };
  ledc_timer_config(&timerConfig);

  //0°,F1
  ledc_channel_config_t channelConfig1 = {
    .gpio_num = 32,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_0,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 0
  };
  ledc_channel_config(&channelConfig1);

  //90°, F2
  ledc_channel_config_t channelConfig2 = {
    .gpio_num = 33,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_1,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 256
  };
  ledc_channel_config(&channelConfig2);
  //==========================================

  //F3 , Tx GB
  ledc_channel_config_t channelConfig3 = {
    .gpio_num = 4,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_0,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 0
  };
  ledc_channel_config(&channelConfig3);
  //++++++++++++++++++++++++++++++++++++++++

  // F4 comp
  ledc_channel_config_t channelConfig4 = {
    .gpio_num = 0,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_0,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 0
  };
  ledc_channel_config(&channelConfig4);
  //===========================================

  //GB, F4
  ledc_channel_config_t channelConfig5 = {
    //.gpio_num = 32,
    //.gpio_num = 16,
    // .gpio_num = 4,
    .gpio_num = 15,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL_0,
    .timer_sel = LEDC_TIMER_0,
    .duty = 512,
    .hpoint = 0
  };
  ledc_channel_config(&channelConfig5);

}
/*
  ///////////////////////////
  const double f_s = 100; // Hz
  // Cut-off frequency (-3 dB)
  const double f_c = 12; // Hz
  // Normalized cut-off frequency
  const double f_n = 2 * f_c / f_s;

  // Sample timer
  Timer<micros> timer = std::round(1e6 / f_s);

  // Sixth-order Butterworth filter
  auto filter = butter<6>(f_n);
*/
//============================   LP filter  ===================================
//lp x
const double fx_s = 100; // Hz
// Cut-off frequency (-3 dB)
const double fx_c = 12; // Hz
// Normalized cut-off frequency
const double fx_n = 2 * fx_c / fx_s;

// Sample timer
//Timer<micros> timer = std::round(1e6 / f_s);

// Sixth-order Butterworth filter
auto filterx = butter<6>(fx_n);

// lp y
const double fy_s = 100; // Hz
// Cut-off frequency (-3 dB)
const double fy_c = 12; // Hz
// Normalized cut-off frequency
const double fy_n = 2 * fy_c / fy_s;

// Sample timer
//Timer<micros> timer = std::round(1e6 / fy_s);

// Siyth-order Butterworth filter
auto filtery = butter<6>(fy_n);

//============================  eng LP filter  ===================================
void loop()
{
  //if (timer)

  ads1.setCompareChannels(ADS1220_MUX_0_1);
  // float Ux = ads1.getVoltage_mV();
  float  Ux = ads1.getVoltage_muV();

  ads2.setCompareChannels(ADS1220_MUX_0_1);
  // float Uy = ads2.getVoltage_mV();
  float Uy = ads2.getVoltage_muV();

  //===============================  hp filter  ======================
  // Simple IIR high-pass filter (difference equation)
  // This removes the DC component and lets through changes
  inputSignalx = filterx(Ux);
  filteredSignalx = alphax * (previousFilteredSignalx + inputSignalx - previousInputSignalx);
  previousInputSignalx = inputSignalx;
  previousFilteredSignalx = filteredSignalx;

  inputSignaly = filtery(Uy);
  filteredSignaly = alphay * (previousFilteredSignaly + inputSignaly - previousInputSignaly);
  previousInputSignaly = inputSignaly;
  previousFilteredSignaly = filteredSignaly;
  //===============================  end hp filter  ======================
  /*
    Serial.print(" Ux =  ");
    Serial.print(Ux + 5 );
    Serial.print("  Uxf =  ");
    Serial.print(filterx(Ux));

    Serial.print(" Uy =  ");
    Serial.print(Uy + 5 );
    Serial.print("  Uyf =  ");
    Serial.print(filtery(Uy));
  */

  Serial.print("  filteredSignalx =  "); //good, gray
  Serial.print(filteredSignalx);
  Serial.print("  filteredSignaly =  "); // not so good, green
  Serial.print(filteredSignaly);

  Serial.println();

}