Общение двух и более ардуин по шине I2C в формате master-master с одновременным использованием радиоканала.
- Войдите на сайт для отправки комментариев
Проблема родилась отсюда: http://arduino.ru/forum/obshchii/problema-virtualware-i-serialsoftwareserial
Нужно чтобы две (а может и более ардуин) общались между собой в независимом формате (т.е. master-master), т.к. важные события могут наступать у любой ардуине и необходимо оповещать о них останьные ардуины, а также передавать данные по радиоканалу.
Решение было найдено с использованием шины I2C. Ниже приведен код для обоих ардуин, отличия в котором только в адресе ардуины, ну и в главном цикле разный формат передаваемых данных. Для усложнения задачи ардуины общаются между собой как по шине I2C, так и по радиоканалу, т.к. к обоим приделаны радипередатчик и приемник на частоту 433 МГц.
Привожу коды, т.к. в такой реализации нигде не нашел, пишлось быстро сваять самому. Написаны простейшие функции, позволяющие передавать строковые данные. При частой передаче радиоданные могут теряться, поэтому для боле "взрослых" функций необходимо реализовывать подтверждения приема, что тоже сделано, но из-за громоздкости не привожу.
Вот код для первой ардуины (адрес 1)
#include <Wire.h> #include <VirtualWire.h> #define R_TX 5 // пин для передатчика D5 #define R_RX 2 // пин для приемника D2, чтобы повесить функцию приема на прерывание int All=0; void setup() { Serial.begin(9600); Wire.begin(1); // устанавливаем адрес ардуины для шины I2C Wire.onReceive(receive_I2C); // если приходят данные, то вызывается эта команда pinMode (R_TX, OUTPUT); pinMode (R_RX, INPUT); vw_set_ptt_inverted(true); // для передатчика vw_set_tx_pin(R_TX); // устанавливаем пин передатчика vw_set_rx_pin(R_RX); // устанавливаем пин приемника vw_setup(3000); // скорость передачи данных в Kbps vw_rx_start(); // Start the receiver PLL running attachInterrupt(0, receive_Radio, CHANGE); // вешаем функцию приема на прерывание } void loop() { // put your main code here, to run repeatedly: All++; send_I2C(2,"I2C 1->2 ("+String(All,DEC)+")"); send_Radio("Radio 1->2 ("+String(All,DEC)+")"); delay(1000); } uint8_t send_I2C(uint8_t adr, String S) // передача по шине I2C { uint8_t c; char msg[32]; // преобразуем строковую переменную в последовательность байт т.к. Wire отправляет байты S.toCharArray(msg, S.length()+1); // печатаем для наглядности передаваемую команду Serial.print("Send I2C <"); for (int i = 0; i < strlen(msg); i++) Serial.print(msg[i]); Serial.println(">"); // передаем последовательность байт по шине I2C на устройство с адресом adr Wire.beginTransmission(adr); // transmit to device #8 Wire.write((byte *)msg, strlen(msg)); // sends five bytes c=Wire.endTransmission(); // stop transmitting if(c!=0) Serial.println("Error Send="+String(c,DEC)); return(c); } void receive_I2C(int howMany) // прием по шине I2C { // прием последовательность байтов и печать для наглядности Serial.print("Read I2C <"); while (Wire.available()) { // loop through all but the last char c = Wire.read(); // receive byte as a character Serial.print(c); // print the character } Serial.println(">"); // print the integer } void send_Radio(String S) // передача по радиоканалу { char msg[27]; // преобразуем строковую переменную в последовательность байт т.к. Wire отправляет байты S.toCharArray(msg, S.length()+1); vw_send((byte *)msg, strlen(msg)); vw_wait_tx(); // Wait until the whole message is gone // для наглядности печатаем отправляемую команду Serial.println("Send Radio <"+S+">"); } void receive_Radio() // прием по радиоканалу { uint8_t i; char buf[VW_MAX_MESSAGE_LEN]; uint8_t buflen = VW_MAX_MESSAGE_LEN; if (vw_get_message(buf, &buflen)) { Serial.print("Read Radio <"); for (i = 0; i < buflen; i++) Serial.print(buf[i]); Serial.println(">"); } }
Вот код для второй ардуины (адрес 2). Функцию радиоприема повесил на прерывания, если главный цикл не длинный, то функцию приема можно снять с прерывания и вставить в тело цикла. К сожалению, функции передачи не работают из MSTimer2, т.е. сам MSTimer2 работает, но передачу нельзя вставить в функцию, вызываемую по таймеру 2.
Получилось реализовать общение между 3-мя ардуинами, залив этот код в 3-ю ардуину, изменив ее адрес на 3.
Вот примеры распечаток с экрана ардуины 1.
Вот с ардуины 2
Вот с адруины 3
Причем, если к одной из ардуин подключен LCD по шине I2C, то на него выводить информацию можно сразу с всех трех ардуин. Вот код, который опять же един для всех трех ардуин, отличия в адресе и куда курсоры выставлять для LCD-дисплея.
А если еще одно чисто ведомое устройство на шину подключить (у меня это INA219), то не работает. Обмена данными нет.
А если одну ардуину оставить ? Лучше всего лог. анализатором глянуть, обычно этого достаточно бывает.
Одна ведомая ардуина с INA прекрасно работает, но если в режиме мастер-мастер + INA, то никак.