Общение двух Atmega328p по COM порту

Тема в разделе "Arduino & Shields", создана пользователем Rauf, 6 окт 2016.

  1. Rauf

    Rauf Нерд

    С выше указанными проблемами с помощью форумчан и гугла справились.
    Теперь возникла новая задача: Как вывести информацию на LCD 2004 используя только две нижние строки?
    В данные строки нужно мне выводить события.

    String messageA1 = "UPS 1 Inverter Off";
    String messageA2 = "On Battery";
    String messageA3 = "General Alarm";
    String messageA4 = "UPS 1 Inverter Off";

    String messageB1 = "UPS 2 Inverter Off";
    String messageB2 = "On Battery";
    String messageB3 = "General Alarm";
    String messageB4 = "UPS 2 Inverter Off";

    имеется две строки по одному событию на каждую строку, это в случае сработки. Как вывести 2 сработки понятно. но что если будет 4 события? Как мне зациклить вывод на LCD и как я понимаю в данном случае нельзя использовать Delay() так как все остальное не будет работать? Также когда состояние возвращается то необходимо удалять сообщение из списка отображаемого. Я не знаю как создать цикл вывода переменных с предварительным считыванием не навредив циклу прослушивания порта. Прошу помогите выстройте правильную логику или если есть похожий пример поделитесь. За ранее благодарю.
     
  2. Tomasina

    Tomasina Сушитель лампочек Модератор

    а чего сложного? Архитектура же простейшая - флаги состояний, да обработка событий:
    Код (C++):
    void loop()
    {
      readButtons(BUTTON_PIN);                             // сканируем кнопку
      sendSerial(RX_PIN);                                  // слушаем Serial
      dataManager();                                       // обработка данных
      sendData(TX_PIN);                                    // отправка данных
      displayData(msgLine1, msgLine2, msgLine3, msgLine4); // отображение информации
      buzzer(BUZZER_PIN);                                  // звуковые уведомления
      ledControl();                                        // моргание нужными LED
    }
     
    К примеру, обработчик событий для пищалки - если есть событие - работаем, нет события - тут же вылетаем из функции и не задерживаемся:
    Код (C++):
    void buzzer(byte pin)
    {
      switch(soundState)
      {
      case SND_BUTTON_PRESSED:
        digitalWrite(pin, HIGH);
        delay(8);
        digitalWrite(pin, LOW);
        break;

      case SND_CONNECT_ERROR:
        for(byte i=0; i<3; i++)
        {
          digitalWrite(pin, HIGH);
          delay(128);
          digitalWrite(pin, LOW);
          delay(128);
        }
        break;
      } // switch
    } // buzzer
     
    или для дисплея:
    Код (C++):
    void displayData(String message1, String message2);
    {
      if(TIMEOUT || needUpdateLCD)
      {
        lcd.SetCursor(0,0);
        lcd.print(message1);

        lcd.SetCursor(0,1);
        lcd.print(message2);

        lcd.SetCursor(0,2);
        if(message3 != "") lcd.print(message3);
        else lcd.print("                ");

        lcd.SetCursor(0,3);
        if(message4 != "") lcd.print(message4);
        else lcd.print("                ");
      } // if
    } // displayData
    А формирование чередования строк message3 и message3 проще сделать в dataManager() - если уже прошло
    2 сек, меняем содержимое переменной на следующую.
     
    Последнее редактирование: 14 окт 2016
  3. Rauf

    Rauf Нерд

    Благодарю за столь колоритный ответ. Но скорее всего я не совсем понятно изложил проблему.
    Начну по порядку
    Имеется два устройства ATMega328
    Связь между ними по UART код уже написан и прекрасно функционирует
    1 устройство имеет 8 входов для оповещения событий с разных UPS-ов, которые передаёт на первое устройство
    2 устройство имеет 8 LED лампочек и LCD 2004 дисплей - в случае сработки загорается лампочка на экране статично прописаны в 0 строке цифры а под ними появляется крестик соответствующий сработки и исчезает когда всё нормализуется.

    _ _ _ _ _ |1|2|3|4|5|6|7|8| _ _ _ _ _ _
    _ _ _ _ _ |x| | | | | |x| | _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    Остаются две нижние строки по 20 символов на которых нужно разместить ниже указанные сообщения.

    String messageA1 = "UPS 1 Inverter Off";
    String messageA2 = "UPS 1 On Battery";
    String messageA3 = "UPS 1 General Alarm";
    String messageA4 = "UPS 1 Inverter Off";

    String messageB1 = "UPS 2 Inverter Off";
    String messageB2 = "UPS 2 On Battery";
    String messageB3 = "UPS 2 General Alarm";
    String messageB4 = "UPS 2 Inverter Off";

    Пример: 1 сработка и 7 сработка - дисплей примет вид

    _ _ _ _ _ |1|2|3|4|5|6|7|8| _ _ _ _ _ _
    _ _ _ _ _ |x| | | | | |x| | _ _ _ _ _ _
    UPS 1 Inverter Off_ _ _ _ _ _ _ _ _ _
    UPS 2 General Alarm _ _ _ _ _ _ _ _

    Это не сложно!
    Но вдруг случится несчастье и будет три сработки или ещё больше?
    Картина будет приблизительно такой

    _ _ _ _ _ |1|2|3|4|5|6|7|8| _ _ _ _ _ _
    _ _ _ _ _ |x| |x |x| | |x| | _ _ _ _ _ _
    UPS 1 Inverter Off_ _ _ _ _ _ _ _ _ _
    UPS 2 General Alarm _ _ _ _ _ _ _ _
    UPS 1 General Alarm
    UPS 1 Inverter Off

    Последние два итого уже 4 сообщения нужно циклично крутить в двух строках
    Затем если состояние 4 исправится нужно убрать его из списка прокручиваемых сообщений и крутить оставшиеся три пока их состояние не изменится. Всего 8 сообщений
    Возможно ли это сделать на данном контроллере? Или если знаете как, может похожий код есть? Любая помощь, совет будет очень полезен. За ранее благодарю.
     
  4. Tomasina

    Tomasina Сушитель лампочек Модератор

    спасибо, но мне суть была понятна и из предыдущего сообщения. ;)
    И предложенная мной архитектура это сделать позволяет, не нарушая обмен по Serial.

    Подключать дисплей 2004 мне лень, поэтому пример на дисплее 2002, первую строку пришлось объединить со второй, на четвертую можно забить, ибо несущественно.


    Код (C++):
    #define BUTTON_PIN                 2
    #define RX_PIN                     0
    #define TX_PIN                     1
    #define BUZZER_PIN                 9
    #define BACKLIGHT_PIN              13
    #define LCD_HEIGHT                 4
    #define LCD_WIDTH                  20
    #define MSG_COUNT                  8

    boolean needUpdateLCD =            true;                    // флаг-признак необходимости одновить данные на дисплее
    String lcdBuffer[LCD_HEIGHT];
    const unsigned int updateTime =    2000;                    // интервал смены надписей в нижней строке
    const unsigned int flashTime =     250;                     // интервал мигания цифр (при аварии)
    boolean msgFlag[MSG_COUNT] =      {0, 0, 0, 0, 0, 0, 0, 0}; // флаги текущих состояний UPS (1 - это авария)
    const String message[MSG_COUNT] = {                         // сообщения об авариях
      "UPS 1 Inverter Off ",
      "UPS 1 On Battery   ",
      "UPS 1 General Alarm",
      "UPS 1 Inverter Off ",
      "UPS 2 Inverter Off ",
      "UPS 2 On Battery   ",
      "UPS 2 General Alarm",
      "UPS 2 Inverter Off ",
    };

    #define TIMEOUT(tm, lastTm) (millis() - lastTm > tm)         // принять как аксиому :)

    /*-----( Import needed libraries )-----*/
    #include <Wire.h>  // Get the LCD I2C Library here: https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
    #include <LiquidCrystal_I2C.h>

    // Set the pins on the I2C chip used for LCD connections:
    //                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
    LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
    //LiquidCrystal_I2C lcd(0x27);  // Set the LCD I2C address// A FEW use address 0x3F
    //LiquidCrystal_I2C lcd(0x27, BACKLIGHT_PIN, POSITIVE);  // Set the LCD I2C address

    // Прототипы функций (чтобы компилятор не ругался)
    void displayData();
    void dataManager();
    void buzzer(byte pin);
    void readButtons(byte pin);
    void readSerial(byte rxPin);
    void sendData(byte txPin);


    void loop()
    {
      readButtons(BUTTON_PIN);      // сканируем кнопку
      readSerial(RX_PIN);           // слушаем Serial
      dataManager();                // обработка данных
      sendData(TX_PIN);             // отправка данных
      displayData();                // отображение информации
      buzzer(BUZZER_PIN);           // звуковые уведомления
      ledControl();                 // моргание нужными LED
    }


    void dataManager()
    {
      // тут обработка каких-то данных
      // ...

      // эмуляция неисправности трех UPS через 1 мин после включения питания
      if (millis() > 60000)
      {
        msgFlag[1] = true;  // авария на 2-м UPS
        msgFlag[6] = true;  // авария на 7-м UPS
        msgFlag[7] = true;  // авария на 8-м UPS
      }

    }



    void displayData()
    {
      // формирование первой строки для вывода на дисплей
      static unsigned long lastFlashTime;
      lcdBuffer[0] = "1 2 3 4 5 6 7 8";          // шаблон для первой строки
      if (TIMEOUT(flashTime, lastFlashTime))     // каждые 0,25 сек формируем первую строку
      {
        static boolean flashFlag = true;
        if (flashFlag)
        {                                        // проверяем флаги на наличие ошибок
          for (byte ups = 1; ups <= MSG_COUNT; ups++)
            if (msgFlag[ups - 1]) lcdBuffer[0].replace(String(ups), "x"); // где ошибка - ставим хер
        }
        flashFlag = !flashFlag;
        lastFlashTime = millis();
        needUpdateLCD = true;
      }

      // формирование второй строки для вывода на дисплей
      static unsigned long lastUpdateTime;
      if (TIMEOUT(updateTime, lastUpdateTime))   // каждые 2 сек формируем вторую строку
      {
        lcdBuffer[1] = "                    ";   // 20 пробелов (у меня 20 знаков на дисплее)
        lastUpdateTime = millis();
        //needUpdateLCD = true;                  // не нужно, т.к. первая строка все равно чаще обновляет ЖК
        byte ups = 0;
        while (!msgFlag[ups])                    // проверяем флаги на наличие ошибок
        {
          ups++;
          if (ups >= MSG_COUNT)                  // если нет ни одной ошибки, то выходим
          {
            ups = 0;
            return;
          }
        }
        // сюда попадаем, если есть хоть одна ошибка
        static byte c = 0;
        while (!msgFlag[c]) c++;                 // пропускаем сообщения, к которым ошибка не относится
        lcdBuffer[1] = message[c];               // буферизуем сообщение
        c++;                                     // чередуем все сообщения об ошибках
        if (c >= MSG_COUNT - 1) c = 0;
      }

      // обновление данных на дисплее
      if (needUpdateLCD)
      {
        lcd.setCursor(0, 0);
        lcd.print(lcdBuffer[0]);

        lcd.setCursor(0, 1);
        lcd.print(lcdBuffer[1]);

        needUpdateLCD = false;
      } // if
    } // displayData



    void ledControl()
    {
    }



    void buzzer(byte pin)
    {
    }



    void readButtons(byte pin)
    {
    }



    void readSerial(byte rxPin)
    {
    }



    void sendData(byte txPin)
    {
    }


    void setup()
    {
      pinMode ( BACKLIGHT_PIN, OUTPUT );
      digitalWrite ( BACKLIGHT_PIN, HIGH );
      lcd.begin(LCD_WIDTH, LCD_HEIGHT);
      lcd.backlight(); // finish with backlight on
      lcd.clear();
    }
     
    Последнее редактирование: 15 окт 2016
    Rauf нравится это.
  5. Rauf

    Rauf Нерд

    Уважаемый добрый Человек, большое спасибо за проделанную работу. Я на видео ролике заметил в окне программы несколько вкладок. Я никогда с таким написанием не работал. Но смею предположить что в них находятся коды необходимые для функционирования написанного вами кода. Я прикреплю файлы рабочих устройств во второе устройство необходимо дописать визуальное оповещение на дисплей на 3 и 4 строку. Очень прошу взгляните на данные коды может туда вообще нельзя будет прописать посланный Вами код. И если есть возможность поделитесь файлом проекта с вкладками, очень хочу понять как идёт к ним обращение. За ранее благодарю.
     

    Вложения:

  6. Tomasina

    Tomasina Сушитель лампочек Модератор

    Мною выложен весь скетч, а по вкладам я всегда код разбиваю - мне так удобнее, когда не огромная простыня, а одна функция - это одна вкладка.
    Для создания вкладок есть махонький треугольник справа ;)
     
  7. Rauf

    Rauf Нерд

    Супер! Благодаря Вашему видео я научился разбивать программу на вкладки. Это очень удобно.
     
  8. uvlich

    uvlich Нуб

    Подскажите, а как потом собрать из вкладок один файл?
     
  9. Rauf

    Rauf Нерд

    Уважаемый Tomasina в коде эмуляция трёх ошибок а чередующаяся строка выдаёт только информацию о двух. Если есть время загляните пожалуйста.
     
  10. Rauf

    Rauf Нерд

    Уважаемый uvlich я заглянул в папку проекта который разбил на вкладки. И там создалось количество файлов равное количеству вкладок. И названия совпадают. Закрыл проект и заново открыл и он автоматом подхватывает всех их. Думаю так и должно быть и нет смысла объединять если был разбит для удобства.
     
  11. Rauf

    Rauf Нерд

    Исправил:
    Код (C++):
        // сюда попадаем, если есть хоть одна ошибка
        static byte c = 0;
        while (!msgFlag[c]) c++;                 // пропускаем сообщения, к которым ошибка не относится
        lcdBuffer[1] = message[c];               // буферизуем сообщение
        c++;                                     // чередуем все сообщения об ошибках
        if (c >= MSG_COUNT) c = 0;
      }
    я убрал -1 (if (c >= MSG_COUNT - 1) c = 0;) и заработало правильно.
    Теперь буду пытаться интегрировать коды. Ух заморское слово употребил :)
     
  12. uvlich

    uvlich Нуб

    Согласен. Только выкладывать одним файлом, хотя бы здесь, проще и удобнее для копирования, хотя, для скачивания можно зазиповать в один файл.
     
  13. Rauf

    Rauf Нерд

    Уважаемый Tomasina прошу Вас взглянуть на видео. Я совместил Ваш код с моим при тревожных сработках пишет на экране как и задумывалось. Но если сработок несколько после цикличного вывода информации проскакивают непонятные символы. Если Вам не трудно взгляните на код что я сделал не так. Заранее благодарю.

     
    Последнее редактирование: 17 окт 2016
  14. Tomasina

    Tomasina Сушитель лампочек Модератор

    по видео трудно восстановить исходники кода и понять что не так ;)
    Предположение: это из-за того, что убрали "-1" из кода (if (c >= MSG_COUNT - 1) c = 0, теперь в одном случае значение переменной выходит за пределы отведенной памяти и читает мусор, у меня было такое.

    P.S. "заранее" пишется слитно.
    P.P.S. ЖК--дисплей действительно такой инертный? Очень заметно при быстром моргании символов. OLED-дисплей меняет символы мгновенно.
     
  15. Rauf

    Rauf Нерд

    Код второго устройства
     

    Вложения:

  16. Rauf

    Rauf Нерд

    Для имитации событий можно в мониторе порта посылать ниже указанные символы.
    Код (C++):
    if(Serial.available()>0)
      {
        char letter = Serial.read();

        if (letter == 'Q')
        {
          SobitieAHigh();
        }
        if (letter == 'q')
        {
          SobitieALow();
        }
    //-------------------------------------------------------------------------------
        if (letter == 'Y')
        {
          SobitieBHigh();
        }
        if (letter == 'y')
        {
          SobitieBLow();
        }
    //-------------------------------------------------------------------------------
        if (letter == 'S')
        {
          SobitieCHigh();
        }
        if (letter == 's')
        {
          SobitieCLow();
        }
    //-------------------------------------------------------------------------------
        if (letter == 'T')
        {
          SobitieDHigh();
        }
        if (letter == 't')
        {
          SobitieDLow();
        }

    //-------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------
       
        if (letter == 'U')
        {
          SobitieEHigh();
        }
        if (letter == 'u')
        {
          SobitieELow();
        }

    //-------------------------------------------------------------------------------
        if (letter == 'V')
        {
          SobitieFHigh();
        }
        if (letter == 'v')
        {
          SobitieFLow();
        }
    //-------------------------------------------------------------------------------
        if (letter == 'W')
        {
          SobitieGHigh();
        }
        if (letter == 'w')
        {
          SobitieGLow();
        }
    //-------------------------------------------------------------------------------
        if (letter == 'X')
        {
          SobitieJHigh();
        }
        if (letter == 'x')
        {
          SobitieJLow();
        }
     
  17. Rauf

    Rauf Нерд

    Уважаемый Tomasina огромное человеческое спасибо. Всё получилось как вернул -1 на место
     
  18. Rauf

    Rauf Нерд

    Поторопился так при 8 контакте глюк полный все начинают срабатывать. И глюк на экране опять проскакивает.
     
  19. Tomasina

    Tomasina Сушитель лампочек Модератор

    вот правильная формула:
    Код (C++):
        if (c > MSG_COUNT - 1) c = 0;
     
  20. Rauf

    Rauf Нерд

    Код (C++):
    void displayData()
    {
      // формирование первой строки для вывода на дисплей
      static unsigned long lastFlashTime;
      lcdBuffer[0] = "1 2 3 4 5 6 7 8 ";          // шаблон для первой строки
      if (TIMEOUT(flashTime, lastFlashTime))     // каждые 0,25 сек формируем первую строку
      {
        static boolean flashFlag = true;
        if (flashFlag)
        {                                        // проверяем флаги на наличие ошибок
          for (byte ups = 1; ups <= MSG_COUNT; ups++)
            if (msgFlag[ups - 1]) lcdBuffer[0].replace(String(ups), "x"); // где ошибка - ставим хер
        }
        flashFlag = !flashFlag;
        lastFlashTime = millis();
        needUpdateLCD = true;
      }

      // формирование второй строки для вывода на дисплей
      static unsigned long lastUpdateTime;
      if (TIMEOUT(updateTime, lastUpdateTime))   // каждые 2 сек формируем вторую строку
      {
        lcdBuffer[1] = "                ";   // 20 пробелов (у меня 20 знаков на дисплее)
        lastUpdateTime = millis();
        //needUpdateLCD = true;                  // не нужно, т.к. первая строка все равно чаще обновляет ЖК
        byte ups = 0;
        while (!msgFlag[ups])                    // проверяем флаги на наличие ошибок
        {
          ups++;
          if (ups >= MSG_COUNT)                  // если нет ни одной ошибки, то выходим
          {
            ups = 0;
            return;
          }
        }
        // сюда попадаем, если есть хоть одна ошибка
        static byte c = 0;
        while (!msgFlag[c]) c++;                 // пропускаем сообщения, к которым ошибка не относится
        lcdBuffer[1] = message[c];               // буферизуем сообщение
        c++;                                     // чередуем все сообщения об ошибках
        if (c >= MSG_COUNT-1) c = 0;
      }

      // обновление данных на дисплее
      if (needUpdateLCD)
      {
        lcd.setCursor(0, 0);
        lcd.print(lcdBuffer[0]);

        lcd.setCursor(0, 1);
        lcd.print(lcdBuffer[1]);

        needUpdateLCD = false;
      } // if
    } // displayData