2025/05/25 電子書籍「Markdown記法大全+Pandoc変換マスター: 初心者から脱却!HTML・PDF・電子書籍を自在に出力する方法」を出版

M5Stackの画面キャプチャー

M5Stack

M5Stackの画面のキャプチャー方法について説明します。

M5Stack良いのアプリケーションの説明や、プログを書くときに画面のキャプチャーが欲しいときがあります。その様な時に、便利なキャプチャ・ツールをご紹介します。

M5Stack 画面キャプチャ・ツール

marutsuのHPで、M5Stack 画面キャプチャ・ツール「Screen_Capture_BMP」が紹介されています。

ライブラリ化はされておらず、ヘッダファイルとして公開されていますので、自由に改変が可能と説明されています。表示データを読み出し、BMPフォーマットの画像データを作成してファイルに保存します。このツールは、「M5Unified」でも使用できます。

ブログや取説に!M5Stack画面キャプチャ・ツール「Screen_Capture_BMP」 | マルツオンライン
ブログや取説に!M5Stack画面キャプチャ・ツール「Screen_Capture_BMP」ボタンでショット撮影!プログラムに埋め込んで連続撮影!320×240pxの美スクリーンをGET

基本仕様

 以下、M5Stack 画面キャプチャ・ツール「Screen_Capture_BMP」の基本仕様です。

  • 関数名:void Screen_Capture_BMP(char *file_name)
    ※全画面をキャプチャし、file_nameで指定されたファイル名でTF(microSD)カードにBMP形式で保存
  • 関数が呼ばれた時点でTF(microSD)カードが挿入されていない場合は、画面にエラー表示し動作停止
  • ファイルが何らかの原因でオープンできなかった場合も画面にエラー表示をし動作停止
  • キャプチャ中は画面バックライトの明るさを5%まで暗くする
    ※キャプチャ中であることを示すためと、使用するTF(microSD)カードによって書き込み中の電圧変動により、
    書き込みに失敗する例があったため実施(バックライトを暗くすることにより解消された)

使用方法

以下、M5Stack 画面キャプチャ・ツール「Screen_Capture_BMP」を使用する手順です。

  1. ヘッダーファイル”Capture.h”のインクルード
  2. void Screen_Capture_BMP(char *file_name)関数を呼び出す
    • 通常、ボタンを押されたときに呼び出します。

ヘッダーファイルのインクルード

#include "Capture.h"

サンプルコード

何らかのトリガーで、Screen_Capture_BMP関数を呼び出します。

以下は、ボタンA 及びデュアルボタンユニット [U025] を使用した例です。

ボタンAが押されたら画面をキャプチャします
#include <Arduino.h>
#include <M5Unified.h>

#include "Capture.h"

int counter;
char fn[100];

void setup() 
{
  M5.begin();
  M5.Lcd.fillScreen(TFT_BLACK);
  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
  M5.Lcd.setTextDatum(TL_DATUM);
  M5.Lcd.setTextFont(4);
  M5.Lcd.setTextSize(1);
  counter = 0;
}

void loop() 
{
  delay(100); //100msウェイト
  M5.update();

  M5.Lcd.setCursor(10, 30);
  M5.Lcd.printf("Counter = %d", counter);
  if(M5.BtnA.isPressed())
  {
    sprintf(fn, "/Counter%d.bmp", counter);
    Screen_Capture_BMP(fn);
    counter++;
  }
}
M5Stack用デュアルボタンユニット [U025]を使用

必要な部分のコードのみ、抜き出しています。

#include <Capture.h>

#define RED_BUTTON	26
#define BLUE_BUTTON	36

int last_value_red	= 0;
int cur_value_red	= 0;
int last_value_blue	= 0;
int cur_value_blue	= 0;

void setup()
{

	// ------------------------------------------------
	pinMode(RED_BUTTON,		INPUT);
	pinMode(BLUE_BUTTON,	INPUT);
	// ------------------------------------------------
	cur_value_red	= digitalRead(RED_BUTTON);
	cur_value_blue	= digitalRead(BLUE_BUTTON);
	last_value_blue	= cur_value_blue;
	last_value_red	= cur_value_red;
}

char fn[100];
int CapNum	= 1;

void loop(void)
{
	M5.delay(1);
	M5.update();

	// ------------------------------------------------
	cur_value_red	= digitalRead(RED_BUTTON);
	cur_value_blue	= digitalRead(BLUE_BUTTON);

	if(cur_value_blue != last_value_blue)
	{
		sprintf(fn, "/Counter%d.bmp", CapNum);
		Screen_Capture_BMP(fn);
		// M5.Speaker.tone(523.251, 100);
		CapNum++;

		last_value_blue		= cur_value_blue;
	}
	
	if(cur_value_red != last_value_red)
	{
		last_value_red		= cur_value_red;
	}
}

画面キャプチャ・ツール「Screen_Capture_BMP」の修正

画面キャプチャ・ツール「Screen_Capture_BMP」を修正して、縦長の画面表示の時にも正しくBMPファイルを正しく保存できるように修正しました。w_widthとw_heightの定義を追加し、条件コンパイルする形としています。

// M5Stack Screen Capture
// Copyright (c) 2022- @logic_star

#if 0
 #define w_width     320
 #define w_height    240
#else
 #define w_width     240
 #define w_height    320
#endif

#define BitImageSize  (w_width * w_height *3)  //画面キャプチャーイメージサイズ
#define PixelPerMeter (w_width / 4 * 100) //画面解像度

//Function entry
void Screen_Capture_BMP(char *file_name)
{
    uint8_t FrameBuffer[320*3];
    File    fp;
    int     x,y,i;
    const uint16_t BMP_header[14 + 40] =  //BMPファイルヘッダー定義
    { //File header 14 bytes
        'B','M',    //uint16_t bfType
        (BitImageSize & 0xff),((BitImageSize>>8) & 0xff),((BitImageSize>>16) & 0xff),0,  //uint32_t bfSize
        0,0,        //uint16_t bfReserved1
        0,0,        //uint16_t bfReserved2
        14+40,0,0,0,//uint32_t bfOffBits
        //Information header 40 bytes
        40,0,0,0,   //uint32_t  biSize
        (w_width & 0xff),((w_width >> 8)& 0xff),0,0,    //int32_t biWidth
        (w_height & 0xff), ((w_height >> 8)& 0xff), 0,0, //int32_t biHeight
        1,0,        //uint16_t biPlanes
        24,0,       //uint16_t biBitCount
        0,0,0,0,    //uint32_t biCompression
        (BitImageSize & 0xff),((BitImageSize>>8) & 0xff),((BitImageSize>>16) & 0xff),0,  //uint32_t biSizeImage
        (PixelPerMeter & 0xff),((PixelPerMeter>>8) & 0xff),0,0,    //int32_t biXPelsPerMeter
        (PixelPerMeter & 0xff),((PixelPerMeter>>8) & 0xff),0,0,    //int32_t biYPelsPerMeter
        0,0,0,0,    //uint32_t biClrUsed
        0,0,0,0 };  //uint32_t biClrImportant

    Serial.printf("Start %s\n", file_name);
    M5.Lcd.setBrightness(10);       //バックライトを暗くする
    if (!SD.begin(4))
    {
        M5.Lcd.println("NO SD CARD.");
        M5.Lcd.setBrightness(200);
        while (1) ;
    }
    fp = SD.open(file_name, FILE_WRITE);
    if(!fp)
    {
        M5.Lcd.println("File open error.");
        M5.Lcd.setBrightness(200);
        while (1) ;
    }
    for(i=0;i<(14+40);i++)  fp.write(BMP_header[i]);  //BMPファイルヘッダーの出力
    // for(y=239;y>=0;y--){                              //画面の下から上へスキャン
    // for(y=(w_height -1);y>=0;y--)

    for(y=319;y>=0;y--)
    {                              //画面の下から上へスキャン
        M5.Lcd.readRectRGB(0, y, w_width, 1, FrameBuffer);  //1ライン分の画面データの取得
        for(x=0;x<w_width*3;x+=3)
        {        //1ライン分のデータ出力
            fp.write(FrameBuffer[x+2]); //Blue
            fp.write(FrameBuffer[x+1]); //Green
            fp.write(FrameBuffer[x]);   //Red
        }
    }
    fp.close();
    M5.Lcd.setBrightness(200);      //バックライトの明るさを戻す
    Serial.printf("end %s\n", file_name);
}

saveToSD_24bit関数

LovyanGFXのサンプルの中に、「SaveBMP.ino」があります。この中のsaveToSD_24bit関数を使用しても画面のキャプチャーが可能です。以下は、saveToSD_24bit関数をベースに、一部修正したコードです。

  • examples/Standard/SaveBMP/SaveBMP.ino
#include <Arduino.h>
#include <M5Unified.h>
#include "FS.h"
#include <SD.h>		// If you use SD card, write this.
// #include <SPIFFS.h>	// If you use SPIFFS, write this.
#include <SPI.h>

bool saveToSD_24bit(char *filename)
{
    bool result = false;

    M5.Lcd.setBrightness(10);       //バックライトを暗くする
    if (!SD.begin(4))
    {
        M5.Lcd.println("NO SD CARD.");
        M5.Lcd.setBrightness(200);
        while (1) ;
    }
    File file = SD.open(filename, "w");
    if (file)
    {

        int width  = M5.Lcd.width();
        int height = M5.Lcd.height();

        int rowSize = (3 * width + 3) & ~ 3;

        lgfx::bitmap_header_t bmpheader;
        bmpheader.bfType        = 0x4D42;
        bmpheader.bfSize        = rowSize * height + sizeof(bmpheader);
        bmpheader.bfOffBits     = sizeof(bmpheader);

        bmpheader.biSize        = 40;
        bmpheader.biWidth       = width;
        bmpheader.biHeight      = height;
        bmpheader.biPlanes      = 1;
        bmpheader.biBitCount    = 24;
        bmpheader.biCompression = 0;

        file.write((std::uint8_t*)&bmpheader, sizeof(bmpheader));
        std::uint8_t buffer[rowSize];
        memset(&buffer[rowSize - 4], 0, 4);
        for (int y = M5.Lcd.height() - 1; y >= 0; y--)
        {
            M5.Lcd.readRect(0, y, M5.Lcd.width(), 1, (lgfx::rgb888_t*)buffer);
            file.write(buffer, rowSize);
        }
        file.close();
        result = true;
    } else {
        Serial.print("error:file open failure\n");
    }
    M5.Lcd.setBrightness(200);      //バックライトの明るさを戻す

    return result;
}

PNGファイルに保存

LovyanGFXのサンプルの中に、「SavePNG.ino」があります。この中のsaveToSD関数を使用して画面をキャプチャーして、PNGファイルに保存することが可能です。

以下のコメントありますので、注意してご使用ください。尚、PSRAMをもつM5Stack Core2では、画面全体のキャプチャーが可能でした。

ESP32の場合 192×192程度が上限です。
メモリ使用状況によってさらに縮みます。
ESP32でPSRAMが有効な場合は大きなサイズでも保存できる可能性があります。

以下は、saveToSD関数をベースに、一部修正したコードです。

  • examples/Standard/SavePNG/SavePNG.ino
#include <Arduino.h>
#include <M5Unified.h>
#include "FS.h"
#include <SD.h>		// If you use SD card, write this.
// #include <SPIFFS.h>	// If you use SPIFFS, write this.
#include <SPI.h>

bool savePNGToSD(char *filename)
{
    M5.Lcd.setBrightness(10);       //バックライトを暗くする
	if (!SD.begin(4))
	{
		M5.Lcd.println("NO SD CARD.");
		M5.Lcd.setBrightness(200);
		while (1) ;
	}

	// createPng関数で指定範囲の画像からPNG形式のデータを生成します。
	// SAMD51の場合 172x172程度が上限です。
	// ESP32の場合 192x192程度が上限です。
	// メモリ使用状況によってさらに縮みます。
	// ESP32でPSRAMが有効な場合は大きなサイズでも保存できる可能性があります。
	std::size_t dlen;
	std::uint8_t* png = (std::uint8_t*)M5.Lcd.createPng(&dlen, 0, 0, M5.Lcd.width(), M5.Lcd.height());
	if (!png)
	{
		Serial.print("error:createPng\n");
		return false;
	}

	Serial.print("success:createPng\n");

	bool result = false;
	File file = SD.open(filename, "w");
	if (file)
	{
		file.write((std::uint8_t*)png, dlen);
		file.close();
		result = true;
	} else {
		Serial.print("error:file open failure\n");
	}

	free(png);
    M5.Lcd.setBrightness(200);      //バックライトの明るさを戻す
	return result;
}
タイトルとURLをコピーしました