Reputation: 41
I have 4 SingleTact capacitive sensors each with the i2c address of 0x04. I want to find the average value of the sensors, in order to make a joystick. However I am unsure how to assign each sensor it's own address since they all have the same address as they are the same sensor. I have an initial code however this only works with one single sensor as it only has one single i2c address byte. I have wired together all the SDA and SCL line together using tutorials online and have included pull-up resistors.
#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;
}
}
Thanks for any advice.
Upvotes: 2
Views: 2836
Reputation: 1830
You can also use an I2C Multiplexer. They have their own I2C address and can enumerate the four sensors on their own buses (broadcast domains) so that it is possible to switch between them. Your programming will need to explicitly select each sensor in turn and keep track of which one it is reading. Once switched, I2C traffic just passes through to the selected device. This works best with a cluster of sensors in a star topology as you are running extra wiring.
I'm going to go further, and say you could even use relays to switch these in and out, or OR/NAND gates.
Upvotes: 1
Reputation: 2880
You should read the manual of this device, available here. It says, at the interface description, that
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.
So you just have to
Then each sensor will reply to the address you set.
Please note that
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
So, even in this case, the RTFM advice is the most important one...
Upvotes: 6
Reputation: 9
You need use the Kalman Filter
#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);
}
}
Upvotes: -3