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

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

    Scheduled Pinned Locked Moved Русскоязычный форум
    1 Posts 1 Posters 5.1k Views 1 Watching
    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 Offline
      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

      Hello! It looks like you're interested in this conversation, but you don't have an account yet.

      Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

      With your input, this post could be even better 💗

      Register Login
      • First post
        Last post