ESP8266でJPEG画像をTFT LCDに表示する

esp826609.jpg

前回はSPIFFSの中に格納したBMPファイルを表示させましたが、JPEG、GIF、PNGなどのファイルも表示させたくなりますね。
調べてみたところ、これらの画像形式の中でESP8266で比較的扱いやすいのはJPEGのようです。

JPEG、GIF、PNGはいずれも圧縮アルゴリズムを使っていますが、GIFとPNGは辞書形式の圧縮符号化方式(GIFはLZW、PNGはZlib)を使っています。
このタイプの圧縮アルゴリズムは、辞書をRAM上に展開すると、比較的大きなワーキングメモリが必要になります。
一方、JPEGは計算量はGIFやPNGよりも大きい代わりに、ワーキングメモリはそれほど大きくないようです。
picojpegというメモリ消費の少ないデコーダ実装があり、これをArduino上で利用した例も見つかりました。

Arduino用JPEGデコーダ | 遊舎工房

今回は、上記のデコーダを使って前回同様にSPIFFS上に置いたJPEGファイルをLCDに表示させてみました。

まず、デコーダライブラリを下記からダウンロードしてArduino IDEに追加(スケッチ→Include Library→Add .ZIP Library…)します。

MakotoKurauchi/JPEGDecoder

このライブラリはSDカードでの利用が想定されていますので、SPIFFSで使うために、以下の部分をちょこっと変更します。
変更するファイルは「マイドキュメント\Arduino\Libraries\JPEGDecoder-master\JPEGDecoder.cpp」です。

修正前: #include <SD.h>
修正後: #include <FS.h>

修正前: g_pInFile = SD.open(pFilename, FILE_READ);
修正後: g_pInFile = SPIFFS.open(pFilename, “r”);

スケッチ本体は以下のようになります。
もともとのライブラリに、デコード結果をシリアルポートに表示する分かりやすいサンプルがありましたので、シリアルポート出力の代わりにLCDに点を打つだけで動作させることができました。

SPIFFSの中に”test1.jpg”というファイルを入れておくと、それをST7735ベースのLCDに表示します。
ESP8266のSPIFFSへのファイル転送のやり方は以前の記事を参照してください。

#include <arduino.h>
#include <SPI.h>
#include <FS.h>
#include <JPEGDecoder.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library

#define TFT_CS     15
#define TFT_RST    5
#define TFT_DC     4

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);

void setup() {
 Serial.begin(115200);
 Serial.println("");
 delay(10);

 tft.initR(INITR_BLUETAB);
 tft.fillScreen(ST7735_BLACK);

 SPIFFS.begin();
 jpegDraw("/test1.jpg");
}

void jpegDraw(char* filename) {
 char str[100];
 uint8 *pImg;
 int x,y,bx,by;
 
 // Decoding start
 JpegDec.decode(filename,0);

 // Image Information
 Serial.print("Width     :");
 Serial.println(JpegDec.width);
 Serial.print("Height    :");
 Serial.println(JpegDec.height);
 Serial.print("Components:");
 Serial.println(JpegDec.comps);
 Serial.print("MCU / row :");
 Serial.println(JpegDec.MCUSPerRow);
 Serial.print("MCU / col :");
 Serial.println(JpegDec.MCUSPerCol);
 Serial.print("Scan type :");
 Serial.println(JpegDec.scanType);
 Serial.print("MCU width :");
 Serial.println(JpegDec.MCUWidth);
 Serial.print("MCU height:");
 Serial.println(JpegDec.MCUHeight);
 Serial.println("");
 
 sprintf(str,"#SIZE,%d,%d",JpegDec.width,JpegDec.height);
 Serial.println(str);

 // Raw Image Data
 while(JpegDec.read()){
   pImg = JpegDec.pImage ;

   for(by=0; by<JpegDec.MCUHeight; by++){
     for(bx=0; bx<JpegDec.MCUWidth; bx++){
       
       x = JpegDec.MCUx * JpegDec.MCUWidth + bx;
       y = JpegDec.MCUy * JpegDec.MCUHeight + by;
       
       if(x<JpegDec.width && y<JpegDec.height){
         if(JpegDec.comps == 1){ // Grayscale          
           tft.drawPixel(x, y, tft.Color565(pImg[0], pImg[0], pImg[0]));
         }else{ // RGB
           tft.drawPixel(x, y, tft.Color565(pImg[0], pImg[1], pImg[2]));
         }
       }
       
       pImg += JpegDec.comps ;
     }
   }
 }
}
   
void loop() {
}

コメント