【それM5StickCでも出来るもん】【第2話】ATOMDisplayっぽいのをESP_8_BIT_composite(ビデオ出力)にLovyanGFXを絡めて作る話

#1. はじめに

[昨日の投稿](https://qiita.com/chrmlinux/items/c66ca9ff5abcc9560902)で　LovyanGFX の製作者さんやlang-shipさんからご助言を頂けました
ありがとうございます
おかげさまで　全画面の転送に成功いたしましたっ

#2. 全ソース
``` ESPDisplay.v0.ino
/*

 +---------------------+                       +---------------------+
 | LovyanGFX Sprite    |                       |ESP_8_BIT_composite  |
 | 256 x 240 x RGB332  | spr.readRect          |256 x 240 x RGB332   |
 |                     | **getFrameBufferLines |                     |
 |                     |            =>         |                     | 
 |                     |                       | RCA(G26)            |
 |                     |                       | GND                 |
 +---------------------+                       +---------------------+

  #### caution ####
  edit ESP_8_BIT_composite.cpp:25   #define USE_DAC_CH (DAC_CHANNEL_2)
  edit ESP_8_BIT_composite.cpp:116  dac_output_enable (USE_DAC_CH);
  edit ESP_8_BIT_composite.cpp:687  dac_output_disable(USE_DAC_CH);

*/

#include "Lcd.h"
#include "Video.h"

uint32_t getFps()
{
  static uint32_t psec = 0;
  static uint32_t cnt = 0;
  static uint32_t fps = 0;
  uint32_t sec = 0;

  sec = millis() / 1000;
  ++cnt;
  if (psec != sec) {
    psec = sec;
    fps = cnt;
    cnt = 0;
  }
  return fps;
}

void setup()
{
  Serial.begin( 115200 );
  lcd.init();
  lcd.setRotation(1);
  lcd.setBrightness(128);
  lcd.fillScreen(TFT_BLACK);
  lcdWidth = lcd.width();
  lcdHeight = lcd.height();

  Serial.printf("lcd:%d,%d\n", lcdWidth, lcdHeight);
  Serial.printf("vdo:%d,%d\n", videoWidth, videoHeight);

  spr.setColorDepth(8);
  spr.createSprite(videoWidth, videoHeight);
  //  spr.setFont(&lgfxJapanMincho_16); // Japanese font ready !
  setupVideo();
}

void draw()
{
  uint16_t stp = 8;
  uint16_t hstp = stp/2;
  uint16_t cx = 0;
  uint16_t cy = 0;
  static uint16_t x = cx;
  static uint16_t y = cy;
  static uint16_t lx = stp;
  static uint16_t ly = stp;

  spr.fillScreen(TFT_BLACK);

  for (int x = 0; x < videoWidth;  x += stp) {
    spr.drawLine(x, 0, x, videoHeight - 1, TFT_BLUE);
  }
  for (int y = 0; y < videoHeight; y += stp) {
    spr.drawLine(0, y, videoWidth - 1, y, TFT_BLUE);
  }
  if ((x > videoWidth) || (x < 0)) lx = lx * -1;
  if ((y > videoHeight) || (y < 0)) ly = ly * -1;
  x = x + lx;
  y = y + ly;
  spr.drawLine(x+hstp, 0, x+hstp, videoHeight - 1, TFT_GREEN);
  spr.drawLine(0, y+hstp, videoWidth - 1, y+hstp, TFT_GREEN);
  spr.fillRect(x + 1, y + 1, stp - 2, stp - 2, TFT_RED);

  spr.setCursor(4, 8);
  spr.printf("%3dx%3d %3dfps [%3d,%3d]", videoWidth, videoHeight, getFps(), x, y);
  spr.setCursor(100, 220);
  spr.printf("Thx. Lovyan03/lang-ship");

}

void loop()
{
  draw();
  execCapture(); // Capture and send video
  delay(1);
}
```
``` Lcd.h
#define _M5DISPLAY_H_
class M5Display {};

//#include <M5StickC.h>
//#include <M5Stack.h>
//#include <M5Atom.h>

#include <LovyanGFX.hpp>
static LGFX lcd;
static LGFX_Sprite spr;

static uint16_t lcdWidth = 0;
static uint16_t lcdHeight = 0;
```
``` Video.h
#include "ESP_8_BIT_composite.h"
ESP_8_BIT_composite video(true /* = NTSC */);

static bool _videoEnable = false;
static const uint16_t videoHeight = 240;
static const uint16_t videoWidth  = 256;
static const uint16_t videoSize = (videoWidth * videoHeight);
static uint8_t _videoBuf[videoSize];

void setupVideo()
{
  video.begin();
}

void sendVideo()
{
  uint8_t **videoFb = video.getFrameBufferLines();
  uint16_t srcPos = 0;
  uint16_t ypos = 0;
  for (uint16_t i = 0; i < videoHeight; i++) {
    memcpy(&videoFb[ypos][0], &_videoBuf[srcPos], videoWidth);
    srcPos += videoWidth;
    ypos ++;
  }
  video.waitForFrame();
  videoFb = NULL;
}

void execCapture()
{
  uint16_t lines = 1; // lcd captire lines 1..20
  uint8_t pxls[videoWidth * lines];

  bool swap = lcd.getSwapBytes();
  uint16_t dstPos = 0;

  lcd.setSwapBytes(true);
  for (uint16_t y = 0; y < videoHeight; y += lines) {
    spr.readRect(0, y, videoWidth, lines, pxls);
    uint16_t srcPos = 0;
    for (uint16_t i = 0; i < lines; i++) {
      memcpy(&_videoBuf[dstPos], &pxls[srcPos], videoWidth);
      srcPos += videoWidth;
      dstPos += videoWidth;
    }
  }
  lcd.setSwapBytes(swap);
  sendVideo();
}
```
#3.まとめ
いつも見守ってくださるlang-shipさんの[ブログ](https://lang-ship.com/blog/)
素晴らしい高速な描画ルーチン[Lovyanさん](https://twitter.com/lovyan03)のGFXには感謝です
今後ともよろしくお願い申し上げます

#4.おまけ
さぁこれで準備は整った
本来の目的である ESPMiraCastっぽいのを作れます(謎
第3話をお楽しみに～