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系を使っていると転送ボタンを押してから実際に転送がはじまる前のビルドが長いのでストレスがかかります。その場合には書き込みの詳細出力をして、書き込みを行っているコマンドラインをコピーして自分で直接実行することにより何度も書き込みを試すことができます。

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

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

コメント

タイトルとURLをコピーしました