Здравствуйте! Делаю свой первый проект для реализации умного дома. Суть заключается в следующем: Имеется дисплей Nextion HMI, пытаюсь его приручить в качестве панели для управления освещением, а также сбора информации с датчиков температуры и влажности. Дисплей с двумя датчиками (один на улице, другой в соседней комнате) общается через радио модуль nrf2401. Также стоит датчик DH22 подключенный через ардуино уно к дисплею и стоит светодиод, имитирующий включения света управляемый через дисплей. Проблема заключается в том, что по отдельности части кода в функции Loop работают отлично, но когда собираешь все вместе, то светодиод включается и выключается через раз и также показания датчиков приходят с задержкой. Я понимаю, что нужна многозадачность, но как её реализовать в ситуации с nrf2401 не представляю. Извиняюсь за много букв и за код сильно не пинайте, недавно осваиваю ардуино. Код (C++): #include <SoftwareSerial.h> #include <Nextion.h> #include "DHT.h" #include <SPI.h> #include <Wire.h> #include "nRF24L01.h" #include "RF24.h" SoftwareSerial nextion(2, 3);// Nextion TX подключен к pin 2 и RX подключен pin 3 Ардуино Nextion myNextion(nextion, 9600); //создать объект с именем Nextion myNextion используя nextion последовательный порт @ 9600bps #define DHTPIN 4 #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 DHT dht(DHTPIN, DHTTYPE); long Temperature = 0, Pressure = 0; float TempNRF1 = 0; float TempNRF2 = 0; void getDataBMP180() { // заполняем данные в массив } typedef struct { int W; int X; float Y; float Z; } B_t; B_t duino1; B_t duino2; RF24 radio(9, 10); // определяем пины на радиомодуль const uint64_t pipe01 = 0xF0F1F2F3F1LL; const uint64_t pipe02 = 0xF0F1F2F3F4LL; #define U_NUM 0x71 // команда UART "Numerical variable data returns" #define PIN_OUT 6 // вывод для управления яркостью byte bright, bright_pwm; // значение яркости 0...100 и 0...255 (для ШИМ) char incomingByte; // входящие данные bool flag = false; // флаг того, что идут нужные нам данные int LEDpin = 7; void setup() { Serial.begin(9600); Wire.begin(); myNextion.init(); dht.begin(); radio.begin(); delay(100); radio.setDataRate(RF24_250KBPS); // Скорость передачи radio.setChannel(110); // Номер канала от 0 до 127 radio.setRetries(15, 15); // Кол-во попыток и время между попытками radio.openWritingPipe(pipe01);// Открываем канал передачи radio.openWritingPipe(pipe02);// Открываем канал передачи radio.openReadingPipe(1, pipe01); // Открываем один из 6-ти каналов приема radio.openReadingPipe(2, pipe02); // Открываем один из 6-ти каналов приема radio.startListening();// Начинаем слушать эфир TempNRF1 = 0; TempNRF2 = 0; pinMode(LEDpin, OUTPUT); pinMode(PIN_OUT, OUTPUT); } void loop() { //....................Включения выключения светодиода........................................ String message = myNextion.listen(); //получение сообщения от дисплея if (message == "65 0 4 1 ffff ffff ffff") { // если код кнопки совпадает с зарезервированным тогда включается светодиод digitalWrite(LEDpin, !digitalRead(LEDpin)); } //....................Данные с Радио модулей........................................ String TempNRF2 = String(duino2.Y); String TempNRF1 = String(duino1.Y); uint8_t pipeNum = 0; if (radio.available(&pipeNum)) { if (pipeNum == 1) { radio.read(&duino1, sizeof(duino1)); Serial.print("Arduino 1 = "); if (duino1.Y < 0 ) Serial.print(""); else Serial.print("+"); Serial.println(duino1.Y); } if (pipeNum == 2) { radio.read(&duino2, sizeof(duino2)); Serial.print("Arduino 2 = "); if (duino2.Y < 0) Serial.print(""); else Serial.print("+"); Serial.println(duino2.Y); } } //....................Плавное включения выключения светодиода........................................ while(nextion.available()){ // если пришли UART данные delay(20); incomingByte = nextion.read(); // считываем байт Serial.println(incomingByte, HEX); if(incomingByte == U_NUM){ // если нужные нам данные, flag = true; // то устанавливаем флаг, чтобы сохранить след. байт break; // и выходим из цикла, чтобы не сбросить } if(flag) { // условие сработает при след. посылке с нужным нам байтом яркости bright = (byte)incomingByte; // сохраняем байт значения яркости Serial.print("bright:"); Serial.println(bright, DEC); } flag = false; // сброс флага - остальные данные нас не интересуют } bright_pwm = map(bright, 0, 100, 0, 255); // пропорционально переносим диапазон 0...100 в 0...255 Serial.print("bright_pwm:"); Serial.println(bright_pwm, DEC); analogWrite(PIN_OUT, bright_pwm); // устанавливаем значение ШИМ //....................Датчик температуры и влажности........................................ delay(1000); float h = dht.readHumidity();// влажность DH22 float t = dht.readTemperature();//температура DH22 if (isnan(h) || isnan(t)) { return; } //....................Вывод на дисплей........................................ myNextion.setComponentText("t0", String(TempNRF2));//вывод на дисплей температуры с датчика №1 myNextion.setComponentText("t1", String(TempNRF1));//вывод на дисплей температуры с датчика №2 myNextion.setComponentText("t2", String(h));//вывод на дисплей влажности с датчика №3 myNextion.setComponentText("t3", String(t));//вывод на дисплей температуры с датчика №3 delay(100); }
Избавтесь от Delay, он то вам все и тормозит , так же оптимизируте и удалите все, что не относится к функционалу (вывод в консоль и прочие) и подумайте над скоростью обновления дисплея.
почитайте про конечный автомат и прерывания например тут http://wiki.amperka.ru/программирование:конечный-автомат http://wiki.amperka.ru/видеоуроки:10-прерывания-и-аппаратная-стабилизация
От Delay я пробовал избавится, но не помогло. Да и про автомат конечный я знаю, но как применить конкретно к Nrf2401 пока не представляю. Хотелось бы разделить выполнения несвязанных друг с другом задачи. Например Nrf2401 принимает показания с датчиков без ущерба для включения и выключения светодиода, а получается пока цикл не пройдет светодиод не выключится или не включится зависимости от состояния на текущий момент.
Если хотите попробовать потоки, то посмотрите на следующую реализацию: https://bitbucket.org/dferreyra/avr-threads/downloads. В этой библиотеке вроде как "честная" реализация потоков. Сам не пробовал...
Вот тут все вешается на секунду Код (C++): //....................Датчик температуры и влажности........................................ delay(1000); еще ловушка Код (C++): while(nextion.available()){ // если пришли UART данные delay(20); А если идут данные, но не те? Фляг не меняется, програма стоит. Ваш Loop на каждой итерации стоит больше секунды, плюс каждый раз перересовывает дисплей (а данные не обновлялись) и опять принудительно тормозится. Вынесете экран отдельной функцией, и обновляйте его по мере необходимости, данные получайте раз в секунду с использованием конструкции Код (C++): if (millis() + 1000 >= lastTime) { // get data lastTime = millis(); } Уменя есть похожее устройство, управляет светом, мониторит Nrf2401, ИК-датчик и еще по мелочи. Успевает получать и обрабатывать данные между переключениями ШИМа. К примеру если выполняется комадна "Уменьшить яркость 1-го канала до 10% за 1 сек", то в любой момен времени (в течении этой секунды на исполнение) он может получить другую команду и исполнить ее. Наверняка есть какие то умные слова, но для себя я решил, что главный цикл должен: * прокручиваться с максимально возможной скоростью * не иметь циклов переменной величины ( либо не превышать определенный размер) * все "потоки" исполняются 1 шаг за итерацию loop-a (loop несколько толстеет, но все в равной мере получают свои "4 миллисекунды славы" ) * никаких ожиданий
Вот именно, что "честная", интересно будет посмотреть, как оно в реале бегает, с тем же delay-ем и прочими while(1) { } Не особо вчитывался, но похоже оно использует таймеры, опять "нихватило ШИМ пинов", "ниработает IR"
Karabas спасибо! вот это дельный совет. Попробую использовать вашу функцию Код (C++): if (millis() + 1000 >= lastTime) { // get data lastTime = millis(); } что получится отпишусь.
Николай, в итоге получилось? Я столкнулся с такой же проблемой многозадачности ... Может выложите итоговый скетч посмотреть? Буду очень признателен.