3.5インチタッチパネル付きESP32-3248S035R/C

概要

上記で紹介したボードに3.5インチ版が発売されたので購入してみました。細かいところが修正されていて、着実に進化をしていると思います。

種類

今回はタッチパネルの違いにより2種類あります。末尾の記号で違うので注意してください。

ESP32-3248S035R

末尾Rは2.8インチと同じ抵抗膜の感圧式になります。Resistiveで抵抗のRですね。ペンを使う限りは使いやすいですが、マルチタッチなどができません。

ESP32-3248S035C

末尾Cは静電容量式のタッチパネルになります。Capacitiveでキャパシタ(コンデンサ)のCですね。こちらのほうが高いですが、マルチタッチなどが可能であり、スマートフォンなどでも採用されている方式になります。

型番の意味

ESP32-3248S035R
↑320×480↑3.5インチ↑感圧式

上記の意味合いだと思われます。

型番解像度画面サイズタッチパネル
ESP32-2432S028R240×3202.8インチ感圧式
ESP32-3248S035R320×4803.5インチ感圧式
ESP32-3248S035C320×4803.5インチ静電容量

わかりにくい型番だと思っていたのですが、こう見ると少し理解できるかな。。。でもわかりにくいですし長いですよね。

製品

上記のAliExpressでのみ現在は販売しているようです。

上記がRの感圧式の基板です。

上記がCの静電容量版の基板です。フレキケーブルが伸びているのがわかると思います。微妙にパターンも違いますね。

2.8インチ版も比較のためにおいておきます。

3枚のサイズ比較です。画面の分大きくなっていますね。

表側です。わかりにくいですが2.8インチでは裏側になったRGB LEDが表に移動しています。あと液晶の右下に型番が書いてあるほうが感圧式になります。

使い方

ESP32-3248S035R(感圧式)

#define LGFX_USE_V1
#include <LovyanGFX.hpp>

class LGFX : public lgfx::LGFX_Device
{
    lgfx::Panel_ST7796  _panel_instance;
    lgfx::Bus_SPI       _bus_instance;
    lgfx::Light_PWM     _light_instance;
    lgfx::Touch_XPT2046 _touch_instance;
  public:
    LGFX(void)
    {
      {
        auto cfg = _bus_instance.config();

        // SPIバスの設定
        cfg.spi_host         = HSPI_HOST;
        cfg.spi_mode         = 0;
        cfg.freq_write       = 40000000;
        cfg.freq_read        = 16000000;
        cfg.spi_3wire        = true;
        cfg.use_lock         = true;
        cfg.dma_channel      = SPI_DMA_CH_AUTO;
        cfg.pin_sclk         = 14; // 変更
        cfg.pin_mosi         = 13; // 変更
        cfg.pin_miso         = 12; // 変更
        cfg.pin_dc           =  2; // 変更

        _bus_instance.config(cfg);
        _panel_instance.setBus(&_bus_instance);
      }

      {
        auto cfg = _panel_instance.config();

        cfg.pin_cs           =    15; // 変更
        cfg.pin_rst          =    -1; // 変更
        cfg.pin_busy         =    -1; // 変更

        cfg.panel_width      =   320;
        cfg.panel_height     =   480;
        cfg.offset_x         =     0;
        cfg.offset_y         =     0;
        cfg.offset_rotation  =     0;
        cfg.dummy_read_pixel =     8;
        cfg.dummy_read_bits  =     1;
        cfg.readable         =  true;
        cfg.invert           = false;
        cfg.rgb_order        = false;
        cfg.dlen_16bit       = false;
        cfg.bus_shared       =  true;

        _panel_instance.config(cfg);
      }

      {
        auto cfg = _light_instance.config();

        cfg.pin_bl = 27;              // 変更
        cfg.invert = false;
        cfg.freq   = 1200;
        cfg.pwm_channel = 7;

        _light_instance.config(cfg);
        _panel_instance.setLight(&_light_instance);
      }

      {
        auto cfg = _touch_instance.config();

        cfg.x_min      = 3875;    // 変更
        cfg.x_max      =  250;    // 変更
        cfg.y_min      =  165;    // 変更
        cfg.y_max      = 3943;    // 変更
        cfg.pin_int    = -1;      // 変更
        cfg.bus_shared = true;    // 変更
        cfg.offset_rotation = 0;  // 変更

        cfg.spi_host = HSPI_HOST; // 変更
        cfg.freq = 1000000;
        cfg.pin_sclk = 14;        // 変更
        cfg.pin_mosi = 13;        // 変更
        cfg.pin_miso = 12;        // 変更
        cfg.pin_cs   = 33;        // 変更

        _touch_instance.config(cfg);
        _panel_instance.setTouch(&_touch_instance);
      }

      setPanel(&_panel_instance);
    }
};

LGFX display;

void setup(void)
{
  Serial.begin(115200);
  display.init();

  display.setTextSize((std::max(display.width(), display.height()) + 255) >> 8);

  if (display.touch() && true)
  {
    if (display.width() < display.height()) display.setRotation(display.getRotation() ^ 1);

    display.setTextDatum(textdatum_t::middle_center);
    display.drawString("touch the arrow marker.", display.width() >> 1, display.height() >> 1);
    display.setTextDatum(textdatum_t::top_left);

    std::uint16_t fg = TFT_WHITE;
    std::uint16_t bg = TFT_BLACK;
    if (display.isEPD()) std::swap(fg, bg);
    uint16_t calibrateData[8];
    display.calibrateTouch(calibrateData, fg, bg, std::max(display.width(), display.height()) >> 3);
    Serial.printf("[0] x_min = %d\n", calibrateData[0]);
    Serial.printf("[1] y_min = %d\n", calibrateData[1]);
    Serial.printf("[2] x_min = %d\n", calibrateData[2]);
    Serial.printf("[3] y_max = %d\n", calibrateData[3]);
    Serial.printf("[4] x_max = %d\n", calibrateData[4]);
    Serial.printf("[5] y_min = %d\n", calibrateData[5]);
    Serial.printf("[6] x_max = %d\n", calibrateData[6]);
    Serial.printf("[7] y_max = %d\n", calibrateData[7]);
    Serial.printf("x_min = %d\n", (calibrateData[0] + calibrateData[2]) / 2);
    Serial.printf("x_max = %d\n", (calibrateData[4] + calibrateData[6]) / 2);
    Serial.printf("y_min = %d\n", (calibrateData[1] + calibrateData[5]) / 2);
    Serial.printf("y_max = %d\n", (calibrateData[3] + calibrateData[3]) / 2);
  }

  display.fillScreen(TFT_BLACK);
}

uint32_t count = ~0;
void loop(void)
{
  display.startWrite();
  display.setRotation(++count & 7);
  display.setColorDepth((count & 8) ? 16 : 24);

  display.setTextColor(TFT_WHITE);
  display.drawNumber(display.getRotation(), 16, 0);

  display.setTextColor(0xFF0000U);
  display.drawString("R", 30, 16);
  display.setTextColor(0x00FF00U);
  display.drawString("G", 40, 16);
  display.setTextColor(0x0000FFU);
  display.drawString("B", 50, 16);

  display.drawRect(30, 30, display.width() - 60, display.height() - 60, count * 7);
  display.drawFastHLine(0, 0, 10);

  display.endWrite();

  int32_t x, y;
  if (display.getTouch(&x, &y)) {
    lgfx::touch_point_t tp;
    display.getTouchRaw(&tp);
    Serial.printf("raw_x = %d, raw_y = %d\n", tp.x, tp.y);
    
    display.fillRect(x - 2, y - 2, 5, 5, count * 7);
  }
}

LovyanGFXのサンプルです。2.8インチとの違いはSPIを画面とタッチパネルで共有していることです。ESP32で3系統のSPIを利用するのは面倒なことに気がついたようです。

そのためSPI周りの設定が変わっています。bus_sharedをtrueに変更して、spi_hostも液晶とタッチパネルで同じものを指定する必要があります。

あとタッチパネルのキャリブレーションの仕方を確認したところ、結果を取得できることがわかりましたので出力しています。最初に手動でキャリブレーションをして、その結果を保存しておくことでキャリブレーションなしでも動作可能になると思います。

ESP32-3248S035C(静電容量式)

#include <driver/i2c.h>
#define LGFX_USE_V1
#include <LovyanGFX.hpp>

class LGFX : public lgfx::LGFX_Device
{
    lgfx::Panel_ST7796  _panel_instance;
    lgfx::Bus_SPI       _bus_instance;
    lgfx::Light_PWM     _light_instance;
    lgfx::Touch_GT911   _touch_instance;
  public:
    LGFX(void)
    {
      {
        auto cfg = _bus_instance.config();

        // SPIバスの設定
        cfg.spi_host         = HSPI_HOST;
        cfg.spi_mode         = 0;
        cfg.freq_write       = 40000000;
        cfg.freq_read        = 16000000;
        cfg.spi_3wire        = true;
        cfg.use_lock         = true;
        cfg.dma_channel      = SPI_DMA_CH_AUTO;
        cfg.pin_sclk         = 14; // 変更
        cfg.pin_mosi         = 13; // 変更
        cfg.pin_miso         = 12; // 変更
        cfg.pin_dc           =  2; // 変更

        _bus_instance.config(cfg);
        _panel_instance.setBus(&_bus_instance);
      }

      {
        auto cfg = _panel_instance.config();

        cfg.pin_cs           =    15; // 変更
        cfg.pin_rst          =    -1; // 変更
        cfg.pin_busy         =    -1; // 変更

        cfg.panel_width      =   320;
        cfg.panel_height     =   480;
        cfg.offset_x         =     0;
        cfg.offset_y         =     0;
        cfg.offset_rotation  =     0;
        cfg.dummy_read_pixel =     8;
        cfg.dummy_read_bits  =     1;
        cfg.readable         =  true;
        cfg.invert           = false;
        cfg.rgb_order        = false;
        cfg.dlen_16bit       = false;
        cfg.bus_shared       = false; // 変更

        _panel_instance.config(cfg);
      }

      {
        auto cfg = _light_instance.config();

        cfg.pin_bl = 27;              // 変更
        cfg.invert = false;
        cfg.freq   = 44100;
        cfg.pwm_channel = 7;

        _light_instance.config(cfg);
        _panel_instance.setLight(&_light_instance);
      }

      {
        auto cfg = _touch_instance.config();
        cfg.pin_int  = GPIO_NUM_36;   // INT pin number
        cfg.pin_sda  = GPIO_NUM_33;   // I2C SDA pin number
        cfg.pin_scl  = GPIO_NUM_32;   // I2C SCL pin number
        cfg.i2c_addr = 0x5D;          // I2C device addr
        cfg.i2c_port = I2C_NUM_0;     // I2C port number
        cfg.freq     = 400000;        // I2C freq
        cfg.x_min    =  14;
        cfg.x_max    = 310;
        cfg.y_min    =   5;
        cfg.y_max    = 448;
        cfg.offset_rotation = 0;
        cfg.bus_shared = false;

        _touch_instance.config(cfg);
        _panel_instance.setTouch(&_touch_instance);
      }

      setPanel(&_panel_instance);
    }
};

LGFX display;

void setup(void)
{
  Serial.begin(115200);
  display.init();

  display.setTextSize((std::max(display.width(), display.height()) + 255) >> 8);

  if (display.touch() && true)
  {
    if (display.width() < display.height()) display.setRotation(display.getRotation() ^ 1);

    display.setTextDatum(textdatum_t::middle_center);
    display.drawString("touch the arrow marker.", display.width() >> 1, display.height() >> 1);
    display.setTextDatum(textdatum_t::top_left);

    std::uint16_t fg = TFT_WHITE;
    std::uint16_t bg = TFT_BLACK;
    if (display.isEPD()) std::swap(fg, bg);
    uint16_t calibrateData[8];
    display.calibrateTouch(calibrateData, fg, bg, std::max(display.width(), display.height()) >> 3);
    Serial.printf("[0] x_min = %d\n", calibrateData[0]);
    Serial.printf("[1] y_min = %d\n", calibrateData[1]);
    Serial.printf("[2] x_min = %d\n", calibrateData[2]);
    Serial.printf("[3] y_max = %d\n", calibrateData[3]);
    Serial.printf("[4] x_max = %d\n", calibrateData[4]);
    Serial.printf("[5] y_min = %d\n", calibrateData[5]);
    Serial.printf("[6] x_max = %d\n", calibrateData[6]);
    Serial.printf("[7] y_max = %d\n", calibrateData[7]);
    Serial.printf("x_min = %d\n", (calibrateData[0] + calibrateData[2]) / 2);
    Serial.printf("x_max = %d\n", (calibrateData[4] + calibrateData[6]) / 2);
    Serial.printf("y_min = %d\n", (calibrateData[1] + calibrateData[5]) / 2);
    Serial.printf("y_max = %d\n", (calibrateData[3] + calibrateData[3]) / 2);
  }

  display.fillScreen(TFT_BLACK);
}

uint32_t count = ~0;
void loop(void)
{
  display.startWrite();
  display.setRotation(++count & 7);
  display.setColorDepth((count & 8) ? 16 : 24);

  display.setTextColor(TFT_WHITE);
  display.drawNumber(display.getRotation(), 16, 0);

  display.setTextColor(0xFF0000U);
  display.drawString("R", 30, 16);
  display.setTextColor(0x00FF00U);
  display.drawString("G", 40, 16);
  display.setTextColor(0x0000FFU);
  display.drawString("B", 50, 16);

  display.drawRect(30, 30, display.width() - 60, display.height() - 60, count * 7);
  display.drawFastHLine(0, 0, 10);

  display.endWrite();

  int32_t x, y;
  if (display.getTouch(&x, &y)) {
    lgfx::touch_point_t tp;
    display.getTouchRaw(&tp);
    Serial.printf("raw_x = %d, raw_y = %d\n", tp.x, tp.y);

    display.fillRect(x - 2, y - 2, 5, 5, count * 7);
  }
}

静電容量式のタッチパネルはSPIではなくI2Cになります。そのためいろいろ変更になっています。

まとめ

全体的に確実に進歩しているので、低価格だけではなく使いやすいボードになってきていると思います。ただし、ネックとしてはUSBシリアルのCH340Cです。このチップは不安定なのかアップロードが失敗しやすい気がいます。

手元だとRの方はBOOT(GPIO0)ボタンを押しっぱなしにしないと書き込めないです。Cの方は自動書き込みはできるのですが、一度正常に書き込みができなくなりました。いろいろ初期化したり、書き込み速度を落としたところで安定しましたが原因はよくわかりません。

とくに自動書き込みができないと非常に面倒です。Arduino IDEの1系を使っていると転送ボタンを押してから実際に転送がはじまる前のビルドが長いのでストレスがかかります。その場合には書き込みの詳細出力をして、書き込みを行っているコマンドラインをコピーして自分で直接実行することにより何度も書き込みを試すことができます。

  • ESP32-3248S035(macsbug)

また、上記の記事が非常に参考になります。

※追記 書き込みが不安定なのは設計ミスかもしれません。なぜ外付けフラッシュを載せているのが疑問だったのですが、内蔵フラッシュがあるのに同じ容量の外付けフラッシュを搭載してしまっているかもしれません。たまたま同じ動作の場合には問題がないのですが差分がでるとそこからおかしくなる可能性があります。外付けフラッシュは外したほうが良さそうです。

コメント

  1. wombat より:

    質問で失礼します.ESP32関係およびArduinoIDEは素人です.この基板(抵抗タッチパネル)で計測器を作っていますが,スクリーンショットがうまく行きません.BMPファイルをSDカードに描くところまでは,作動しています(計測データも書き込めています)が,画像が色縞だったり,真っ黒だったりでどうにも困っています.heightとwidthを交換したりもしましたが,縦,横が変わるだけでした.画面の読み出しかどこかで,何か根本的に間違っているでしょうか?使っているプログラムは下記の16ビット用の部分と,

    LovyanGFX入門 その8 画面キャプチャからのGIF作成
    概要 前回はスプライトの中身を説明しました。今回は実際の使い方をと思ったのですが、画面キャプチャしたものをGIF画像に保存する方法を忘れないうちに書いておきたいと思います。 画面キャプチャとは? 対応しているかは接続しているLcdに依存する...


    lgfx_ESP32_3248S035.h
    です.お忙しいなかを,どうぞよろしくお願いします.

    • たなかまさゆき より:

      パネルからの読み出しですね
      そこはちょっと検証していないです、、、

      たしか、読み出しに使っているPINが未接続な場合があると聞いたことがあります

      資料みたところR24がその線で私の基板は未実装でした
      抵抗値0の抵抗か、半田とかでR24を導通させることで呼び出しが使えるようになるかもしれません

      • wombat より:

        返信ありがとうございます.今調べると,R24は私の持つタッチセンサがR,Cいずれの基板でも未実装でした.macsbugさんのhttps://macsbug.wordpress.com/2022/10/02/esp32-3248s035/ にある設定ファイルをlgfx_ESP32_3248S035.hとしてincludeして使っています.widthとheightを交換したりすると色縞が鮮明に出るので,何らかの読み出しはできているようにも思えます.素人なのでBMPファイルのヘッダとかをまだ勉強している最中です.すみません.私の方でも上記のアドバイスに従って,色々とやってみます.結果はご報告します.また何かの折りにご教示いただけるとありがたいです.ありがとうございます.

  2. wombat より:

    その後,アドバイス通りR24を短絡して,各種修正を行った所,Bingo!でした.やっと1ヶ月悩んだ画面キャプチャーがまだ少し問題(6ピクセル程度左端画面が右端に回り込みます)があるものの,SDカード保存まで漕ぎ着けました.アドバイス本当にありがとうございました.顛末を少し下記(隠しURL)にまとめました.ご笑覧ください.変更点は以下です.
    cfg.spi_3wire = false; // true x
    cfg.use_lock = true;

    cfg.readable = true; // 重要!
    cfg.invert = false;
    cfg.rgb_order = false;
    cfg.dlen_16bit = false;
    cfg.bus_shared = true; // False OK

    http://www.yossi-okamoto.net/Microbarometer/ESP32-3248S035_Capture.html