LoginSignup
9
2

More than 1 year has passed since last update.

【ESP Arduinoライブラリのご紹介】ESP_8_BIT_composite

Last updated at Posted at 2021-09-10

はじめに

私が今までかじって来たESPマイコンのArduinoライブラリについて紹介したいと思います。
これらの記事が、どなたかの何らかに貢献できれば幸いです。👍

ESP_8_BIT_composite Library

今回は、ESP_8_BIT_compositeライブラリについて簡単に説明します。

https://github.com/Roger-random/ESP_8_BIT_composite.git

概要

ESP32マイコンのI2Sピン(GPIO25)からコンポジット信号(NTSC or PALフォーマット)を出力できます。
Adafruit GFX Library描画ライブラリを使用しています。
アナログTVやデジタルTVの黄色いRCAコネクタに入力すると、アナログ映像(NTSC)でフォント、アニメーションGIF、BITMAP、線、円、ポリゴンなどを表示させることができます。4:3表示で解像度は480i(インターレース)です。カラー表示ができます。(8bitカラー:256色)

お試し環境

このライブラリは次の環境でビルドを行い動作を確認しました。

  • ソフト
    • VSCode
    • PlatformIO(VSCode拡張機能)
    • arduino-esp32 ライブラリ
  • ハード
    • M5 ATOM Lite(ESP32-PICO)

動作の様子

テレビはメルカリで買ったSHARP LC-13S4(3000円)です。ATOM LiteのG25ポートをTVのRCA端子(映像)に接続しました。(言わずもがなGNDも接続しています)

サンプルコード

riraosan/ESP_8_BIT_composite

フォントを表示させるために、ESP_8_BIT_GFXクラスをEfontWrapperクラスから派生しています。
Adafruit_GFXライブラリのフォント以外のフォント(日本語、韓国語、etc)が使いたい方はこちらをお使いください。
本家Roger-random/ESP_8_BIT_composite.gitにはプルリクエストを出していません。

tanakamasayuki/efont

EfontWrapperクラスは私がefontライブラリへプルリクエストを出してマージしてもらったものです。よろしければどうぞ。
#もしかすると、こちらのライブラリのEfontWrapperクラスはコンパイルエラーになるかもしれません。そのときはriraosan/efont.git(fork)のほうを使ってください。

riraosan/ESP_8_BIT_composite_Sample

こちらは私がmodifyしたサンプルになります。よろしければご参考になさってください。
test_efontブランチのほうにefontを使ったサンプルアプリをアップしています。
platformio.iniを見ればライブラリの依存関係がわかると思います。
SHARP製液晶アナログTVの電源をONするための、赤外線リモコンライブラリをおまけでくっつけてます。要らなければ削除してください。

main.cpp
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <AnimatedGIF.h>
#include <ESP_8_BIT_GFX.h>
#include "non_4b_gif.h"
#include <IRremoteESP8266.h>
#include <IRsend.h>

const uint16_t kIrLed = 12;  // ESP8266 GPIO pin to use. Recommended: 4 (D2).

IRsend irsend(kIrLed);  // Set the GPIO to be used to sending the message.

// Create an instance of the graphics library
ESP_8_BIT_GFX videoOut(true /* = NTSC */, 16 /* = RGB565 colors will be downsampled to 8-bit RGB332 */);
AnimatedGIF gif;

// Vertical margin to compensate for aspect ratio
constexpr int margin = 10;

constexpr int _gif_offset_x  = (256 - 180) / 2 - 10;
constexpr int _text_offset_y = 112;
constexpr int _text_offset_x = _gif_offset_x - 10;

// Draw a line of image to ESP_8_BIT_GFX frame buffer
void GIFDraw(GIFDRAW *pDraw) {
    uint8_t *s;
    uint16_t *d, *usPalette, usTemp[320];
    int x, y;

    usPalette = pDraw->pPalette;
    y         = pDraw->iY + pDraw->y;  // current line

    s = pDraw->pPixels;
    if (pDraw->ucDisposalMethod == 2)  // restore to background color
    {
        for (x = 0; x < pDraw->iWidth; x++) {
            if (s[x] == pDraw->ucTransparent)
                s[x] = pDraw->ucBackground;
        }
        pDraw->ucHasTransparency = 0;
    }
    // Apply the new pixels to the main image
    if (pDraw->ucHasTransparency)  // if transparency used
    {
        uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent;
        int x, iCount;
        pEnd   = s + pDraw->iWidth;
        x      = 0;
        iCount = 0;  // count non-transparent pixels
        while (x < pDraw->iWidth) {
            c = ucTransparent - 1;
            d = usTemp;
            while (c != ucTransparent && s < pEnd) {
                c = *s++;
                if (c == ucTransparent)  // done, stop
                {
                    s--;  // back up to treat it like transparent
                } else    // opaque
                {
                    *d++ = usPalette[c];
                    iCount++;
                }
            }            // while looking for opaque pixels
            if (iCount)  // any opaque pixels?
            {
                for (int xOffset = 0; xOffset < iCount; xOffset++) {
                    videoOut.drawPixel(pDraw->iX + x + xOffset, margin + y, usTemp[xOffset]);
                }
                x += iCount;
                iCount = 0;
            }
            // no, look for a run of transparent pixels
            c = ucTransparent;
            while (c == ucTransparent && s < pEnd) {
                c = *s++;
                if (c == ucTransparent)
                    iCount++;
                else
                    s--;
            }
            if (iCount) {
                x += iCount;  // skip these
                iCount = 0;
            }
        }
    } else {
        s = pDraw->pPixels;
        // Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
        for (x = 0; x < pDraw->iWidth; x++) {
            videoOut.drawPixel(_gif_offset_x + x, margin + y, usPalette[*s++]);
        }
    }
} /* GIFDraw() */

void setup() {
    irsend.begin();
    delay(1000);
    irsend.sendPanasonic(0x555A, 0xF148688B);
    delay(10);
    irsend.sendPanasonic(0x555A, 0xF148688B);
    delay(2000);

    videoOut.begin();
    videoOut.copyAfterSwap = true;  // gif library depends on data from previous buffer
    videoOut.fillScreen(0);
    videoOut.waitForFrame();

    gif.begin(LITTLE_ENDIAN_PIXELS);
}

void loop() {
    if (gif.open((uint8_t *)non_4b_gif, 2252208, GIFDraw)) {
        while (gif.playFrame(true, NULL)) {
            videoOut.setTextSize(1);
            videoOut.setTextColor(0xf800, 0x0000);
            videoOut.printEfont("          By using             ", _text_offset_x - 12, _text_offset_y + 16 * 2);
            videoOut.setTextColor(0xFFFF, 0x03e0);
            videoOut.printEfont("ESP_8_BIT_composite Library    ", _text_offset_x - 12, _text_offset_y + 16 * 3);
            videoOut.setTextColor(0xFFFF, 0x001f);
            videoOut.printEfont("AnimatedGIF Library            ", _text_offset_x - 12, _text_offset_y + 16 * 4);
            videoOut.setTextColor(0xFFFF, 0xf800);
            videoOut.printEfont("EfontWrapper Library           ", _text_offset_x - 12, _text_offset_y + 16 * 5);

            videoOut.waitForFrame();
        }
        videoOut.waitForFrame();
        gif.close();
    }
}

気をつけること

  • 色数は256色です。NTSCフォーマットですので、テレビによって発色が異なります。そのような前提でNTSCフォーマットは設計されているようです。(個人的解釈)
  • releaseモードでビルドすること。Debugモードでビルドすると、垂直同期信号が不安定になり画像が細かい縦揺れを起こします。
  • このライブラリだけでは音はでません。コンポジット信号を出力する目的でI2Sチャンネルを一つ使用しています。ESP32から音を出すにはPWM信号を他のGPIOから出力してください。もしくは、残りのI2Sチャンネルを初期化して、内蔵DACからモノラル音声を出力してください。MP3等の音声ファイルはSPIFFSを使って内蔵フラッシュかSDカードから読み込めばよいと思います。

さいごに

おもしろいESP32ライブラリがありましたら教えて下さい。よろしく!:pray:

追記2021-10-18

応用例?

Weather Stationのデータをメルカリで買ったアナログ液晶テレビ(3000円)の大画面で確認してみる。
画質と発色は悪いけどね。これでいいんじゃないの?👍
246153462_2110460712440513_4760729400736712406_n.jpg

(了)

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2