SenseCAP Indicatorという4インチタッチスクリーン液晶のESP32デバイスがあるんですね。
https://www.seeedstudio.com/SenseCAP-Indicator-D1-p-5643.html
Groveコネクタもあるし、microSDカードも挿せるので、でかいM5Stackみたいに使えないかな…と思って…買いました。
https://files.seeedstudio.com/products/SenseCAP/SenseCAP_Indicator/SenseCAP%20Indicator%20User%20Manual_2023.4.21.pdf
ところがこのマニュアルの回路図を見てほしいんですけど、microSDスロットがつながっているのはRP2040の方なんですよ。M5Stackの感覚で直接読むわけには行かないのでどうしようかと思って…RP2040でmicroSDから読んだデータをUARTでESP32の方に送信してみることにしました。
準備
Platform IOでESP32側の開発ができるようにする
公式にはESP-IDFを使う方法が書いてあるんですけど、
https://wiki.seeedstudio.com/SenseCAP_Indicator_How_To_Flash_The_Default_Firmware/#for-esp32-s3
使い慣れたPlatform IOを使いたかったのでここを参考にしました。
https://qiita.com/fukuebiz/items/de6007c651255dd2458b
例ではesp32s3box.jsonを書き換えていますが、一応SenseCap-Indicator.jsonみたいに別ファイルを作成してボードを追加しています。
LovyanGFXが使えれば画像の表示も簡単そうです。microSDカードから画像を読みこんで表示することにしました。
Arduino IDEでRP2040側の開発ができるようにする
こっちもPlatform IOでやりたかったのですが…よく分からなかったため…公式に設定方法が書いてあるArduino IDEの方を使うことにしました。
https://wiki.seeedstudio.com/SenseCAP_Indicator_How_To_Flash_The_Default_Firmware/#for-rp2040
UART通信
RP2040側
UART通信ってどの程度まで速度出せるんでしょうね?こちらを参考に921600を設定してます。
https://asukiaaa.blogspot.com/2023/02/esp32-c3-s3-builtin-usb-can-send-700kbps.html
ESP32側から一行単位でコマンドを受け取り、応答を返します。「SD_OPEN_READ:(ファイル名)」でファイルを開いて「SD_READ_LENGTH:(バイト数)」でファイルを読むとかそんな感じです。手を抜いたので読み込みのみだし、ファイルは1つしか同時に開けないし、ファイル名は13文字までです(「/filename.txt」みたいなSD直下の8.3ファイル名を想定しています)。
#include <Arduino.h>
#include <SPI.h>
#include <SD.h>
File currentFile;
void setup()
{
Serial.begin(115200);
const int chipSelect = 13;
SPI1.setSCK(10);
SPI1.setTX(11);
SPI1.setRX(12);
if (SD.begin(chipSelect, 1000000, SPI1))
{
Serial.println("SD_BEGIN:TRUE");
}
else
{
Serial.println("SD_BEGIN:FALSE");
}
// Begin Serial1 for UART communication with ESP32
Serial1.setTX(16);
Serial1.setRX(17);
Serial1.begin(921600);
}
void loop()
{
if (Serial1.available() > 0)
{ // Listening Serial1 to receive commands from ESP32S3
String line = Serial1.readStringUntil('\n');
if (line.startsWith("SD_EXISTS:"))
{
String fileName = line.substring(strlen("SD_EXISTS:"));
if (fileName.length() < 14) {
char fileChar[14];
fileName.toCharArray(fileChar, fileName.length());
if (SD.exists(fileChar))
{
Serial1.printf("SD_EXISTS:TRUE %s\n", fileChar);
}
else
{
Serial1.printf("SD_EXISTS:FALSE %s\n", fileChar);
}
}
else
{
Serial1.printf("SD_EXISTS:FALSE LONG %s\n", fileName.c_str());
}
}
else if (line.startsWith("SD_OPEN_READ:"))
{
String fileName = line.substring(strlen("SD_OPEN_READ:"));
if (fileName.length() < 14) {
char fileChar[14];
fileName.toCharArray(fileChar, fileName.length());
if (currentFile = SD.open(fileChar))
{
Serial1.println("SD_OPEN_READ:TRUE");
}
else
{
Serial1.println("SD_OPEN_READ:FALSE");
}
} else {
Serial1.println("SD_OPEN_READ:FALSE LONG");
}
}
else if (line.startsWith("SD_CLOSE:"))
{
if (currentFile)
{
currentFile.close();
Serial1.println("SD_CLOSE:TRUE");
}
else
{
Serial1.println("SD_CLOSE:FALSE");
}
}
else if (line.startsWith("SD_SIZE:"))
{
if (currentFile)
{
Serial1.printf("SD_SIZE:%d\n", currentFile.size());
}
else
{
Serial1.println("SD_SIZE:0");
}
}
else if (line.startsWith("SD_READ_LENGTH:"))
{
String lenString = line.substring(strlen("SD_READ_LENGTH:"));
size_t len = lenString.toInt();
if (currentFile.size() - currentFile.position() < len) {
len = currentFile.size() - currentFile.position();
}
Serial1.println("SD_READ_START:");
char buffer[512];
while (len > 0)
{
size_t readLen = 0;
if (len > 512) {
readLen = currentFile.readBytes(buffer, 512);
} else {
readLen = currentFile.readBytes(buffer, len);
}
Serial1.write(buffer, readLen);
//byte aByte = currentFile.read();
//len -= Serial1.write(aByte);
len -= readLen;
}
}
else if (line.startsWith("SD_READ_LINE:"))
{
String fileLine = currentFile.readStringUntil('\n');
Serial1.println(fileLine);
}
else
{
Serial1.println(line);
}
}
}
ESP32側
本当はLovyanGFX周りのセッティングがありますが、それはこちらのまんまですので割愛します。
https://qiita.com/fukuebiz/items/de6007c651255dd2458b
Serial2を使ってRP2040側にコマンド文字列を送り、ファイルサイズを聞いてメモリを確保し、受信したJPEGをLovyanGFXで表示させます。本当はStreamとして直に渡せると思う。
void setup(void)
{
Serial2.begin(921600, SERIAL_8N1, 20, 19);
}
void loadJpeg(String fileName)
{
String command = "SD_OPEN_READ:" + fileName;
Serial2.println(command);
String response = Serial2.readStringUntil('\n');
Serial.println(response);
Serial2.println("SD_SIZE:");
response = Serial2.readStringUntil('\n');
Serial.println(response);
String sizeString = response.substring(strlen("SD_SIZE:"));
size_t fileSize = sizeString.toInt();
size_t position = 0;
byte *fileBuffer = (byte *)malloc(fileSize * sizeof(byte));
if (!fileBuffer) {
Serial.println("malloc failed");
return;
}
Serial2.printf("SD_READ_LENGTH:%d\n", fileSize);
response = Serial2.readStringUntil('\n');
while (position < fileSize)
{
if (Serial2.available()) {
size_t readLength = Serial2.readBytes(fileBuffer + position, (fileSize - position));
position += readLength;
if (position == fileSize) break;
}
}
display.drawJpg(fileBuffer, fileSize, 0, 0, 480, 480);
Serial2.println("SD_CLOSE");
free(fileBuffer);
}