Displaying Images from SD Card on m5Stack (esp32 controller)

M5stack is a distribution of esp32 devices – its like an Arduino with superpowers. I am building a hardware AI assistant for kids and am trying to make cores3s display an image from an sdcard. I’m copying official code from their online wysiwyg editor and it crashes:


What’s happening

This is the code that should work according to documentation:

M5.Lcd.drawImage("/sd/my_image.jpg", 0, 0)

And the device immediately crashes. No exception. No error message. Just a hard reset or frozen screen.

But here’s the weird part – you can read the image from SD just fine:

with open("/sd/my_image.jpg", "rb") as f:
    data = f.read()  # This works!

And you can display the image from flash storage without any issues:

M5.Lcd.drawImage("/flash/my_image.jpg", 0, 0)  # This works!

It’s specifically the combination of drawImage() + SD card path that kills everything.


Why this happens

After much frustration, I figured it out: SPI bus conflict.

The LCD and the SD card share the same SPI bus on M5Stack devices. When drawImage() tries to simultaneously read from the SD card and write to the LCD display, it creates a bus conflict that crashes the device.

It’s not a bug in your code. It’s a hardware-level limitation in how the M5Stack firmware handles this operation.


The workaround

The solution is dumb but effective: copy the image to flash first, then display it from there.

import os
import M5
from M5 import *
from hardware import sdcard

def display_sd_image(sd_path, flash_temp="/flash/temp.jpg"):
    """
    Display an image from SD card by copying to flash first.
    Workaround for M5Stack SPI bus conflict.
    """
    # Read from SD into memory
    with open(sd_path, "rb") as f:
        image_data = f.read()
    
    # Write to flash
    with open(flash_temp, "wb") as f:
        f.write(image_data)
    
    # Free memory
    del image_data
    
    # Now display from flash (this works!)
    M5.Lcd.drawImage(flash_temp, 0, 0)


def setup():
    M5.begin()
    Widgets.setRotation(1)
    
    # Mount SD card (CoreS3 SE pins)
    sdcard.SDCard(slot=3, width=1, sck=36, miso=35, mosi=37, cs=4, freq=20000000)
    
    # Display image using the workaround
    display_sd_image("/sd/background.jpg")


def loop():
    M5.update()


if __name__ == '__main__':
    try:
        setup()
        while True:
            loop()
    except Exception as e:
        print("Error: {}".format(e))

Yes, it adds latency. Yes, flash storage has limited write cycles. But it works, and until M5Stack fixes this in firmware, it’s what we’ve got.


Quick reference

OperationWorks?
open("/sd/image.jpg", "rb").read()✅ Yes
M5.Lcd.drawImage("/flash/image.jpg", 0, 0)✅ Yes
M5.Lcd.drawImage("/sd/image.jpg", 0, 0)❌ Crashes

Notes for the future

  • For large images, consider reading/writing in chunks to avoid memory issues
  • Don’t put this in a tight loop unless you want to burn through your flash storage
  • Tested on M5Stack CoreS3 SE with UIFlow2 MicroPython v1.25.0
  • I’m building an AI voice assistant for M5Stack – and ran into this while working on the display system

Leave a Reply