Добрый день, уважаемые форумчане!
Прошу помочь выявить и устранить проблему, а также подсказать более/наиболее удачный алгоритм работы системы, где пульт должен управлять цветом и частотой синхронного(!) мигания 10 автономных лент.
Собираю систему, состоящую из 1-го пульта и 10 приёмников.
Пульт и приёмники работают на Arduino Nano, передача сигналов осуществляется при помощи радиомодулей NRF24L01+PA+LNA на пульте и на приёмниках. Радиомодули питаются через специальные адаптеры питания для NRF24.
Алгоритм работы. На пульте есть два вращающихся потенциометра, при помощи которых настраивается цвет и частота мигания. Мигание осуществляется следующим образом: передатчик через раз передаёт на пульты в соответствующей ячейке массива то «0», то «1» и в зависимости от этого значения пульты либо гасят свою светодиодную ленту, либо включают. Частота мигания определяется частотой передачи сигналов «0» и «1», которая определяется значением, выдаваемым одним из потенциометров. Значение цвета определяется значением, выдаваемым вторым потенциометром.
Так как приёмников много, было решено отключить функцию проверки доставки сигнала и, что бы сообщения точно доходили до всех приёмников, просто слать по 20 раз подряд одно и то же сообщение: сначала 20 раз «включи такой-то цвет», а потом 20 раз подряд «погасни».
Но при таком топорном алгоритме возникла проблема с миганием ленты на приёмнике (пока только на одном испытываю). Во-первых, как ни старайся, невозможно увеличит частоту мигания ленты выше какого-то предела (раз пять в секунду, наверное). Во-вторых, судя по показателям монитора порта, частенько пропадают пакеты и мигание становится нерегулярным.
При питании через USB кабели от ПК проблем с потерей пакетов было меньше. Когда собрал на автономном питании с DC-DC преобразователями, банками 18650 и освободил платы Arduino от лишних проводов, пересадив питание радиомодулей и светодиодных лент на шины, проблема с пропуском мигания усугубилась крайне.
Ниже привожу скетчи и схемы приёмника и передатчика.
Заранее благодарю за конструктивные ответы.
Скетч пульта:
#include <nRF24L01.h> // Подключаем файл настроек из библиотеки RF24.
#include <RF24.h> // Подключаем библиотеку для работы с nRF24L01+.
RF24 radio(7, 10); // Создаём объект radio для работы с библиотекой RF24, указывая номера выводов модуля (CE, SS).
int potValue[4];// Объявляем массив для хранения и передачи данных (до 32 байт включительно).
boolean FlagSveta = true;//флаг, меняющий с каждым циклом значение на противоположное для создания мигания ленты
int NomerPaketa = 0;
#define color_pin A0 //сюда подключена средняя нога потенциометра
#define freq_pin A1 //сюда подключена средняя нога потенциометра
void setup(){ //
Serial.begin(9600);
radio.begin (); // Инициируем работу модуля nRF24L01+.
radio.setChannel (66); // был канал 27. Указываем канал передачи данных (от 0 до 125), 27 - значит передача данных осуществляется на частоте 2,427 ГГц.
radio.setDataRate (RF24_1MBPS); // Указываем скорость передачи данных (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS), RF24_1MBPS - 1Мбит/сек.
radio.setPALevel (RF24_PA_MAX); // Указываем уровень усиления передатчика (RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm).
radio.enableDynamicAck(); // Разрешаем выборочно отключать запросы подтверждения приема данных.
//radio.setAutoAck(1, false); // Запретить приёмнику отправлять пакеты подтверждения приема передатчику использующему адрес 1 трубы.
radio.openWritingPipe (0xAABBCCDD11LL); // Открываем трубу с адресом 0xAABBCCDD11 для передачи данных (передатчик может одновременно вещать только по одной трубе).
pinMode (2, OUTPUT); //выдаём с 2 ноги 5 Вольт для потенциометра цвета
pinMode (3, OUTPUT); //выдаём с 3 ноги 5 Вольт для потенциометра частоты
}
void loop(){
digitalWrite (2, HIGH); // включаем подачу питания 2 ногу для потенциометра цвета
digitalWrite (3, HIGH); // включаем подачу питания 3 ногу для потенциометра частоты
byte color = analogRead(color_pin)/4; //переменная "color" (цвет) типа байт считывает с аналоговой ноги потенциометра
if (color > 200) color = 200; else {} //ограничиваем диапазон значений цвета, что бы красный был только с одного крайнего положения потенциометра
byte freq = analogRead(freq_pin)/80+1; //переменная "freq" (частота) типа байт считывает с аналоговой ноги потенциометра
potValue[0] = FlagSveta;//записываем значение первой ячейки массива. Попеременно должно быть то 0, то 1.
potValue[1] = color;//записываем значение второй ячейки массива. Там будет шифр цвета ленты.
potValue[2] = freq;//записываем значение третьей ячейки массива. Там будет "сырая" частота.
potValue[3] = NomerPaketa;//записываем значение порядкового номера пакета для отслеживания потерь
for (byte CountTriesTrans = 0; CountTriesTrans < 20; CountTriesTrans++){ //определяем количество попыток отправки
radio.write(&potValue, sizeof(potValue), true); // Отправляем данные из массива potValue указывая весь размер массива в байтах.
//третий аргумент "true" работает совместно с "radio.enableDynamicAck();" из войд сетапа
}
FlagSveta = !FlagSveta;
NomerPaketa++;
// delay(50); // Устанавливаем задержку на 50 мс. В этом скетче нет смысла слать данные чаще.
// Так же задержка нужна для того, что бы приёмник успел выполнить свои действия над полученными данными (если таковые необходимы) до следующей передачи.
delay(1000/freq); //определяем долю секунды, которую нужно ждать, что бы послать следующий сигнал
//для отладки:
Serial.print(potValue[0]); Serial.println(" - должно чередоваться");
Serial.print(potValue[1]); Serial.println(" - цвет с потенциометра");
Serial.print(potValue[2]); Serial.println(" - сырая частота с потенциометра");
Serial.print(potValue[3]); Serial.println(" - порядковый номер пакета");
Serial.println("=============");
}```
**Скетч приемника:**
```#include <SPI.h> // Подключаем библиотеку для работы с шиной SPI.
#include <nRF24L01.h> // Подключаем файл настроек из библиотеки RF24.
#include <RF24.h> // Подключаем библиотеку для работы с nRF24L01+.
RF24 radio(7, 10); // Создаём объект radio для работы с библиотекой RF24, указывая номера выводов модуля (CE, SS).
int potValue[4]; // Массив для хранения значений потенциометров
uint8_t pipe; // Объявляем переменную в которую будет сохраняться номер трубы по которой приняты данные.
#include <FastLED.h> //подключаем библиотеку для светодиодов
#define NUM_LEDS 144 // How many leds in your strip?
// For led chips like WS2812, which have a data line, ground, and power, you just
// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock,
// ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN
// Clock pin only needed for SPI based chipsets when not using hardware SPI
#define DATA_PIN 2
//#define CLOCK_PIN 3
CRGB leds[NUM_LEDS];// Define the array of leds
void setup(){
Serial.begin(9600);
radio.begin(); // Инициируем работу nRF24L01+.
radio.setChannel (66); // Указываем канал передачи данных (от 0 до 125), 27 - значит приём данных осуществляется на частоте 2,427 ГГц.
radio.setDataRate (RF24_1MBPS); // Указываем скорость передачи данных (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS), RF24_1MBPS - 1Мбит/сек.
radio.setPALevel (RF24_PA_MAX); // Указываем мощность передатчика (RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm).
radio.openReadingPipe (1, 0xAABBCCDD11LL); // Открываем 1 трубу с адресом 1 передатчика 0xAABBCCDD11, для приема данных.
//radio.setAutoAck (1, false); // Запретить приёмнику отправлять пакеты подтверждения приема передатчику использующему адрес 1 трубы.
radio.startListening (); // Включаем приемник, начинаем прослушивать открытые трубы.
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
pinMode (3, OUTPUT);
}
void loop(){
digitalWrite (3, 1); //включаем пин 3 для подачи напряжения на ленту. При автономном питании от батареек не нужно.
if(radio.available(&pipe)){ // Если в буфере имеются принятые данные, то получаем номер трубы по которой эти данные пришли в переменную pipe.
radio.read( &potValue, sizeof(potValue) ); // Читаем данные из буфера в массив potValue указывая сколько всего байт может поместиться в массив.
if(pipe==1){ // Если данные пришли от 1 передатчика (по 1 трубе), то можно выполнить соответствующее действие ...
for (int i = 0; i < 4; i++) {
Serial.println(potValue[i]);
}
Serial.println("=====================");
byte FlagSveta = potValue[0]; //получаем значение флага для определения команды ленте вкл. или выкл.
byte color = potValue[1]; //получаем значение цвета светодиодов, ограничение диапазона и деление показателя потенциометра на 4 произведено на передатчике
byte MaxCountLed = 144; //На случай отладки при питании с Ардуино, когда тока мало, включаем мало диодов, если питание соответствует ленте, то все диоды включаем
if (FlagSveta==0){ //если переменная FlagSveta равна 1, то гасим число диодов равное MaxCountLed
for (byte CountLED = 0; CountLED < MaxCountLed; CountLED++){
leds[CountLED] = CRGB::Black; //гасим светодиоды
}
FastLED.show(); //функция передачи команды на ленту
}
else { //если переменная FlagSveta НЕ равна 0, то включаем число диодов равное MaxCountLed
for (byte CountLED = 0; CountLED < MaxCountLed; CountLED++){ //определяем сколько диодов из ленты включаем
leds[CountLED] = CHSV(color,255,100); //(1й знак - цвет, 2й знак - насыщенность ноль это белый, 3й знак - яркость)
}
FastLED.show(); //функция передачи команды на ленту. В данному случае на включение.
}
}
else {Serial.println("Ничего не пришло по 1 трубе");}
}
else {
//Serial.println("в буфере пусто");
//delay(1000);
}
//Serial.println("=====================");
}```