ARDUINO UNO + BMX055 + PCF8575

SSanya

✩✩✩✩✩✩✩
3 Июн 2021
5
0
Всем привет, пишу прошивку для опроса датчиков BMX-055, использую мультиплексор PCF8575, подключение стандартное I2C к ардуино uno. Скетч компилируется, но вывод по нулям.
Библиотека для работы с датчиком: https://wiki.iarduino.ru/page/Trema_IMU9/
Библиотека для работы с мультиплексором(I2C расширителем портов): https://github.com/xreef/PCF8575_library
Всегда выводятся нули, причем если использовать этот "мультиплексор" с ESP-WROOM32, то все работает. В чем может быть проблема?
Мой код для опроса всех датчиков и вывода показаний только гироскопа:
C++:
#include <Wire.h>
#include <iarduino_Position_BMX055.h>
#include <PCF8575.h>
iarduino_Position_BMX055 sensorA(BMA); // Создаём объект sensorA указывая что ему требуется работать только с акселерометром.
iarduino_Position_BMX055 sensorG(BMG); // Создаём объект sensorG указывая что ему требуется работать только с гироскопом.
iarduino_Position_BMX055 sensorM(BMM);
iarduino_Position_BMX055 sensorALL(BMX);

#define SENSOR_CNT 6
#define SENSORS_ON true

#define I2C_SWITCH_ADDRESS = 0x20;
PCF8575 pcf8575(0x20);

typedef struct{
  float ax;
  float ay;
  float az;
  float gx;
  float gy;
  float gz;
  float mx;
  float my;
  float mz;
} sensor_t;

sensor_t sensors[SENSOR_CNT];
void sensors_select(int senson_number) {
    for (int i = 0; i < SENSOR_CNT; i++) {
      if ( senson_number != i)
        pcf8575.digitalWrite(i, LOW);
    }
    pcf8575.digitalWrite(P1, HIGH);
    delay(10);
}

float q[4] = {1.0f, 0.0f, 0.0f, 0.0f};
float GyroMeasError = PI * (40.0f / 180.0f);
float beta = sqrt(3.0f / 4.0f) * GyroMeasError;
float deltat = 0.0f;
uint32_t Now = 0;   
uint32_t lastUpdate = 0;

void _init(){
  for(int i =0; i<SENSOR_CNT; ++i){
    sensors_select(i);
    sensorA.begin();
    sensorG.begin();
    sensorM.begin(); 
  }
}

void sensors_fill(int sensor_number) {
    sensors_select(sensor_number);
    sensorA.read(BMA_M_S);
    sensors[sensor_number].ax = sensorA.axisX;
    sensors[sensor_number].ay = sensorA.axisY;
    sensors[sensor_number].az = sensorA.axisZ;
    sensorG.read(BMG_DEG_S);
    sensors[sensor_number].gx = sensorG.axisX;
    sensors[sensor_number].gy = sensorG.axisY;
    sensors[sensor_number].gz = sensorG.axisZ;
    sensorM.read(BMM_MG);
    sensors[sensor_number].mx = sensorM.axisX;
    sensors[sensor_number].my = sensorM.axisY;
    sensors[sensor_number].mz = sensorM.axisZ;
}

void setToSerial(){
  for(int i =0; i<SENSOR_CNT; ++i){
    Serial.print(i);Serial.print(" ");
    Serial.print(sensors[i].gx);Serial.print(" ");
    Serial.print(sensors[i].gy);Serial.print(" ");
    Serial.print(sensors[i].gz);Serial.println(" ");
  }
}
void setup() {
  Wire.begin();
  Serial.begin(115200);
     while(!Serial){}
     for (int i = 0; i < SENSOR_CNT; i++)
      pcf8575.pinMode(i, OUTPUT);
      pcf8575.begin();
      _init();
}
void loop() {
  //Now = micros();
  //deltat = ((Now - lastUpdate)/1000000.0f);
  //lastUpdate = Now;
  for(int i =0;i<SENSOR_CNT;++i){
      sensors_fill(i);
  }
  setToSerial();
}
 

kostyamat

★★★★★★✩
29 Окт 2019
1,097
630
@SSanya, что то я совершенно не понял к чему тут расширитель портов? Датчик сам создает три устройства i2c, ему никакой расширитель не нужен. Во вторых, мне кажется у вас конфликт адресов датчика и расширителя. И датчик, и расширитель имеют дополнительные пины, которые нужно либо подтянуть к питанию, либо к массе, чтобы выбрать им адреса на шине. Это делается один раз, до старта прошивки, физически соеденив нужные пины с массой или питанием, и во время работы смене "на лету" адреса не подлежат, так шина i2c работать не умеет.
Опять же, библиотека от iarduuno.ru писана под их модуль Трема-9, там адреса выставлены с завода. Если вы используете сторонний датчик, то чтобы он заработал, вам нужно установить ему адреса, такие же, какие выбраны для Трема-9, или использовать другую библиотеку, ну или на крайний случай исправить адреса на нужные вам в самой библиотеке.
 
Изменено:
  • Лойс +1
Реакции: SSanya и bort707

bort707

★★★★★★✩
21 Сен 2020
2,898
862
@SSanya, см строчки 12 и 13 в своем коде, ты сам устанавливаешь один и тот же адрес для датчика и расширителя. Это как самому себе в ногу выстрелить.
Не знаю, откуда ты взял эту ересь, но так точно работать не будет.
Остальное не смотрел.
 
  • Лойс +1
Реакции: SSanya

SSanya

✩✩✩✩✩✩✩
3 Июн 2021
5
0
@Un_ka, 1624017664379.png
Вот такая схема подключения к ардуино, немного криво нарисовал, на всякий распишу так:
vcc(IMU) - 5v(ARDUINO);
GND - GND;
P0 (PCF8575) -SCL(IMU)
P1 (PCF8575) - SDA(IMU)
и так для каждого датчика. Могу ошибаться с подключением, т.к. только недавно начал разбираться в ардуино и микроконтроллерах. Подключал только к ESP-WROOM32 а затем перекидывал I2C шину на ардуино.
Код для ESP32:

ESP-32 test IMU9DOF:
#include <ArduinoLog.h>
#include <Wire.h>
#include <iarduino_Position_BMX055.h>                       // Подключаем библиотеку iarduino_Position_BMX055 для работы с Trema-модулем IMU 9 DOF.
#include <PCF8575.h>
#include "BluetoothSerial.h"

#define SENSOR_CNT 1
#define SENSORS_ON true
#define BLE_ON false
#define COM_ON true

#define TEST_ON false

#define DEVICE_NAME         "ims"
#define DEVICE_VERSION      "1.0.0"
#define SDA_1 21
#define SCL_1 22

#define SDA_2 33
#define SCL_2 32

#define I2C_SWITCH_ADDRESS = 0x20;
#define LOG_LEVEL_VERBOSE 6
// 0 - LOG_LEVEL_SILENT     no output
// 1 - LOG_LEVEL_FATAL      fatal errors
// 2 - LOG_LEVEL_ERROR      all errors
// 3 - LOG_LEVEL_WARNING    errors, and warnings
// 4 - LOG_LEVEL_NOTICE     errors, warnings and notices
// 5 - LOG_LEVEL_TRACE      errors, warnings, notices & traces
// 6 - LOG_LEVEL_VERBOSE    all

TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1);
PCF8575 pcf8575(&I2Ctwo, 0x20, SDA_2, SCL_2);
iarduino_Position_BMX055 sensorA(BMA);  // ускорения
iarduino_Position_BMX055 sensorG(BMG);
iarduino_Position_BMX055 sensorM(BMM);
iarduino_Position_BMX055 sensorALL(BMX); // чтение со всех датчиков

BluetoothSerial SerialBT;

// config
typedef struct
{
  uint8_t cnt;
} config_t;

//float q[4] = {1.0f, 0.0f, 0.0f ,0.0f};
typedef struct
{
  float AaxisX = 0;
  float AaxisY = 0;
  float AaxisZ = 0;
  float AAaxisX = 0;
  float AAaxisY = 0;
  float AAaxisZ = 0;
  float GaxisX = 0;
  float GaxisY = 0;
  float GaxisZ = 0;
  float MaxisX = 0;
  float MaxisY = 0;
  float MaxisZ = 0;
  float qx;
  float qy;
  float qz;
  float qw;
  float temp;
} sensor_t;

sensor_t sensors[SENSOR_CNT];

float q[4] = {1.0f, 0.0f, 0.0f, 0.0f};
float GyroMeasError = PI * (40.0f / 180.0f);
float beta = sqrt(3.0f / 4.0f) * GyroMeasError;
float deltat = 0.0f;
uint32_t Now = 0;   
uint32_t lastUpdate = 0;

void sensors_select(int senson_number) {
    // Disable all sensors
    for (int i = 0; i < SENSOR_CNT; i++) {
      if ( senson_number != i)
        // Disable sensor
        pcf8575.digitalWrite(i, LOW);
    }
    // Enable one sensor
    pcf8575.digitalWrite(senson_number, HIGH);
    delay(10);
}

void sensors_init() {
  Log.trace("Init sensors\n");
   for (int i = 0; i < SENSOR_CNT; i++) {
    sensors_select(i);
    sensorA.begin();
    sensorG.begin();
    sensorM.begin();
    sensorALL.begin();
  }
}

void code_error(int code) {
  switch( code )
  {    // Получаем результат самотестирования для его сравнения с указанными ниже константами.
      case 0:          Log.error("Аппаратное самотестирование всех датчиков успешно пройдено!\n"); break;
      case BMA_ERR_ID: Log.error("Акселерометр не найден.\n");                                     break;
      case BMG_ERR_ID: Log.error("Гироскоп не найден.\n");                                         break;
      case BMM_ERR_ID: Log.error("Магнитометр не найден.\n");                                      break;
      case BMA_ERR_ST: Log.error("Акселерометр не прошел самотестирование.\n");                    break;
      case BMG_ERR_ST: Log.error("Гироскоп не прошел самотестирование.\n");                        break;
      case BMM_ERR_ST: Log.error("Магнитометр не прошел самотестирование.\n");                     break;
      default:         Log.error("Модуль не прошел самотестирование по неизвестной причине.\n");   break;
    }
}

void sensors_test(int senson_number) {
  Log.trace("Testing sensor %d\n", senson_number+1);
  sensors_select(senson_number);
  sensorA.begin();
  code_error(sensorA.test());
}

void sensors_erase() {
  for (int i = 0; i < SENSOR_CNT; i++) {
    sensors[i].AaxisX = 0;
    sensors[i].AaxisY = 0;
    sensors[i].AaxisZ = 0;
    sensors[i].AAaxisX = 0;
    sensors[i].AAaxisY = 0;
    sensors[i].AAaxisZ = 0;
    sensors[i].GaxisX = 0;
    sensors[i].GaxisY = 0;
    sensors[i].GaxisZ = 0;
    sensors[i].MaxisX = 0;
    sensors[i].MaxisY = 0;
    sensors[i].MaxisZ = 0;
    sensors[i].temp = 0;
  }
}

void sensors_fill(int senson_number) {
    sensors_select(senson_number);
                                                            // Функция read() читает данные того датчика, для которого был создан объект.
                                                            // Для объекта работающего с акселерометром, функция read() может принять
                                                            // один из четырёх параметров: BMA_M_S, BMA_G, BMA_DEG, или BMA_RAD.
                                                            // Если параметра нет, то используется параметр по умолчанию
                                                            // sensor.read(BMA_M_S); читать угловое ускорение в м/с² (по умолчанию).
                                                            // sensor.read(BMA_G);   читать угловое ускорение в g.
                                                            // sensor.read(BMA_DEG); читать углы «крен» и «тангаж» в градусах.
                                                            // sensor.read(BMA_RAD); читать углы «крен» и «тангаж» в радианах.
                                                            // Данные прочитанные функцией read() сохраняются в переменных axisX, axisY, axisZ и temp.
    sensorA.read(BMA_M_S);
    sensors[senson_number].AaxisX = sensorA.axisX;
    sensors[senson_number].AaxisY = sensorA.axisY;
    sensors[senson_number].AaxisZ = sensorA.axisZ;
    sensorA.read(BMA_DEG);
    sensors[senson_number].AAaxisX = sensorA.axisX;
    sensors[senson_number].AAaxisY = sensorA.axisY;
    sensors[senson_number].AAaxisZ = sensorA.axisZ;
    sensorG.read(BMG_DEG_S);
    sensors[senson_number].GaxisX = sensorG.axisX;
    sensors[senson_number].GaxisY = sensorG.axisY;
    sensors[senson_number].GaxisZ = sensorG.axisZ;
    sensorM.read(BMM_MG);
    sensors[senson_number].MaxisX = sensorM.axisX;
    sensors[senson_number].MaxisY = sensorM.axisY;
    sensors[senson_number].MaxisZ = sensorM.axisZ;
    sensorALL.read();
    sensors[senson_number].qx = sensorALL.q1;
    sensors[senson_number].qy = sensorALL.q2;
    sensors[senson_number].qz = sensorALL.q3;
    sensors[senson_number].qw = sensorALL.q4;
}

void sensors_send_to_com() {
  for (int i = 0; i < SENSOR_CNT; i++) {
    Serial.printf("%d ", i+1);
    //MadgwickQuaternionUpdate(sensors[i].AaxisX, sensors[i].AaxisY, sensors[i].AaxisZ, sensors[i].GaxisX * PI / 180.0f, sensors[i].GaxisY * PI / 180.0f, sensors[i].GaxisZ * PI / 180.0f, -sensors[i].MaxisY, sensors[i].MaxisX, sensors[i].MaxisZ);
    Serial.print(sensors[i].qx); Serial.print(" ");
    Serial.print(sensors[i].qy); Serial.print(" ");
    Serial.print(sensors[i].qz); Serial.print(" ");
    Serial.print(sensors[i].qw); Serial.println(" ");
    //Serial.println();
  }
}

void sensors_send_ble_serial() {
  for (int i = 0; i < SENSOR_CNT; i++) {
    SerialBT.printf("%d ", i+1);
    SerialBT.printf("%f ", sensors[i].AaxisX);
    SerialBT.printf("%f ", sensors[i].AaxisY);
    SerialBT.printf("%f ", sensors[i].AaxisZ);
    SerialBT.printf("%f ", sensors[i].AAaxisX);
    SerialBT.printf("%f ", sensors[i].AAaxisY);
    SerialBT.printf("%f ", sensors[i].AAaxisZ);
    SerialBT.printf("%f ", sensors[i].GaxisX);
    SerialBT.printf("%f ", sensors[i].GaxisY);
    SerialBT.printf("%f ", sensors[i].GaxisZ);
    SerialBT.printf("%f ", sensors[i].MaxisX);
    SerialBT.printf("%f ", sensors[i].MaxisY);
    SerialBT.printf("%f ", sensors[i].MaxisZ);
    SerialBT.printf("%f ", sensors[i].temp);
    SerialBT.println();
    delay(10);
  }
}

void ble_serial_init() {
  String devName = DEVICE_NAME;
  String devVersion = DEVICE_VERSION;
  devName += ':';
  devName += devVersion;
  Log.trace("Start ble serial \n");
  SerialBT.begin(devName);
}

void setup() {
  Serial.begin(115200, SERIAL_8N1);
  while(!Serial){}
  Log.begin(LOG_LEVEL_VERBOSE, &Serial);
  Log.trace("Starting\n");
  sensors_erase();

  // Инициализация BLE
  if ( BLE_ON ) ble_serial_init();

  // Инициализация датчиков
  if ( SENSORS_ON ) {
    I2Cone.begin(SDA_1, SCL_1, 100000);
    I2Ctwo.begin(SDA_2, SCL_2, 100000);
    for(int i=0; i<SENSOR_CNT; i++)
      pcf8575.pinMode(i, OUTPUT);
    pcf8575.begin();
    sensors_init();
  }
 
  // TEST
  if ( TEST_ON && SENSORS_ON )
    for (int i = 0; i < SENSOR_CNT; i++)
      sensors_test(i);
}

void loop() {
  //Now = micros();
  //deltat = ((Now - lastUpdate)/1000000.0f);
  //lastUpdate = Now;
  if ( SENSORS_ON )
    for (int i = 0; i < SENSOR_CNT; i++)
      sensors_fill(i);

  if ( COM_ON ) sensors_send_to_com();

  if ( BLE_ON ) {
    sensors_send_ble_serial();
  }
}
Подключение к ESP32 такое же, только там 2 I2C шины, поэтому SDA - D33, SCL - D32
 

SSanya

✩✩✩✩✩✩✩
3 Июн 2021
5
0
@kostyamat, расширитель нужен потому что я подключаю несколько датчиков BMX055, а они имеют одинаковый адрес. Про акселерометр, гироскоп и магнетометр я знаю, но когда датчиков несколько то они имеют опять одинаковые адреса, может быть и без расширителя будет работать конечно, я не знаю. А датчики у меня такие же как и на сайте iarduino, заказывал у них. Адреса стоят такие же, даже i2c сканером смотрел. У меня такое ощущение, что мультиплексор как то неверно опрашивает датчики, я вообще думал так: выбираем какой то датчик из списка(по индексу от 0 до SENSOR_CNT), ему даем высокий сигнал, остальным соответственно низкий. Ну либо я использую неверную логику\подключение.
 

SSanya

✩✩✩✩✩✩✩
3 Июн 2021
5
0
@bort707, этот код писал не я, поэтому хочу переписать его под ардуино , и пишу все с начала, использую только функцию работы с pcf8575.
Вот что у меня сейчас есть:
Arduino_test_BMX055:
#include <Wire.h>
#include <iarduino_Position_BMX055.h>
#include <PCF8575.h>
iarduino_Position_BMX055 sensorALL(BMX);

#define SENSOR_CNT 6 // number of sensors

PCF8575 pcf8575(0x20);

typedef struct{
  float qx;
  float qy;
  float qz;
  float qw;
} sensor_t;

sensor_t sensors[SENSOR_CNT];
void sensors_select(int sensor) { // sensor - number of active sensor
    for (int i = 0; i < SENSOR_CNT; i++) {
      if ( sensor != i)
        pcf8575.digitalWrite(i, LOW);
    }
    pcf8575.digitalWrite(sensor, HIGH);
    delay(10);
}

void _init(){
  for(int i =0; i<SENSOR_CNT; ++i){
    sensors_select(i);
    sensorALL.begin();
  }
}

void sensors_fill(int sensor_number) {
    sensors_select(sensor_number);
    sensorA.read(BMA_M_S);
    sensors[sensor_number].qx = sensorA.q1;
    sensors[sensor_number].qy = sensorA.q2;
    sensors[sensor_number].qw = sensorA.q3;
    sensors[sensor_number].qw = sensorA.q4;
}

void setToSerial(){
  for(int i =0; i<SENSOR_CNT; ++i){
    Serial.print(i);Serial.print(" "); // number of sensor
    Serial.print(sensors[i].qx); Serial.print(" ");
    Serial.print(sensors[i].qy); Serial.print(" ");
    Serial.print(sensors[i].qz); Serial.print(" ");
    Serial.println(sensors[i].qw);
  }
}
void setup() {
  Wire.begin();
  Serial.begin(115200);
     while(!Serial){}
     for (int i = 0; i < SENSOR_CNT; i++)
      pcf8575.pinMode(i, OUTPUT);
      pcf8575.begin();
      _init();
}
void loop() {
  for(int i =0;i<SENSOR_CNT;++i){
      sensors_fill(i);
  }
  setToSerial();
}
 

poty

★★★★★★✩
19 Фев 2020
2,988
895
Мне кажется или это не мультиплексор I2C? PCF8575 - это мультиплексор цифровых портов! Вы выбираете с помощью интерфейса I2C какой порт будете менять и на что, только и всего. Сигналы шины I2C не попадают на выводы PCF8575, поэтому всё, что Вы делаете с помощью классов sensors вообще уходит в пустоту.
 
Изменено:

bort707

★★★★★★✩
21 Сен 2020
2,898
862
@SSanya, если у вас датчики работают по шине i2c, то этот мультиплексор вам не подойдет. Вам нужен модуль tca9548, например

И да, стандартная библиотека для bmx055 скорее всего через мультиплексор работать не будет, надо будет дописывать самому
 
  • Лойс +1
Реакции: kostyamat и SSanya

SSanya

✩✩✩✩✩✩✩
3 Июн 2021
5
0
@poty, тогда, как я понимаю, на ESP32 это работало только с одним датчиком, который каким то образом через мультиплексор давал сигналы? А теперь мне нужно искать мультиплексор I2C, как в примере @bort707 - tca9548 и через него подключать датчики? Но тогда почему в чип и дипе пишется, что это "PCF8575PW, Расширитель I/O, 16бит, 400 кГц, I2C, SMBus, 2.5 В, 5.5 В, TSSOP". То есть каким то образом I2C тут работает, или это просто подключение по I2C так указывается?
 
Изменено:

kostyamat

★★★★★★✩
29 Окт 2019
1,097
630
или это просто подключение по I2C так указывается?
Ага.

PCF8575 это расширитель дискретных портов. Вы ему по i2c посылаете команду какую ногу подтянуть к питанию, или массе, - он ее подтягивает. Если я правильно помню, он еще и на вход работает, то есть при опросе контроллером может ответить состояние своего пина, 0 или 1. Никакие протоколы он через себя пропустить не в состоянии. Есть аналоговые мультиплексоры, они ведут себя как реле, то есть физически подключают выделенный вход/выход к одному из группы своих входов/выходов. В принципе такой бы мог коммутировать i2c. Другой вопрос - понравится ли это контроллеру и датчикам? Ну, может и прокатит. Я бы не надеялся на это. Мое мнение - покупайте tca9548 и не ищите сложных путей, тут и без них у вас приключений хватит.
 
  • Лойс +1
Реакции: SSanya

poty

★★★★★★✩
19 Фев 2020
2,988
895
@SSanya, я думаю, один датчик Вы подключали непосредственно к шине I2C, параллельно PCF8575, потому что иначе он бы точно не работал.
 
  • Лойс +1
Реакции: SSanya