RF24 + EncButton(gyverButton), перестает работать .writeAckPayload()

danial200
Offline
Зарегистрирован: 26.09.2021

Здравствуйте, ув. форумчане!

Пару слов о проекте:

Вентиляция на балконе с отслеживанием сигаретного дыма от соседей снизу. Логика работы - 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");
//    }

  }

}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

а библиотеку nRF24L01.h  откуда берёте?

b707
Offline
Зарегистрирован: 26.05.2017

А что, без askPayload двухсторонняя связь невозможна? Отсылайте ответ сами

danial200
Offline
Зарегистрирован: 26.09.2021

ua6em пишет:

а библиотеку nRF24L01.h  откуда берёте?

из папки libraries 

danial200
Offline
Зарегистрирован: 26.09.2021

b707 пишет:
А что, без askPayload двухсторонняя связь невозможна? Отсылайте ответ сами

Это сложно. Выключать передачу, включать слушателя, послушать, включить передачу. .ackPayload решает все мои проблемы. 

Green
Offline
Зарегистрирован: 01.10.2015

Одно дело, что не работает изначально, а другое - работало, но перестало.
Попадались китайские модули где ackPayload не работал ни в какую! Приходилось выкручиваться.

b707
Offline
Зарегистрирован: 26.05.2017

danial200 пишет:

ackPayload решает все мои проблемы. 


при условии что он работает. Но он у половины китайских модулей не работает. Не ленитесь, делайте без askpayload, это проще

danial200
Offline
Зарегистрирован: 26.09.2021

Green пишет:

Одно дело, что не работает изначально, а другое - работало, но перестало.
Попадались китайские модули где ackPayload не работал ни в какую! Приходилось выкручиваться.

Ну у меня же работает, пока условие в код не добавляешь. Значит не в железе дело, а в кривом коде, осталось найти, что ему мешает. Мне знаний не хватает понять, что я сделал не так.

rkit
Онлайн
Зарегистрирован: 23.11.2016

Ошибка тут в использовании nrf24.

b707
Offline
Зарегистрирован: 26.05.2017

Тут в коде и в описании куча ошибок. Передатчик называется в комментах приемником и наоборот.
Ключевым признаком проблемы указано мигание светодиода в строках 77-84, которые, на мой взгляд, вообще не имеют никакого отношения к сути

ТС может вы еще раз, более понятно, опишите проблему?

danial200
Offline
Зарегистрирован: 26.09.2021

b707 пишет:
Тут в коде и в описании куча ошибок. Передатчик называется в комментах приемником и наоборот. Ключевым признаком проблемы указано мигание светодиода в строках 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   }

Остальные проблемы - последствия вышеописанной ситуации. Передатчик перестает читать ответ приемника.