从具有相同地址的 i2c 传感器读取值

Read values from i2c sensors with the same address

我有 4 个 SingleTact 电容式传感器,每个传感器的 i2c 地址都是 0x04。我想找到传感器的平均值,以制作操纵杆。但是我不确定如何为每个传感器分配它自己的地址,因为它们都具有相同的地址,因为它们是相同的传感器。我有一个初始代码,但这只适用于一个传感器,因为它只有一个 i2c 地址字节。我使用在线教程将所有 SDA 和 SCL 线连接在一起,并包含上拉电阻。

#include <Wire.h>
#define initializetime 4
byte serialToPCBuffer[77];
byte serialToPCBufferIndex = 0;

int data[4];
int databuffer[4][initializetime] = {0,0,0,0,0,0,0,0,0,0,0,0};
int base[4] = {0,0,0,0};
int ArduinoToPCBuffer[4] = {1000,2000,3000,4000};
byte outgoingI2CBuffer[32];
unsigned long timeStamp_;

void setup() {
  int i;
  Wire.begin();
  //TWBR = 12;
  Serial.begin(57600);
  Serial.flush();
  initializeSensors();

  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println("PPS UK: SingleTact sensor value in PSI. \n(resembles PC executable display)");
  Serial.println("Refer manual for any other calculation.");
  Serial.println("----------------------------------------");  
}    

void loop(){
  byte i2cAddress = 0x04; // Slave address (SingleTact), default 0x04
  int data = readDataFromSensor(i2cAddress);
  Serial.print("I2C Sensor Data:");
  Serial.print(data);    
  Serial.print("\n");
  delay(100); // Change this if you are getting values too quickly 
}

int readDataFromSensor(int address)
{
  byte i = 0;
  byte i2cPacketLength = 6;
  byte outgoingI2CBuffer[3];
  byte incomingI2CBuffer[6];

  outgoingI2CBuffer[0] = 0x01;
  outgoingI2CBuffer[1] = 128;
  outgoingI2CBuffer[2] = i2cPacketLength;

  Wire.beginTransmission(address);
  Wire.write(outgoingI2CBuffer,3);
  byte error = Wire.endTransmission();
  if (error != 0) return -1;
  Wire.requestFrom(address,i2cPacketLength);

  int incomeCount =0;
  while(incomeCount < i2cPacketLength)
  {
    if(Wire.available())
    {
    incomingI2CBuffer[incomeCount] = Wire.read();
    incomeCount++;
    }
    else
    {
    delay(1);
    }
  }
  if(serialToPCBuffer[4] == 0x00 && serialToPCBuffer[5] == 0xFE)
  {
    serialToPCBuffer[5] = 0xFF;
  }

  int datafromi2c = serialToPCBuffer[4]*256+serialToPCBuffer[5]-base[address-5];

  if(datafromi2c<21)
    datafromi2c = 0;

  return datafromi2c;
}

void initializeSensors()
{
  for(int k = 0;k<4;k++)
  {
    databuffer[k][0] = readDataFromSensor(k+5);
    delay(10);
    databuffer[k][1] = readDataFromSensor(k+5);
    delay(10);
    databuffer[k][2] = readDataFromSensor(k+5);
    delay(10);
    databuffer[k][3] = readDataFromSensor(k+5);
    delay(10);
    base[k] = (databuffer[k][0] + databuffer[k][1] + databuffer[k][2] +     databuffer[k][3])/3;
  }
}

感谢任何建议。

您需要使用卡尔曼滤波器

    #include "Wire.h"                      // i2c library
    #include "BMP085.h"                   // bmp085 library, download from url link (1)
    #include "Tone.h"                      // tone library, download from url link (3)
    #include "stdlib.h"                    // we need that to use dtostrf() and convert float to string
    #include "stdarg.h"

    #define UART_SPEED  9600
    short SPEAKER_PIN1 = 11;               // Speaker output -
    short SPEAKER_PIN2 = 12;               // Speaker output +
    short LED_PIN = 13;

    Tone speaker1, speaker2;
    BMP085   bmp085 = BMP085();            // BMP085 sensor

    const float SEA_LEVEL_PRESSURE = 101325;    // Pressure at sea level (Pa)
    const float KF_VAR_MEASUREMENT = 0.1;       // Variance of pressure measurement noise.
    const float KF_VAR_ACCEL = 0.75;             // Variance of pressure acceleration noise input.

    float CLIMB_TONE2_MULT;
    float SINK_TONE2_MULT;

    float   kf_x_abs,
            kf_x_vel,
            kf_p_abs_abs,
            kf_p_abs_vel,
            kf_p_vel_vel,
            kf_var_accel;

    #define VARIOS_LEN  5
    int varios[VARIOS_LEN];
    int varios_pos = 0, varios_sum = 0;

    void p(char *fmt, ... ){
        char tmp[128]; // resulting string limited to 128 chars
        va_list args;
        va_start (args, fmt );
        vsnprintf(tmp, 128, fmt, args);
        va_end (args);
        Serial.print(tmp);
    }

    void kf_reset(float abs_value, float vel_value) {
        kf_x_abs = abs_value;
        kf_x_vel = vel_value;
        kf_p_abs_abs = 1000000000;
        kf_p_abs_vel = 0;
        kf_p_vel_vel = KF_VAR_ACCEL;
        kf_var_accel = KF_VAR_ACCEL;

        varios_sum = 0;
        for (int i = 0; i < VARIOS_LEN; i++) varios[i] = 0;
        varios_pos = 0;
    }

    void setup() {
        Serial.begin(UART_SPEED);            // set up arduino serial port
        Wire.begin();                        // lets init i2c protocol
        speaker1.begin(SPEAKER_PIN1);        // piezo speaker output -
        speaker2.begin(SPEAKER_PIN2);        // piezo speaker output +
        digitalWrite(SPEAKER_PIN2, LOW);

        bmp085.init(MODE_ULTRA_HIGHRES, SEA_LEVEL_PRESSURE, false);

        kf_reset(SEA_LEVEL_PRESSURE, 0);

        CLIMB_TONE2_MULT = pow(2, 9/12);
        SINK_TONE2_MULT = pow(2, 1/12);

        welcome();      //everything is ready, play "welcome" sound
    }

    void welcome() {
        speaker1.play(300, 50);     // (note, duration)
        delay(100);
        speaker2.play(300, 50);     // (note, duration)
        delay(100);
        Serial.println("Vario is ready");
    }

    float pressure2altitude(float pressure) {
        return (float)44330 * (1 - pow(((float)(pressure)/SEA_LEVEL_PRESSURE), 0.190295));
    }

    float last_time = 0;
    void update_pressure() {
        long pressure;
        bmp085.calcTruePressure(&pressure);

        float time = millis();
        float dt = (time - last_time) / 1000;
        last_time = time;

        /* Kalman Filter code */
        kf_x_abs += kf_x_vel * dt;

        kf_p_abs_abs += (float)2 * dt * kf_p_abs_vel  +  dt * dt * kf_p_vel_vel  +  kf_var_accel * dt * dt * dt * dt / (float)4;
        kf_p_abs_vel +=                                       dt * kf_p_vel_vel  +  kf_var_accel * dt * dt * dt / (float)2;
        kf_p_vel_vel +=                                                          +  kf_var_accel * dt * dt;

        // Update state covariance. The last term mixes in acceleration noise.
        float y = pressure - kf_x_abs;                              // Innovation.
        float s_inv = 1.0 / (kf_p_abs_abs + KF_VAR_MEASUREMENT);    // Innovation precision.
        float k_abs = kf_p_abs_abs * s_inv;                         // Kalman gain
        float k_vel = kf_p_abs_vel * s_inv;

        // Update state estimate.
        kf_x_abs += k_abs * y;
        kf_x_vel += k_vel * y;

        // Update state covariance.
        kf_p_vel_vel -= kf_p_abs_vel * k_vel;
        kf_p_abs_vel -= kf_p_abs_vel * k_abs;
        kf_p_abs_abs -= kf_p_abs_abs * k_abs;
    }

    int avg_vario() {
        float altitude = pressure2altitude(kf_x_abs);
        int vario = (int)((altitude - pressure2altitude(kf_x_abs - kf_x_vel)) * 100);

        varios_sum += vario;
        varios_sum -= varios[varios_pos];
        varios[varios_pos] = vario;

        if (++varios_pos == VARIOS_LEN) varios_pos = 0;
        return varios_sum / VARIOS_LEN;
    }


    int CLIMB_RATE_START = 25,
        SINK_RATE_START  = -80;

    int loop_id = 0;
    unsigned long next_signal_time = 0;
    void loop() {
        update_pressure();
        int vario = avg_vario();

        unsigned long time = millis();
        if (time >= next_signal_time) {
            if (vario > CLIMB_RATE_START) {
                long beep_period = 350 - vario / 2;
                if (beep_period < 20) beep_period = 20;

                int silence_period = beep_period / 16;
                int tone = 1300 + vario;
                if (tone > 2300) tone = 2300;

                next_signal_time = time + beep_period + silence_period;
                speaker1.play(tone, beep_period);

                Serial.print("CLIMB  beep:");
                Serial.print(beep_period);
                Serial.print("    silence:");
                Serial.print(silence_period);
                Serial.print("    vario: ");
                Serial.println(vario);
            } else if (vario < SINK_RATE_START) {
    //            int beep_period = 350 * 50 / (-vario);
    //            int silence_period = beep_period / 5;
                int beep_period = 350 + vario / 2;
                if (beep_period < 20) beep_period = 20;
                int silence_period = beep_period / 16;
                int tone = 1000 + vario;
                if (tone < 300) tone = 300;

                next_signal_time = time + beep_period + silence_period;
                speaker1.play(tone, beep_period);     // (note, duration)

                Serial.print("SINK  beep:");
                Serial.print(beep_period);
                Serial.print("    silence:");
                Serial.print(silence_period);
                Serial.print("    vario: ");
                Serial.println(vario);
            }
        }

        loop_id++;
        if ((loop_id % 10) == 0) {
            Serial.print("vario: ");
            Serial.println(vario);
        }

        if ((loop_id % 10) == 0) {
            digitalWrite(LED_PIN, LOW);
        }
        if ((loop_id % 10) == 5) {
            digitalWrite(LED_PIN, HIGH);
        }
    }

您应该阅读此设备的手册,可用 here。它说,在界面描述中,

Multiple sensor interfaces may be connected to a single I2C bus. The bus address of individual sensor interfaces can be configured by writing desired address value (4 to 127) via the I2C interface to register address 0 with an I2C Write Operation. Change of individual sensor I2C addresses is supported by the PC and Arduino Example.

所以你只需要

  1. 插入第一个传感器
  2. 将地址(例如 0x41)写入此设备的寄存器 0
  3. 拔下传感器
  4. 对使用不同地址的所有传感器重复 1-2-3

然后每个传感器都会回复你设置的地址。

请注意

As the interface board will always respond to address 0x04 then this address must be considered reserved for SingleTact. Where multiple SingleTact interfaces are to be connected to the same I2C bus then address 0x04 must be considered invalid

因此,即使在这种情况下,RTFM 建议也是最重要的...

您还可以使用 I2C 多路复用器。它们有自己的 I2C 地址,可以在自己的总线(广播域)上枚举四个传感器,以便可以在它们之间切换。您的程序需要依次明确 select 每个传感器并跟踪它正在读取的传感器。切换后,I2C 流量直接通过 selected 设备。这最适用于星形拓扑中的一组传感器,因为您需要 运行 额外布线。

我要走得更远,说你甚至可以使用继电器来切换这些进出,或者 OR/NAND 门。