Проблема с плохой пропускной способностью при 50 Arduino, подключенных по I2C

У меня есть проблема, и я надеюсь, что кто-нибудь здесь сможет мне помочь.

Я хочу измерить изменения в емкости с течением времени. На каждый датчик емкости используется один Arduino. На Arduino приходится один анод и в общей сложности один общий каход. (См. Рисунок)

С помощью четырех Arduino, соединенных последовательно через соединение I2C master-slave, были получены успешные результаты. При использовании более четырех Arduino все результаты измерений были равны 0, поскольку напряжение на входном выводе больше не измеряется. Затем я понизил опорное напряжение для analogRead. Таким образом, было возможно 10 Ардуино в серии. Однако измеренные значения явно ниже. Тем не менее, абсолютные значения не подлежат измерению, но разницы в мощности достаточно. Наконец, следует использовать 50 Ардуино. Я подумал о следующих возможностях

  1. уменьшите опорное напряжение еще больше, но это может еще больше ухудшить результаты измерений, так что отклонение не будет измерено.
  2. увеличьте выходное напряжение на Arduino с помощью операционного усилителя, чтобы компенсировать падение напряжения.

Есть ли у кого-нибудь другая идея, как я мог бы продолжить, чтобы получить полезный результат со всеми 50 Arduino? Код, который я использовал, можно найти ниже.

Заранее большое спасибо

Приветствую , Имоджия

Мастер

const int OUT_PIN = A0;
const int IN_PIN = A2;

//Емкость между IN_PIN и Землей
// Паразитная емкость присутствует всегда. Дополнительная емкость может быть добавлена в
// позволяет измерять более высокую емкость.
const float IN_STRAY_CAP_TO_GND = 30.00;
const float IN_EXTRA_CAP_TO_GND = 0.0;
const float IN_CAP_TO_GND  = IN_STRAY_CAP_TO_GND + IN_EXTRA_CAP_TO_GND;
const int MAX_ADC_VALUE = 1023;

#include <Wire.h>

#define nodeMax 10
#define nodeStart 2

union u_capacitance
{
  struct
  {
    float capa_slave;
  };
  byte bytes[10];
};

u_capacitance capacitanceSlave;


void setup()
{
 analogReference(EXTERNAL);
 Wire.begin(); // присоединиться к шине i2c (адрес необязателен для master)

 pinMode(OUT_PIN, OUTPUT);
 pinMode(IN_PIN, OUTPUT);

 Serial.begin(9600);
}

void loop() {
  //Тестируемый конденсатор между OUT_PIN и IN_PIN
  // Увеличение верхнего края в режиме вывода
  pinMode(IN_PIN, INPUT);
  digitalWrite(OUT_PIN, HIGH);
  int val = analogRead(IN_PIN);

  //Очистить все для следующего измерения
  digitalWrite(OUT_PIN, LOW);
  pinMode(IN_PIN, OUTPUT);
  //digitalWrite(IN_PIN, НИЗКИЙ);

  // Вычислить и распечатать результат

  float capacitance = (float)val * IN_CAP_TO_GND / (float)(MAX_ADC_VALUE - val);

  Serial.print(capacitance, 2);
  Serial.print(",");
  Serial.print(val);
  Serial.print(",");

  delay(10);

  for( int node = nodeStart; node <= nodeMax; node++){
    Wire.requestFrom(node, sizeof(capacitanceSlave));
    
    for (unsigned int i = 0; i < sizeof(capacitanceSlave); i++){
      capacitanceSlave.bytes[i] = Wire.read();
      }
    Serial.print(capacitanceSlave.capa_slave);
    Serial.print(",");

    delay(10);
  }
  Serial.println();
  delay(50);
}

Подчинение

const int OUT_PIN = A0;
const int IN_PIN = A2;

//Емкость между IN_PIN и Землей
// Паразитная емкость присутствует всегда. Дополнительная емкость может быть добавлена в
// позволяет измерять более высокую емкость.
const float IN_STRAY_CAP_TO_GND = 30.00;
const float IN_EXTRA_CAP_TO_GND = 0.0;
const float IN_CAP_TO_GND  = IN_STRAY_CAP_TO_GND + IN_EXTRA_CAP_TO_GND;
const int MAX_ADC_VALUE = 1023;

#include <Wire.h>

#define node 10

union u_capacitance
{
  struct
  {
    float capa_slave;
  };
  byte bytes[10];
};

u_capacitance capacitanceSlave;

void setup()
{
  analogReference(EXTERNAL);
  Wire.begin(node);                // соединение шины i2c с адресным узлом
  Wire.onRequest(requestEvent); // зарегистрировать событие
  Serial.begin(9600);           // запустить последовательный для вывода

  pinMode(OUT_PIN, OUTPUT);
  pinMode(IN_PIN, OUTPUT);
}

void loop()
{
}

// функция, которая выполняется всякий раз, когда данные поступают от ведущего
// эта функция зарегистрирована как событие, см. раздел setup()
void requestEvent()
{
  //Тестируемый конденсатор между OUT_PIN и IN_PIN
  // Увеличение верхнего края в режиме вывода
  pinMode(IN_PIN, INPUT);
  digitalWrite(OUT_PIN, HIGH);
  int val = analogRead(IN_PIN);

  //Очистить все для следующего измерения
  digitalWrite(OUT_PIN, LOW);
  pinMode(IN_PIN, OUTPUT);
  //digitalWrite(IN_PIN, НИЗКИЙ);

  // Вычислить и распечатать результат

  float capacitance = (float)val * IN_CAP_TO_GND / (float)(MAX_ADC_VALUE - val);

  capacitanceSlave.capa_slave = capacitance;
  
  Wire.write(capacitanceSlave.bytes, sizeof(capacitanceSlave));
  //Serial.println(емкость, 3);
}

, 👍2

Обсуждение

Вам нужно будет значительно уменьшить подтягивающие резисторы на вашей шине I2C, чтобы противодействовать увеличению емкости затвора из-за количества устройств. Чем больше у вас устройств, тем ниже подтягивающие резисторы. Через некоторое время становится непрактичным больше уменьшать подтягивающие резисторы, и вы достигаете предела количества устройств, которые вы можете иметь на шине. Я бы сказал, что 50 устройств - это гораздо больше, чем может выдержать любая разумная шина., @Majenko

Есть ли причина, по которой вы используете 50 Arduino, а не 4 Arduino, подключенных к 8-портовым мультиплексорам? Это уменьшит затраты, а также позволит избежать вашей проблемы., @Nick Gammon

«Наконец-то нужно использовать 50 Arduino» — почему? Не создает ли это проблему, которую вы пытаетесь решить?, @Nick Gammon


3 ответа


3

Катодная линия используется совместно всеми выводами ардуино. Когда один из них пытается установить вывод ВЫСОКО, все остальные пытаются держать ее НИЗКО. Это состояние короткого замыкания, которое может повредить выходы Arduino. Даже если они выживут, напряжение на линии будет значительно ниже 5 В из-за того, что эти Arduino тянут линию НИЗКО. Чем больше из них тянут линию НИЗКО, тем ниже будет напряжение .

Простым решением было бы изменить протокол таким образом, чтобы только Arduino, который в данный момент производит измерение, мог использовать эту линию, в то время как другие сохраняют свой OUT_PIN в режиме высокого импеданса (т. Е. Ввода). Это хрупко, потому что ... случаются ошибки, и вы все еще можете допустить короткое замыкание. Более надежным решением было бы подключить к катоду только главный источник питания и возложить на него ответственность за управление линией ВЫСОКИЙ и НИЗКИЙ.

Теперь несколько несвязанных комментариев:

  • Число с плавающей запятой равно четырем байтам: нет смысла передавать десять байтов.

  • Вы можете измерить до четырех конденсаторов с каждым Arduino (контакты от A0 до A3, вы можете использовать цифровой вывод для OUT_PIN), расточительно использовать один Arduino на конденсатор.

,

В конфигурации I2C никто не устанавливает высокий уровень выходного сигнала. Это делают подтягивающие резисторы., @Nick Gammon

@NickGammon: я говорю о линии с надписью «катод» на рисунке, а не о I2C SDA., @Edgar Bonet

ХОРОШО. «Контурная схема» была не так уж и ясна., @Nick Gammon

@Eggar Bonet: Прежде всего спасибо за ответы, Я попробовал ваше предложение, только подключив мастер к катоду и изменив код, но это не сработало. датчик, подключенный к ведущему, дает лучшую выходную емкость, но выход ведомого - «нан». Я должен использовать один Arduino для каждого датчика, потому что в предыдущих научных работах два датчика мешали одному Arduino, и результаты были плохими., @Imogdia

@Imogdia: Должно быть что-то не так либо с вашей обновленной схемой, либо с вашим обновленным кодом. Возможно, вы захотите обновить вопрос, чтобы он отражал текущую схему и код., @Edgar Bonet

@Imogdia: с обновленным кодом ведомые устройства все еще получают OUT_PIN. И они освобождают «IN_PIN» _после_ того, как мастер установил «OUT_PIN» в «HIGH», что уже слишком поздно. Вам нужен более сложный коммуникационный протокол, в котором мастер сообщает ведомому, когда освободить IN_PIN., @Edgar Bonet

И как я могу это сделать? Я совершенно новичок в ардуино и у меня нет опыта программирования, @Imogdia

@Imogdia: см. расширенный ответ., @Edgar Bonet

Я протестировал ваш код (см. обновленную угрозу), но результат остался прежним. я протестировал ваш код на 10 arduinos и получил следующие результаты: только мастер получает измерение 12:22:21.472 -> 1.38,45,в,в,в,в,в,в,в,в,в,в 12:22:21.710 -> 1.38,45,в,в,в,в,в,в,в,в,в,в 12:22:21.947 -> 1.41,46,в,в,в,в,в,в,в,в,в,в 12:22:22.186 -> 1.38,45,в,в,в,в,в,в,в,в,в,в 12:22:22.426 -> 1.38,45,в,в,в,в,в,в,в,в,в,в 12:22:22.700 -> 1.38,45, нан, нан, нан, нан, нан, нан, нан, нан, нан,, @Imogdia

@Tizian: 1. Ваши рабы все еще управляют OUT_PIN. 2. Они ничего не отправляют (мой комментарий // передаем результат... должен был быть заменен вашим кодом, передающим результат). 3. Зачем дважды вызывать Wire.onRequest()? 4. Попробуйте отправить фиксированное значение (например, 12.34), чтобы исключить проблему со связью. 5. Почему бы не отправить необработанные показания АЦП вместо вычисленной емкости?, @Edgar Bonet

@EdgarBonet Я снова обновил свой код. Спасибо за вашу помощь! Завтра проверю и скажу, получилось ли., @Imogdia

@EdgarBonet Сработало отлично, спасибо за помощь!, @Imogdia


1

Шина I2C никогда не предназначалась в качестве распределительной шины между многими компьютерами. Выходы не предназначены для подключения длинных проводов. С большим трудом вы можете заставить это работать, но определенно не повторяемо и не доступно повторно. Подумайте о чем-нибудь другом. Может, может сделать, может быть, 60 узлов в зависимости от выбранного приемопередатчика. Это относительно недорого.

,

1

Возможно, вы захотите использовать протокол RS485. В отличие от I2C, здесь используется «сбалансированный» проводка, поэтому больше подходит для более длинных кабелей.

Я описал способ кодирования нескольких Arduino с помощью "Rolling Master" протокол здесь — это позволяет подключить значительное количество Arduino к двум проводам, и они по очереди будут отправлять данные.

Однако в вашем случае, поскольку вы измеряете емкость (а не емкость?), то каждый из ваших Arduino может измерять 6 (или, возможно, больше) аналоговых показаний, поэтому вам не нужно 50, вам нужно 50/6 = 8,33 Arduino. (ОК, девять).

Например, Arduino Micro имеет 12 аналоговых входных каналов, поэтому потребуется только 4 (чтобы получить 48 входов) или 5 (чтобы получить 60 входов). Тогда вы можете придерживаться I2C.

,

Я рекомендовал CAN из-за 50 узлов, RS485 имеет ограничение в 32. Чтобы выйти за эти пределы, необходимы повторители., @Gil