🤖Have you ever tried Chat.M5Stack.com before asking??😎
    M5Stack Community
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Register
    • Login

    Урок 11. Подключение к телевизору. Трансляция погоды

    Русскоязычный форум
    1
    1
    4.4k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • DimiD
      Dimi
      last edited by Dimi

      Цель урока

      Привет! Сегодня мы научимся подключать телевизор к M5STACK и выводить на него изображения и текст по средствам видеосигнала (рис. 1).

      Рисунок 1

      Необходимо сделать так, чтобы шлюз M5 black периодически получал информацию о погоде на текущий момент с сайта https://openweathermap.org и передавал её по радиоканалу 433 МГц, а M5 gray принимал эту информацию и выводил в графическом виде на телевизор.
      В M5 black установлена TF-карта с параметрами Wi-Fi сетей и парметрами сервиса openweathermap.org.

      При включении устройства пользователю будет предложено вставить карту памяти. На карте памяти будет находится следующие файлы (рис. 1.1):

      • файл с известными Wi-Fi-сетями (system/wifi.ini);
      • файл стилей (system/openweather.ini) (рис. 1.2).

      Рисунок 1.1. Содержимое папки system

      Рисунок 1.2. Содержимое файла openweather.ini

      Краткая справка

      Под видеосигналом понимается электрический сигнал специальной формы (рис. 2), посредством которого передается телевизионное изображение.
      Понятие видеосигнала применимо как к аналоговому, так и к цифровому телевидению, а также к системам отображения информации компьютеров, основанных на электронно-лучевых трубках.
      Мгновенное значение освещенности на фоточувствительной поверхности телевизионного фотоприемника преобразуется в мгновенное значение напряжения на выходе этого фотоприемника. Таким образом, в видеосигнале напряжение пропорционально яркости в данной точке изображения. Эта часть видеосигнала носит название сигнала яркости и используется для передачи черно-белого видеосигнала или сигнала яркости в цветном видеосигнале. Диапазон передаваемых значений яркости определяется уровнем черного и уровнем белого сигналов в видеосигнале. Уровень черного является минимальным сигналом яркости и соответствует уровню синхроимпульсов. Он же одновременно является и опорным сигналом. Уровень белого соответствует максимальному уровню передаваемой яркости. Кроме сигнала яркости в видеосигнале присутствуют служебные составляющие, обеспечивающие синхронизацию сигнала между источником и приемником. Существуют два типа сигналов синхронизации – строчные и кадровые. Сигналы кадровой синхронизации обеспечивают передачу информации о времени начала каждого поля телевизионного изображения в видеосигнале, а также информацию о типе этого поля (четное или нечетное).

      Рисунок 2. Осциллограмма видеосигнала: 4 мкс - гасящий импульс, 8 мкс - информация о цвете, 52 мкс - сигнал яркости

      Перечень компонентов для урока

      • M5STACK (2 шт.);
      • кабель USB-C из стандартного набора;
      • карта памяти MicroSD на 4 ГБайт;
      • цветные провода из стандартного набора (тип розетка-вилка);
      • радиоприёмник MX-JS-05V;
      • радиопередатчик FS1000A;
      • телевизор с поддержкой видеосигнала PAL;
      • RCA-вилка;
      • паяльник и припой;
      • термоусадка.

      Начнём!

      Шаг 1. Зарегистрируемся на сайте openweathermap.org

      Для того, чтобы получать информацию о погоде необходимо зарегистрироваться на данном ресурсе. Регистрация абсолютно бесплатна и не займёт много врмени (рис. 3).

      Рисунок 3. Регистрация на сайте

      После регистрации в своем личном кабинете на сайте перейдите в раздел API keys и получите Key доступа к сервису.

      Рисунок 3.1. Получение API-ключа

      На этом регистрация на сайте завершена.

      Шаг 2. Сделаем переходник для подключения к ТВ

      Здесь без лишних слов - просто возьмите и сделайте. Главное, помните - цветной провод припаяйте к центральному контакту RCA-штекера (рис. 4 - 4.2).

      Рисунок 4.

      Рисунок 4.1

      Рисунок 4.2

      Шаг 3. Напишем скетч для шлюза на m5 black

      Для того, чтобы получить информацию с сайта с помощью GET-запроса напишем несложную функцию. В качестве аргумента функция принимает строку GET-запроса, и возвращает ответ сервера в качестве строки:

      String GET(String url) {
        while (true)
        {
          if ((wifiMulti.run() == WL_CONNECTED))
          {
            HTTPClient http;
            http.begin(url);
            int httpCode = http.GET();
            if (httpCode > 0)
            {
                if (httpCode == HTTP_CODE_OK)
                {
                  return http.getString();
                }
            }
            else
            {
              return (httpCode + "");
            }
            http.end();
          }
        }  
        return ""; 
      }
      

      Вся конфигурационная информация для сервиса openweathermap, как было сказано ранее, будет храниться на TF-карте. Поэтому нам необходимо написать функцию, для формирования GET-запроса:

      Обратите внимание - сервис nl.sxgeo.city - возвращает информацию о Вашем расположении на основании IP-адреса, который нет необходимости передавать в GET-запросе.

      String configOpenWeather() {
        String file = TFReadFile("/system/openweather.ini");
        if (file != "")
        {  
          String city = "&q=" + parseString(1, '\"', parseString(1, ':', parseString(5, ',', GET("http://nl.sxgeo.city/?")))); 
          String api_key = "&APPID=" + parseString(0, ' ', file);
          String app_id = "&id=" + parseString(1, ' ', file);
          String lang = "&lang=" + parseString(2, ' ', file);
          String units = "&units=" + parseString(3, ' ', file);
          String host = "http://api.openweathermap.org/data/2.5/weather?";
          String url_ = host + city + api_key + app_id + lang + units;
          return url_;
        }
        return "";
      }
      

      В одном из прошлых уроков мы рассматривали функцию, настраивающую Wi-Fi с помощью параметров полученных с TF-карты. В этот раз мы несколько модифицируем тело функции. Добавим возможность сохранения конфигурационного файла под операционной системой MacOS X. Дело в том, что Mac добавляет один спец. символ в конце строки, не как Windows - два.

      bool configWifiMulti() {
        /* Get WiFi SSID & password from wifi.ini from TF-card */
        String file = TFReadFile("/system/wifi.ini");
        if (file != "")
        {
          for (int i = 0; i < cntChrs(file, '\n'); i++)
          {
            String wifi = parseString(i, '\n', file);
            wifi = wifi.substring(0, (wifi.length()));
            if (wifi[wifi.length() - 1] == '\r') wifi = wifi.substring(0, (wifi.length() - 1));
            String ssid = parseString(0, ' ', wifi);
            String pswd = parseString(1, ' ', wifi);
            char* ssid_ = strToChar(ssid);
            char* pswd_ = strToChar(pswd);
            if (wifiMulti.addAP(ssid_, pswd_))
            {
              return true;
            }
          }
        }
        return false;
      }
      

      С помощью следующих строк кода будем извлекать необходимые нам данные из ответа сервера:

      temp = parseString(2, ':', parseString(7, ',', weather));
      pres = parseString(1, ':', parseString(8, ',', weather));
      hum = parseString(1, ':', parseString(9, ',', weather));
      desc = parseString(1, '"', parseString(1, ':', parseString(4, ',', weather))); 
      weatherIcon = parseString(1, '"', parseString(1, ':', parseString(5, ',', weather)));
      

      Далее будем разделять данные при помощи спецсимвола и отправлять в радиоэфир:

      sendString("1" + String((char)0x1d) + temp);
      delay(1);
      sendString("2" + String((char)0x1d) + String(round(pres.toInt() * 0.75)));
      delay(1);
      sendString("3" + String((char)0x1d) + hum);
      delay(1);
      sendString("4" + String((char)0x1d) + desc);
      delay(1);
      sendString("5" + String((char)0x1d) + weatherIcon);
      delay(1);	
      

      На этом написание скетча для шлюза-передатчика закончено.

      Шаг 4. Теперь напишем скетч для приёмника на m5 gray

      Для вывода изображения на ТВ мы будем использовать библиотеку автора http://bitluni.net/esp32-composite-video/. Поскольку библиотека использует оба канала ЦАП, то пришлось несколько изменить библиотку, чтобы отключить левый канал. Я упростил процедуру использования библиотеки, поместив всё самое главное в заголовочный файл m5stack_tv.h:

      namespace m5stack_tv
      {
        #include "CompositeGraphics.h"
        #include "Image.h"
        #include "CompositeOutput.h"
        #include <soc/rtc.h>
        #include "font6x8.h"
        
        const int XRES = 320;
        const int YRES = 200;
      
        CompositeGraphics graphics(XRES, YRES);
        CompositeOutput composite(CompositeOutput::NTSC, XRES * 2, YRES * 2);
        Font<CompositeGraphics> font(6, 8, font6x8::pixels);
      
        char* strToChar(String str) {
          int len = str.length() + 1;
          char* buf = new char[len];
          strcpy(buf, str.c_str());
          return buf;
        }
      
        void compositeCore(void *data) {  
          while (true)
          {
            composite.sendFrameHalfResolution(&graphics.frame);
          }
        }
        
        void begin() {
          rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M);
          composite.init();
          graphics.init();
          graphics.setFont(font);
          xTaskCreatePinnedToCore(compositeCore, "c", 2048, NULL, 1, NULL, 0);
        }
      
        void setTextColor(int c) {
          graphics.setTextColor(c);
        }
        
        void setCursor(int x, int y) {
          graphics.setCursor(x, y);
        }
      
        void print(String str) {
          graphics.print(strToChar(str));
        }
      
        void drawBitmap(int x, int y, int w, int h, const unsigned char* img) {   
          Image<CompositeGraphics> img_(w, h, img);
          img_.draw(graphics, x, y);
        }
      
        void fillRect(int x, int y, int w, int h, int color = 0) {
          graphics.fillRect(x, y, w, h, color);
        }
      
        void drawDot(int x, int y, int color) {
          graphics.dotFast(x, y, color);
        }
      
        void clearScreen(int color = 0) {
          fillRect(0, 0, XRES, YRES, color);
        }
      }
      

      Обратите внимание: формат изображений отличается от того, что мы рассматривали в уроках по работе со встроенным дисплеем. Для того, чтобы конвертировать изображение для вывода его на ТВ необходимо воспользоваться программой конвертер из пункта "Downloads".

      Напишем функцию, которая в зависимости от кода иконки погоды будет выводить изображение на экран:

      void drawWeatherIcon(int x, int y, String str) {
        if (str == "01d") m5stack_tv::drawBitmap(x, y, _01d::xres, _01d::yres, _01d::pixels);
        else if (str == "01n") m5stack_tv::drawBitmap(x, y, _01n::xres, _01n::yres, _01n::pixels);
        else if (str == "02d") m5stack_tv::drawBitmap(x, y, _02d::xres, _02d::yres, _02d::pixels);
        else if (str == "02n") m5stack_tv::drawBitmap(x, y, _02n::xres, _02n::yres, _02n::pixels);
        else if (str == "03d") m5stack_tv::drawBitmap(x, y, _03d::xres, _03d::yres, _03d::pixels);
        else if (str == "03n") m5stack_tv::drawBitmap(x, y, _03n::xres, _03n::yres, _03n::pixels);
        else if (str == "04d") m5stack_tv::drawBitmap(x, y, _04d::xres, _04d::yres, _04d::pixels);
        else if (str == "04n") m5stack_tv::drawBitmap(x, y, _04n::xres, _04n::yres, _04n::pixels);
        else if (str == "09d") m5stack_tv::drawBitmap(x, y, _09d::xres, _09d::yres, _09d::pixels);
        else if (str == "09n") m5stack_tv::drawBitmap(x, y, _09n::xres, _09n::yres, _09n::pixels);
        else if (str == "10d") m5stack_tv::drawBitmap(x, y, _10d::xres, _10d::yres, _10d::pixels);
        else if (str == "10n") m5stack_tv::drawBitmap(x, y, _10n::xres, _10n::yres, _10n::pixels);
        else if (str == "11d") m5stack_tv::drawBitmap(x, y, _11d::xres, _11d::yres, _11d::pixels);
        else if (str == "11n") m5stack_tv::drawBitmap(x, y, _11n::xres, _11n::yres, _11n::pixels);
        else if (str == "13d") m5stack_tv::drawBitmap(x, y, _13d::xres, _13d::yres, _13d::pixels);
        else if (str == "13n") m5stack_tv::drawBitmap(x, y, _13n::xres, _13n::yres, _13n::pixels);
        else if (str == "50d") m5stack_tv::drawBitmap(x, y, _50d::xres, _50d::yres, _50d::pixels);
        else if (str == "50n") m5stack_tv::drawBitmap(x, y, _50n::xres, _50n::yres, _50n::pixels);
      }
      

      Сами изображения подключим заголовочным файлом:

      #include "weatherIcons/main.h"
      

      Запуск осуществляется вызовом метода:

      m5stack_tv::begin();
      

      Таким образом при получении информация о погоде будет немедленно отображаться на ТВ:

      void loop() { 
        if (radioRX.available(&k))
        {
          message("data accepted");
          radioRX.read(&j, sizeof(j));
          delay(1);
          message(j);
          int type = (parseString(0, (char)0x1d, j)).toInt();
          String data = parseString(1, (char)0x1d, j);
          if (type == 1) temp = data;
          else if (type == 2) pres = data;
          else if (type == 3) hum = data;
          else if (type == 4) desc = data; 
          else if (type == 5) icon = data;
          
          if (type > 0)
          {
            m5stack_tv::setTextColor(0);
            m5stack_tv::clearScreen(54);
            message("drawing on TV");  
            m5stack_tv::setCursor(140, 60);
            m5stack_tv::print("Temperature, C: " + temp);
            m5stack_tv::setCursor(140, 80);
            m5stack_tv::print("Humidity, %: " + hum);
            m5stack_tv::setCursor(140, 100);
            m5stack_tv::print("Pressure, mm Hg: " + pres);
            m5stack_tv::setCursor(140, 120);
            m5stack_tv::print(desc);
            drawWeatherIcon(30, 45, icon);
          }
        }
      }
      

      Шаг 5. Запуск!

      В разделе "Download" прилагается видео с демонстрацией работы. На этом урок завершён.

      Downloads

      • Скетчи и библиотека (GitHub): https://github.com/dsiberia9s/TV-out.-Weather-broadcast
      • Конвертер изображений для ТВ: https://yadi.sk/d/PzJVuAWj3UbLiv
      • Видео с демонстрацией работы (YouTube): https://youtu.be/OLJlK17hkDo
      1 Reply Last reply Reply Quote 0
      • First post
        Last post