RF24 + EncButton(gyverButton), перестает работать .writeAckPayload()
- Войдите на сайт для отправки комментариев
Здравствуйте, ув. форумчане!
Пару слов о проекте:
Вентиляция на балконе с отслеживанием сигаретного дыма от соседей снизу. Логика работы - 5 датчиков MQ-5 за бортом, подключены к базовой станции (В прошивках - передатчик), к станции подключены два вентилятора со шторками и возможностью реверса, по обнаружении дыма на n-ных датчиках, будут запускаться различные сценарии приток-сток. Так же проект подразумевает дистанционное использование с помощью пульта на ардуине + oled + энкодер (arduino со встроенным wifi) или ручное управление на базовой станции с помощью кнопок без фиксации положения и индикацией состояния датчиков и режимов (адресная светодиодная лента).
Проблема заключается в следующем:
Мне нужна двусторонняя связь между модулями, я остановился на .writeAckPayload(). При раскомментировании строк 77-84 в скетче приемника, перестает работать расчет качества связи на передатчике, между пультом(передатчик) и баз. станцией (приемник). Перестает заходить в условие в строках 142-178 (я выделил строки, чтобы они подсвечивались, но в предпросмотре показывает, что ничего он не выделил), вываливается в блок else на строке 174, и в итоге Общие отравленные пакеты N штук, доставленных 0. В итоге связь 0%. Так же, пакет ack передатчик не получает. Если оставить только Serial.println в 77-84 в скетче приемника, то все работает.
Приемник получает все пакеты отправленные передатчиком.
Мои попытки решения:
-Эксперименты с настройками RF24 в блоке setup() у приемника и передатчика;
-Изначально, я использовал без millis(), просто в теле цикла, на приемнике и передатчике блок кода с отправкой и получением данных;
-Менял название переменной powerState, менял с true\false на 1\0;
-Пробовал назначение =true\=false
Внешние факторы:
При раскомментировании строк 77-84 в скетче приемника, на передатчике начинает моргать зеленый светодиод, вместо, почти, статичного горения.
Обратите внимание, что в передатчике используется ардуино нано со встроенным wifi, с антенной на плате, а на приемнике испольуется ардуино с отдельным модулем с усилием и стабилизатором питания 3.3v, возможно, это играет какую-то роль.
Прошу прощения за отсутствие макетной схемы.
Приемник:
#include <EncButton.h> #include <SPI.h> // библиотека для работы с шиной SPI #include "nRF24L01.h" // библиотека радиомодуля #include "RF24.h" // ещё библиотека радиомодуля #include <GyverOLED.h> // Библиотека дисплея GyverOLED<SSD1306_128x64, OLED_NO_BUFFER> oled; #define CLK 5 #define DT 6 #define SW 4 #define ITEMS 3 // Общее кол во пунктов EncButton<EB_TICK, CLK, DT, SW> enc1; const char *menuNames[] = { "Режим:", "Вент 1:", "Вент 2:", }; char *menuValuesNames[] = { "-", "-", "-", }; const char *modeValuesNames[] = { "Авто", "Ручной", "Выкл", }; const char *fanValuesNames[] = { "Приток", "Сток", "Выкл", }; RF24 radio(10, 9); // "создать" модуль на пинах 9 и 10 Для Уно byte address[][2] = {"NodeS", "SlaveS",}; //возможные номера труб unsigned long buttonTimer; unsigned long sendTimer; unsigned long RSSI_timer; bool enterMenuItem = false; // Флаг выбора bool isMenuOpen = false; bool isModeItemEnter = false; uint8_t pointer = 0; byte rssi; byte lastRssi; int trnsmtd_pack = 1; int totalPack; int settings[3] = {2, 2, 2}; int settingsAnswer[5]; float my_vcc_const = 1.060; void setup() { pinMode(A0, INPUT); Serial.begin(9600); //открываем порт для связи с ПК oled.init(); // Инциализация дисплея oled.setContrast(255); // Макс. яркость radio.begin(); //активировать модуль oled.clear(); mainScreen(); radio.enableAckPayload(); //разрешить отсылку данных в ответ на входящий сигнал radio.openWritingPipe(address[0]); //мы - труба 0, открываем канал для передачи данных radio.setChannel(0x67); //выбираем канал (в котором нет шумов!) radio.setPALevel (RF24_PA_LOW); //уровень мощности передатчика. На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX radio.setDataRate (RF24_1MBPS); //скорость обмена. На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS radio.powerUp(); //начать работу radio.stopListening(); // не слушаем радиоэфир, мы передатчик } void loop() { enc1.tick(); // Переменная указатель if (enc1.isClick()) { isMenuOpen = true; } if (isMenuOpen) { oled.clear(); mainMenu(); printPointer(pointer); while (1) { enc1.tick(); if (enc1.isRight()) { pointer = constrain(pointer - 1, 0, ITEMS - 1); printPointer(pointer); } if (enc1.isLeft()) { pointer = constrain(pointer + 1, 0, ITEMS - 1); printPointer(pointer); } if (enc1.isClick()) { enterMenuItem = !enterMenuItem; printPointer(pointer); propertySet(pointer); enterMenuItem = !enterMenuItem; printPointer(pointer); } if (millis() - buttonTimer > 500) { buttonTimer = millis(); } if (enc1.isHeld()) { oled.clear(); mainScreen(); isMenuOpen = false; return; } } } if (enc1.isHeld()) { isMenuOpen = false; enterMenuItem = false; oled.clear(); mainScreen(); } byte pipeNo; if (millis() - sendTimer > 200) { if (radio.write(&settings, sizeof(settings))) { // отправка пакета transmit_data radio.write(&settings, sizeof(settings)); // for (byte i = 0; i < 3; i++) { // Serial.print(i); // Serial.print(","); // Serial.print(settings[i]); // Serial.print(";"); // // } trnsmtd_pack++; totalPack++; if (radio.available()) { radio.read(&settingsAnswer, sizeof(settingsAnswer)); // читаем if (millis() - buttonTimer > 1000) { for (byte i = 0; i < 3; i++) { Serial.print("settingsAnswer["); Serial.print(i); Serial.print("] == "); Serial.print(settingsAnswer[i]); Serial.println(); } buttonTimer = millis(); } } } else { totalPack++; } sendTimer = millis(); } if (millis() - RSSI_timer > 1000) { // таймер RSSI readVcc(); // расчёт качества связи (0 - 100%) на основе числа ошибок и числа успешных передач rssi = ( (float)trnsmtd_pack / totalPack) * 100; rssiInfo(rssi); Serial.print("totalPack == "); Serial.println(totalPack); Serial.print("trnsmtd_pack == "); Serial.println(trnsmtd_pack); // сбросить значения totalPack = 1; trnsmtd_pack = 0; RSSI_timer = millis(); } delay(10); } void mainMenu() { oled.clear(64, 0, 101, 64); for (byte i = 0; i < ITEMS; i++) { oled.setCursor(10, i); oled.print(menuNames[i]); } for (byte i = 0; i < ITEMS; i++) { if (i == 0) { menuValuesNames[i] = modeValuesNames[settings[i]]; } if (i == 1 || i == 2) { menuValuesNames[i] = fanValuesNames[settings[i]]; } oled.setCursor(64, i); oled.print(menuValuesNames[i]); } } void mainScreen() { rssiInfo(100); batteryInfo(readVcc()); oled.setCursor(5, 2); oled.print("Вент 1: "); oled.print(fanValuesNames[settings[1]]); oled.setCursor(5, 3); oled.print("Вент 2: "); oled.print(fanValuesNames[settings[2]]); oled.setCursor(5, 5); oled.print("Режим:"); oled.print(modeValuesNames[settings[0]]); } void rssiInfo(byte signalPower) { if (signalPower != lastRssi) { oled.clear(0, 0, 64, 9); int drawLimit = map(signalPower, 0, 100, 1, 7); Serial.println(drawLimit); int height = 10; int rectLength = 0; for (int i = 0; i < 7; i++) { if (drawLimit > i) { height > 2 ? height-- : height; oled.rect(rectLength, height, rectLength + 1, 10); } else { oled.fastLineH(10, rectLength, rectLength + 1); } rectLength = rectLength + 3 ; } oled.setCursorXY(24, 4); oled.print(signalPower); oled.print("%"); lastRssi = signalPower; } } void batteryInfo(byte chargePersent) { int battery = map(chargePersent, 0, 100, 101, 120); if (chargePersent < 100) { oled.setCursorXY(78, 4); } else { oled.setCursorXY(72, 4); } oled.print(chargePersent); oled.print("%"); oled.roundRect(116, 4, 122, 9, OLED_STROKE); oled.rect(100, 3, 120, 10); oled.rect(battery, 3, 120, 10, OLED_STROKE); } void printPointer(uint8_t pointer) { if (enterMenuItem) { oled.clear(0, 0, 5, 64); oled.clear(102, 0, 107, 64); oled.setCursor(102, pointer); oled.print("<"); } else { oled.clear(0, 0, 5, 64); oled.clear(102, 0, 107, 64); oled.setCursor(0, pointer); oled.print(">"); } } void propertySet(byte pointer) { switch (pointer) { case 0: modeEdit(); break; case 1: fan1(1); break; case 2: fan1(2); break; case 3: break; case 4: break; } } void modeEdit() { isModeItemEnter = !isModeItemEnter; while (1) { enc1.tick(); if (enc1.isRight()) { if (!enterMenuItem) { pointer = constrain(pointer - 1, 0, 4); printPointer(pointer); } else { settings[0] = (int)constrain(settings[0] - 1, 0, 2); menuValuesNames[0] = modeValuesNames[settings[0]]; oled.clear(64, 0, 100, 5); oled.setCursor(64, 0); oled.print(menuValuesNames[0]); } } if (enc1.isLeft()) { if (!enterMenuItem) { pointer = constrain(pointer + 1, 0, 4); printPointer(pointer); } else { settings[0] = (int)constrain(settings[0] + 1, 0, 2); menuValuesNames[0] = modeValuesNames[settings[0]]; oled.clear(64, 0, 100, 5); oled.setCursor(64, 0); oled.print(menuValuesNames[0]); } } if (enc1.isClick()) { if (settings[0] == 2) { settings[1] = 2; settings[2] = 2; mainMenu(); } return; }// return возвращает нас в предыдущее меню } } void fan1(byte fanNumber) { isModeItemEnter = !isModeItemEnter; while (1) { enc1.tick(); if (enc1.isRight()) { if (!enterMenuItem) { pointer = constrain(pointer - 1, 0, 4); printPointer(pointer); } else { settings[fanNumber] = (int)constrain(settings[fanNumber] - 1, 0, 2); menuValuesNames[fanNumber] = fanValuesNames[settings[fanNumber]]; if (fanNumber == 1) { oled.clear(64, 10, 101, 15); } if (fanNumber == 2) { oled.clear(64, 20, 101, 25); } oled.setCursor(64, fanNumber); oled.print(menuValuesNames[fanNumber]); } } if (enc1.isLeft()) { if (!enterMenuItem) { pointer = constrain(pointer + 1, 0, 4); printPointer(pointer); } else { settings[fanNumber] = (int)constrain(settings[fanNumber] + 1, 0, 2); menuValuesNames[fanNumber] = fanValuesNames[settings[fanNumber]]; if (fanNumber == 1) { oled.clear(64, 10, 101, 15); } if (fanNumber == 2) { oled.clear(64, 20, 101, 25); } oled.setCursor(64, fanNumber); oled.print(menuValuesNames[fanNumber]); } } if (enc1.isClick()) { if (settings[fanNumber] == 0 || settings[fanNumber] == 1) { settings[0] = 1; mainMenu(); } if (settings[1] == 2 && settings[2] == 2) { settings[0] = 2; mainMenu(); } return; }// return возвращает нас в предыдущее меню } } long readVcc() { //функция чтения внутреннего опорного напряжения, универсальная (для всех ардуин) #if defined([B]AVR_ATmega32U4[/B]) || defined([B]AVR_ATmega1280[/B]) || defined([B]AVR_ATmega2560[/B]) ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #elif defined ([B]AVR_ATtiny24[/B]) || defined([B]AVR_ATtiny44[/B]) || defined([B]AVR_ATtiny84[/B]) ADMUX = _BV(MUX5) | _BV(MUX0); #elif defined ([B]AVR_ATtiny25[/B]) || defined([B]AVR_ATtiny45[/B]) || defined([B]AVR_ATtiny85[/B]) ADMUX = _BV(MUX3) | _BV(MUX2); #else ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #endif delay(2); // Wait for Vref to settle ADCSRA |= _BV(ADSC); // Start conversion while (bit_is_set(ADCSRA, ADSC)); // measuring uint8_t low = ADCL; // must read ADCL first - it then locks ADCH uint8_t high = ADCH; // unlocks both long result = (high << 8) | low; result = my_vcc_const * 1023 * 1000 / result; // расчёт реального VCC return result; // возвращает VCC }
Передатчик:
//Протокол // 0 - "режим" ожидает (2 - выкл, 1- ручной, 0 - авто) пример 0,2; // 1 - "вент 1" ожидает (1 - сток, 0 - приток, 2 - выкл // 1 - "вент 1" ожидает (1 - сток, 0 - приток, 2 - выкл //#define BTN_POWER 2 //#define BTN_FAN_1 3 //#define BTN_FAN_2 3 //#define BTN_AUTO 3 #include <EncButton.h> #include <SPI.h> #include "nRF24L01.h" #include "RF24.h" EncButton<EB_TICK, 2> powerButton; RF24 radio(9, 10); // "создать" модуль на пинах 9 и 10 Для Уно //RF24 radio(9,53); // для Меги byte address[][2] = {"NodeS", "SlaveS",}; //возможные номера труб unsigned long lightTimer, sendTimer, clickTimer; int settingsPayload[3]; int settingsAnswer[3]; int settingsHolder[3] = {2, 2 , 2}; int lastSettingsBeforeOff[3]; bool powerState1 = 1; bool settingsChangedFromBase = false; void setup() { Serial.begin(9600); //открываем порт для связи с ПК radio.begin(); //активировать модуль radio.enableAckPayload(); //разрешить отсылку данных в ответ на входящий сигнал radio.openReadingPipe(1, address[0]); //хотим слушать трубу 0 radio.setChannel(0x67); //выбираем канал (в котором нет шумов!) radio.setPALevel (RF24_PA_LOW); //уровень мощности передатчика. На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX radio.setDataRate (RF24_1MBPS); //скорость обмена. На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS radio.powerUp(); //начать работу radio.startListening(); radio.writeAckPayload (1, &settingsHolder, sizeof(settingsHolder)); } void loop() { powerButton.tick(); byte pipeNo; if (radio.available()) { radio.read(&settingsPayload, sizeof(settingsPayload)); radio.writeAckPayload(1, &settingsHolder, sizeof(settingsHolder)); } if (millis() - lightTimer > 1000) { for (byte i = 0; i < 3; i++) { Serial.print(i); Serial.print(","); Serial.print(settingsHolder[i]); Serial.print(";"); } lightTimer = millis(); } // if (millis() - clickTimer > 500) { Serial.println(powerState1); clickTimer = millis(); } if (powerButton.isClick()) { Serial.println("On"); // powerState1 = !powerState1; // if (powerState1) { // // // } else { // // Serial.println("off"); // } } }
а библиотеку
nRF24L01.h откуда берёте?
А что, без askPayload двухсторонняя связь невозможна? Отсылайте ответ сами
а библиотеку
nRF24L01.h откуда берёте?
из папки libraries
Это сложно. Выключать передачу, включать слушателя, послушать, включить передачу. .ackPayload решает все мои проблемы.
Одно дело, что не работает изначально, а другое - работало, но перестало.
Попадались китайские модули где ackPayload не работал ни в какую! Приходилось выкручиваться.
ackPayload решает все мои проблемы.
при условии что он работает. Но он у половины китайских модулей не работает. Не ленитесь, делайте без askpayload, это проще
Одно дело, что не работает изначально, а другое - работало, но перестало.
Попадались китайские модули где ackPayload не работал ни в какую! Приходилось выкручиваться.
Ну у меня же работает, пока условие в код не добавляешь. Значит не в железе дело, а в кривом коде, осталось найти, что ему мешает. Мне знаний не хватает понять, что я сделал не так.
Ошибка тут в использовании nrf24.
Тут в коде и в описании куча ошибок. Передатчик называется в комментах приемником и наоборот.
Ключевым признаком проблемы указано мигание светодиода в строках 77-84, которые, на мой взгляд, вообще не имеют никакого отношения к сути
ТС может вы еще раз, более понятно, опишите проблему?
Да, вы правы, название скетчей перепутано, не заметил.
Проблема заключается в том, что при раскомментировании следующих строк (на приемнике):
75
if
(powerButton.isClick()) {
76
Serial
.println(
"On"
);
77
// powerState1 = !powerState1;
78
// if (powerState1) {
79
//
80
//
81
// } else {
82
//
83
// Serial.println("off");
84
// }
85
86
}
Перестает работать .writeAckPayload на строке 54,
52
if
(radio.available()) {
53
radio.read(&settingsPayload,
sizeof
(settingsPayload));
54
radio.writeAckPayload(1, &settingsHolder,
sizeof
(settingsHolder));
55
}
Остальные проблемы - последствия вышеописанной ситуации. Передатчик перестает читать ответ приемника.