Здравствуйте, ув. форумчане!
Пару слов о проекте:
Вентиляция на балконе с отслеживанием сигаретного дыма от соседей снизу. Логика работы - 5 датчиков MQ-5 за бортом, подключены к базовой станции (В прошивках - передатчик), к станции подключены два вентилятора со шторками и возможностью реверса, по обнаружении дыма на n-ных датчиках, будут запускаться различные сценарии приток-сток. Так же проект подразумевает дистанционное использование с помощью пульта на ардуине + oled + энкодер (arduino со встроенным wifi) или ручное управление на базовой станции с помощью кнопок без фиксации положения и индикацией состояния датчиков и режимов (адресная светодиодная лента).
Проблема заключается в следующем:
Мне нужна двусторонняя связь между модулями, я остановился на .writeAckPayload(). При раскомментировании строк 77-84 в скетче приемника, перестает работать расчет качества связи на передатчике, между пультом(передатчик) и баз. станцией (приемник). Перестает заходить в условие в строках 149-180 (я выделил строки, чтобы они подсвечивались, но в предпросмотре показывает, что ничего он не выделил), вываливается в блок else на строке 180, и в итоге Общие отравленные пакеты N штук, доставленных 0. В итоге связь 0%. Так же, пакет ack передатчик не получает. Если оставить только Serial.println в 77-84 в скетче приемника, то все работает.
Приемник получает все пакеты отправленные передатчиком.
Мои попытки решения:
-Эксперименты с настройками RF24 в блоке setup() у приемника и передатчика;
-Изначально, я использовал без millis(), просто в теле цикла, на приемнике и передатчике блок кода с отправкой и получением данных;
-Менял название переменной powerState, менял с true\false на 1\0;
-Пробовал назначение =true\=false
Внешние факторы:
При раскомментировании строк 77-84 в скетче приемника, на передатчике начинает моргать зеленый светодиод, вместо, почти, статичного горения.
Обратите внимание, что в передатчике используется ардуино нано со встроенным wifi, с антенной на плате, а на приемнике испольуется ардуино с отдельным модулем с усилием и стабилизатором питания 3.3v, возможно, это играет какую-то роль.
Прошу прощения за отсутствие макетной схемы.
В спойлерах находятся прошивки приемника и передатчика.
Пару слов о проекте:
Вентиляция на балконе с отслеживанием сигаретного дыма от соседей снизу. Логика работы - 5 датчиков MQ-5 за бортом, подключены к базовой станции (В прошивках - передатчик), к станции подключены два вентилятора со шторками и возможностью реверса, по обнаружении дыма на n-ных датчиках, будут запускаться различные сценарии приток-сток. Так же проект подразумевает дистанционное использование с помощью пульта на ардуине + oled + энкодер (arduino со встроенным wifi) или ручное управление на базовой станции с помощью кнопок без фиксации положения и индикацией состояния датчиков и режимов (адресная светодиодная лента).
Проблема заключается в следующем:
Мне нужна двусторонняя связь между модулями, я остановился на .writeAckPayload(). При раскомментировании строк 77-84 в скетче приемника, перестает работать расчет качества связи на передатчике, между пультом(передатчик) и баз. станцией (приемник). Перестает заходить в условие в строках 149-180 (я выделил строки, чтобы они подсвечивались, но в предпросмотре показывает, что ничего он не выделил), вываливается в блок else на строке 180, и в итоге Общие отравленные пакеты N штук, доставленных 0. В итоге связь 0%. Так же, пакет ack передатчик не получает. Если оставить только Serial.println в 77-84 в скетче приемника, то все работает.
Приемник получает все пакеты отправленные передатчиком.
Мои попытки решения:
-Эксперименты с настройками RF24 в блоке setup() у приемника и передатчика;
-Изначально, я использовал без millis(), просто в теле цикла, на приемнике и передатчике блок кода с отправкой и получением данных;
-Менял название переменной powerState, менял с true\false на 1\0;
-Пробовал назначение =true\=false
Внешние факторы:
При раскомментировании строк 77-84 в скетче приемника, на передатчике начинает моргать зеленый светодиод, вместо, почти, статичного горения.
Обратите внимание, что в передатчике используется ардуино нано со встроенным wifi, с антенной на плате, а на приемнике испольуется ардуино с отдельным модулем с усилием и стабилизатором питания 3.3v, возможно, это играет какую-то роль.
Прошу прощения за отсутствие макетной схемы.
В спойлерах находятся прошивки приемника и передатчика.
Прошивка передатчика:
/* Данный скетч делает следующее: передатчик (TX) отправляет массив
данных, который генерируется согласно показаниям с кнопки и с
двух потенциомтеров. Приёмник (RX) получает массив, и записывает
данные на реле, сервомашинку и генерирует ШИМ сигнал на транзистор.
by AlexGyver 2016
*/
#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");
// }
}
}
Изменено: