Hi there.
I'm making a clock that displays time in single digits on several screens with nixie tubes images.
I have been trying for several days to get an image stored in SPIFFS with LittleFS to display on a 240x240 screen with several boards. Currently with a XIAO ESP32-S3.
I can get graphics to display with several different processors, no problem.
With this program I can see the 10 files I loaded with the IDE 2.3.7 into flash.
There are no errors regarding LittleFS in the serial monitor.
I have tried numerous types of files, made with several image converters. Raw RGB565 etc
The 16-bit binaries vary between 60k and 115200.
There is not the slightest pixel on the LCDto be seen. But I can display graphics just fine.
This is the offending function.
void showNumber() {
char fileName[13]; // buffer
snprintf(fileName, 12, "%i.bin", image_nb); // files: bin.0 to bin.9 for digit 0-9
Serial.print("Opening: ");
Serial.println(fileName);
if (!fileName) Serial.println("File error"); // no error
File file = LittleFS.open(fileName);
// this part does not work
uint8_t buffer[64]; // Small buffer
while (file.available()) {
int bytesRead = file.read(buffer, sizeof(buffer));
tft1.writePixels((uint16_t *)buffer, bytesRead / 2);
}
tft1.endWrite(); // End SPI transaction
file.close();
delay(1000); // these test block works
if (screen == 1) {
tft1.fillScreen(ST77XX_BLUE);
tft1.setCursor(75, 50);
tft1.setTextSize(20);
tft1.setTextColor(ST77XX_RED);
tft1.print(image_nb);
}
if (screen == 2) {
tft2.fillScreen(ST77XX_BLUE);
tft2.setCursor(75, 50);
tft2.setTextSize(20);
tft2.setTextColor(ST77XX_RED);
tft2.print(image_nb);
}
}
I can post the whole sketch if needed, but it's a work in progress.
As said, graphics display fine, so I expect the problem to be in that function.
Leo..
// These functions require a chip-select and/or SPI transaction
// around them. Higher-level graphics primitives might start a
// single transaction and then make multiple calls to these functions
// (e.g. circle or text rendering might make repeated lines or rects)
// before ending the transaction. It's more efficient than starting a
// transaction every time.
void writePixel(int16_t x, int16_t y, uint16_t color);
void writePixels(uint16_t *colors, uint32_t len, bool block = true,
bool bigEndian = false);
Since you only posted a snippet, I have to ask:
Did you?
Do a chip select and/or SPI transaction around them? Do a startWrite? And a setAddrWindow?
There is only a chip select for the tft displays, which is handled in the declarations.
Adafruit_ST7789 tft1 = Adafruit_ST7789(D0, D6, D7); // CS, DC, RST
Adafruit_ST7789 tft2 = Adafruit_ST7789(D1, D6, -1);
There is no SD card to select.
Full code (ignore incomplete parts).
Leo..
#include <WiFi.h>
#include "esp_sntp.h"
#include "FS.h"
#include <SPI.h>
#include <LittleFS.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <Adafruit_ImageReader.h>
unsigned long prevTime;
unsigned int reSync, display;
time_t now;
tm tm;
int tensHr, unitsHr, tensMin, unitsMin, image_nb;
int prevTensHr = -1, prevUnitsHr = -1, prevTensMin = -1, prevWeekday = -1, prevMinute = -1;
int screen;
Adafruit_ST7789 tft1 = Adafruit_ST7789(D0, D6, D7); // CS, DC, RST
Adafruit_ST7789 tft2 = Adafruit_ST7789(D1, D6, -1);
//Adafruit_ST7735 tft3 = Adafruit_ST7789(D0, D6, -1);
//Adafruit_ST7735 tft4 = Adafruit_ST7789(D0, D6, -1);
void cbSyncTime(struct timeval *tv) {
reSync++;
Serial.print("NTP update: ");
Serial.println(reSync);
}
void showNumber() {
char fileName[13]; // buffer
snprintf(fileName, 12, "%i.bmp", image_nb);
Serial.print("Opening: ");
Serial.println(fileName);
if (!fileName) Serial.println("File error"); // no error
File file = LittleFS.open(fileName);
// this part does not work
uint8_t buffer[64]; // Small buffer
while (file.available()) {
int bytesRead = file.read(buffer, sizeof(buffer));
tft1.writePixels((uint16_t *)buffer, bytesRead / 2);
}
tft1.endWrite(); // End SPI transaction
file.close();
delay(1000); // these test block works
if (screen == 1) {
tft1.fillScreen(ST77XX_BLUE);
tft1.setCursor(75, 50);
tft1.setTextSize(20);
tft1.setTextColor(ST77XX_RED);
tft1.print(image_nb);
}
if (screen == 2) {
tft2.fillScreen(ST77XX_BLUE);
tft2.setCursor(75, 50);
tft2.setTextSize(20);
tft2.setTextColor(ST77XX_RED);
tft2.print(image_nb);
}
}
void showFace() {
for (int i = 0; i < 32; i++) {
//if (birthdayEvent[i][1] == tm.tm_mday && birthdayEvent[i][2] == tm.tm_mon) {
}
}
void setup() {
Serial.begin(115200);
delay(2000);
if (!LittleFS.begin()) {
Serial.println("LittleFS initialisation failed!");
}
sntp_set_time_sync_notification_cb(cbSyncTime);
configTzTime("NZST-12NZDT,M9.5.0,M4.1.0/3", "nz.pool.ntp.org");
tft1.init(240, 240);
tft2.init(240, 240);
//tft.setSPISpeed(40000000);
tft1.setRotation(2);
tft2.setRotation(2);
tft1.fillScreen(ST77XX_RED);
WiFi.begin("xxxxxxx", "xxxxxxx");
Serial.println("Waiting for Time sync");
while (!reSync) yield();
}
void loop() {
time(&now); // epoch
if (now != prevTime) { // time has changed
prevTime = now; // remember
localtime_r(&now, &tm); // local time elements
}
if (tm.tm_min != prevMinute) { // minutes have changed ?
prevMinute = tm.tm_min; // remember
// extract digits
tensHr = tm.tm_hour / 10;
unitsHr = tm.tm_hour % 10;
tensMin = tm.tm_min / 10;
unitsMin = tm.tm_min % 10;
Serial.print("Units minutes:");
Serial.println(unitsMin);
screen = 1;
image_nb = unitsMin;
showNumber();
if (tensMin != prevTensMin) {
prevTensMin = tensMin;
Serial.print("Tens minutes:");
Serial.println(tensMin);
screen = 2;
image_nb = tensMin;
showNumber();
}
if (unitsHr != prevUnitsHr) {
prevUnitsHr = unitsHr;
Serial.print("Units hour:");
Serial.println(unitsHr);
screen = 3;
image_nb = unitsHr;
showNumber();
}
if (tensHr != prevTensHr) {
prevTensHr = tensHr;
Serial.print("Tens hour:");
Serial.println(tensHr);
screen = 4;
if (tensHr) { // if not zero
image_nb = tensHr;
showNumber();
} else if (unitsHr > 6) showFace();
//else showTemp();
}
}
}
//if (tm.tm_wday != prevWeekday) {
// flag = false;
// prevWeekday = tm.tm_wday;
//}
I see no setAddrWindow(...) call to tell the display where to put the bytes being written to it.
And if file.size() is returning 0, I don't think it opened properly. So no data.
It's been yonks since I played around with LittleFS, but I'd expect there to be another parameter to say whether the file is being opened for reading or writing.
Making progress.
Post#7 I had the forward slash in there when I was still working with SPIFFS.
Removed it at some stage.
With LittleFS it seems to be needed.
Now I have file content printed in the serial monitor.
Waiting for Time sync
NTP updat1
Units minutes:7
Opening: /7.bin
61920 bytes
Still no picture though. Wrong format?
I wish I had a example .bin file that worked.
Why setAddrWindow(...)
All files are trimmed to 240x240.
Leo..
/**************************************************************************/
/*!
@brief SPI displays set an address window rectangle for blitting pixels
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param w Width of window
@param h Height of window
*/
/**************************************************************************/
void Adafruit_ST77xx::setAddrWindow(uint16_t x, uint16_t y, uint16_t w,
uint16_t h) {
x += _xstart;
y += _ystart;
uint32_t xa = ((uint32_t)x << 16) | (x + w - 1);
uint32_t ya = ((uint32_t)y << 16) | (y + h - 1);
writeCommand(ST77XX_CASET); // Column addr set
SPI_WRITE32(xa);
writeCommand(ST77XX_RASET); // Row addr set
SPI_WRITE32(ya);
writeCommand(ST77XX_RAMWR); // write to RAM
}
Because without it, the commands to tell the controller where to write in RAM aren't sent.
Which means that the controller has no idea what do to with the flood of data that follows.
With tft1.startWrite(); I see now something that could be the image (nixie tube).
It has the colours and detail. But only half the screen and skewed.
But more than I had in the last three days
That's the source code for the setAddrWindow call. I showed it to you do demonstrate that's what sets the controller up to accept pixel data and that without calling it, the controller has no idea what to do.
Try using the drawRGBBitmap() function from the Adafruit_GFX library, instead of trying to directly draw pixels. You will need to change 129 to 240, my test images are 129x240.
size_t line = 0;
File file = LittleFS.open(fileName);
if (file) {
uint8_t buffer[129 * 2]; // 2 bytes per pixel
while (file.available()) {
int bytesRead = file.read(buffer, sizeof(buffer));
tft1.drawRGBBitmap(0, line, (uint16_t *)buffer, 129, 1);
line++;
}
file.close();
} else {
Serial.println("File error");
}