Многозадачность на ардуино

Тема в разделе "Проводная и беспроводная связь", создана пользователем НиколайСок, 11 июн 2016.

  1. Здравствуйте! Делаю свой первый проект для реализации умного дома. Суть заключается в следующем: Имеется дисплей 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);

     
    }
     
  2. Karabas

    Karabas Гик

    Избавтесь от Delay, он то вам все и тормозит , так же оптимизируте и удалите все, что не относится к функционалу (вывод в консоль и прочие) и подумайте над скоростью обновления дисплея.
     
  3. XCB

    XCB Гик

  4. От Delay я пробовал избавится, но не помогло. Да и про автомат конечный я знаю, но как применить конкретно к Nrf2401 пока не представляю. Хотелось бы разделить выполнения несвязанных друг с другом задачи. Например Nrf2401 принимает показания с датчиков без ущерба для включения и выключения светодиода, а получается пока цикл не пройдет светодиод не выключится или не включится зависимости от состояния на текущий момент.
     
  5. AlexU

    AlexU Гуру

    Если хотите попробовать потоки, то посмотрите на следующую реализацию: https://bitbucket.org/dferreyra/avr-threads/downloads. В этой библиотеке вроде как "честная" реализация потоков. Сам не пробовал...
     
    rico и Karabas нравится это.
  6. Karabas

    Karabas Гик

    Вот тут все вешается на секунду
    Код (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 миллисекунды славы" :D)
    * никаких ожиданий
     
    rico нравится это.
  7. Karabas

    Karabas Гик

    Вот именно, что "честная", интересно будет посмотреть, как оно в реале бегает, с тем же delay-ем и прочими while(1) { }
    Не особо вчитывался, но похоже оно использует таймеры, опять "нихватило ШИМ пинов", "ниработает IR" :)
     
  8. Karabas спасибо! вот это дельный совет. Попробую использовать вашу функцию
    Код (C++):

    if (millis() + 1000  >= lastTime) {
    // get data
    lastTime = millis();
    }
    что получится отпишусь.
     
  9. Lazzio

    Lazzio Нуб

    Николай, в итоге получилось? Я столкнулся с такой же проблемой многозадачности ...
    Может выложите итоговый скетч посмотреть? Буду очень признателен.