ESP32 Port - pixelmatix/SmartMatrix GitHub Wiki

Overview

The SmartMatrix Library ESP32 port is still incomplete, and there are no plans to bring it up to par with the Teensy 3/4 ports. That being said, it is working well enough for quite a few people. In particular SmartMatrix Library has a difficult time coexisting with memory intensive libraries like WiFi on the ESP32 because despite the ESP32 having a lot of RAM listed in the spec sheet, there's a limited amount of large blocks of actually useful RAM, and SmartMatrix Library needs a lot of those blocks. Consider using the ESP32-HUB75-MatrixPanel-I2S-DMA Library instead which makes some tradeoffs in graphical quality to dramatically reduce the amount of RAM needed to drive the HUB75 panel, making it more useful for applications that need more free RAM.

The SmartMatrix Library ESP32 port at a low level is based on Sprite_TM's ESP32 I2S Parallel example. The ESP32 can continuously shift data from RAM through the I2S peripheral in parallel to GPIO pins, without using up CPU cycles. This wasn't obvious to me from reading through the reference manual, and this peripheral doesn't have great documentation or example code besides Sprite_TM's example, so this was an invaluable start to the project. It was a challenge to move from the example with 21-bit color refresh to approaching the SmartMatrix Library's performance on the Teensy with up to 48-bit color and high refresh rates. The example code didn't scale well in RAM usage or refresh rate when increasing color depth. The architecture of the ESP32 and the Freescale processors used in the Teensy 3 family are so different a lot of the tricks I used on the Teensy 3 wouldn't port over. There are some significant changes from the Teensy Platform, but in general, sketches that used the Teensy SmartMatrix Library should work with the ESP32 SmartMatrix Library.

  • ESP32 Port Differences from Teensy

    • Because of more RAM available and the DMA architecture on the ESP32, two entire refresh frames are used. Refreshing the panel can take up little to no CPU usage for a static image, though the way SmartMatrix Library is written it does update the refresh frames with the Layer data periodically.
    • Layer update rate is decoupled from the panel refresh rate. By default, the Layer refresh rate is set to half of the panel refresh rate. e.g. if you use matrix.setRefreshRate(120), and repeatedly call backgroundLayer.swapBuffers(), it will swap at max 60 times per second.
      • Call matrix.setCalcRefreshRateDivider(uint8_t newDivider) to change the division from the default of 2 to something else. Be careful with setting to 1 with a high refresh rate, as you can end up with no CPU left to run your sketch (this will look like a blank display)
    • Refresh rate is not set exactly, it is a minimum value. It must be set before calling matrix.begin(), or the default 120 Hz will be used
    • The method of refreshing the panel is different from Teensy. With the current method, there is a strong tradeoff between color depth and refresh rate vs maximum brightness. If you set a higher color depth or refresh rate, you may find that the panel isn't refreshing as bright as it was previously.
    • Refresh Rate is calculated very granularly, in powers of 2, and the calculation is based on color depth and matrix size, and will double until it reaches a maximum value, or exceeds the minimum value you set (or the default of 120 Hz). e.g. If the minimum refresh rate is set to 120, and the refresh rate calculated for your color depth is 115 Hz (very close to 120 Hz but still lower), the library will double it and your refresh rate will be 230Hz, sacrificing maximum brightness and using up more CPU to update the frames more frequently. Refresh Rate details are printed to the serial terminal so you can tweak Sketch parameters to get the best refresh rate for your situation.
    • kDmaBufferRows is not used by the ESP32 port
    • Memory must be dynamically allocated for the ESP32, so the global buffer sizes printed by Arduino compilation are hiding a significant amount of memory that won't be available for your sketch. matrix.begin() prints with debug information on memory usage and memory available to the Serial port
      • matrix.begin() can be called with an optional argument of minimum number of bytes of DMA capable memory to keep free (so other code can malloc them after matrix.begin()). More details below.
      • Note that the values of "32-bit Memory" and "Heap Memory" in the printfs is a bit deceiving as not all of that can be allocated by the SmartMatrix Library or most other ESP32 Arduino libraries, so it's effectively unusable RAM. The amount of 8-bit and DMA RAM are more important stats as those are required for most libraries.
  • Not Yet Fully Working

    • AnimatedGIFs sketch is a bit fragile because of the ESP32 SD library
      • In general, resetting a sketch while the SD library is connected to the SD card can result communication with the SD card not working after reset - fix it with a power cycle
    • AnimatedGIFs sketch could have performance improved: there's a lot of time spent waiting for a swapBuffers() call to complete, as it needs to wait for the next frame transition, which is just wasting time if the frame rate is set relatively low to allow more sketch CPU time for GIF decoding).
    • Only updating panel buffers when there are Layer changes, reducing CPU usage even further
    • APA102 strip support (bringing to parity with the new Teensy APA102 driver that's in this branch)
    • C-shaped Chaining of panels to create multiple rows is broken (Z-shaped is working)
    • The panel stacking order is reversed, bottom to top is the default and SM_HUB75_OPTIONS_BOTTOM_TO_TOP_STACKING sets top to bottom stacking
    • Refresh fails at higher display sizes: 128x32 is largest that has been seen working. Not sure about cause of failure
    • Not all examples work on the ESP32 platform
      • SpectrumAnalyzer requires Teensy
      • MatrixClock hasn't been tested
      • FASTLED_Panel_Plus_APA hasn't been tested
    • Scrolling text speed is maximum one pixel shift per frame, which is quite slow with slow calculation framerates - need to allow scrolling text more than once per frame?
    • The ESP32 port only support 24, 36, and 48 values for kRefreshDepth
    • Use the 96k 32-bit addressible RAM for something, so it doesn't go to waste, and more of the 8-bit addressible DMA RAM is available to drive larger panels?
      • 96kB fits two frames of 128x128xRGB24, a good fit for a double-buffered background layer. Problem is RGB24 fits in 3 bytes and is normally accessed a byte at a time to set individual colors. It would need to be accessed 32-bit aligned, which would be a bit of overhead, reading (up to) two 32-bit words and writing (up to) two words back to change values in a RGB24 struct. Backbuffer() wouldn't work well as you can't provide a RGB24 pointer to the user.
    • Still seeing some crashes related to memory usage early in sketch when other memory intensive objects (e.g. WiFi library) are included in the sketch? Need to reproduce and track down
      • May see a rolling reset before the sketch can do much
        • This is hopefully fixed now. Please post to the SmartMatrix Community or create a GitHub Issue if you see this error: "Guru Meditation Error: Core 1 panic'ed (Cache disabled but cached memory region accessed)"
      • May see some libraries failing, e.g. in AnimatedGIFs sketch with a large panel and high color depth, calling SD.begin() after matrix.begin() results in "No SD Card" error, likely because there was not enough RAM (specifically DMA-capable RAM) available for the SD library. Moving the matrix.begin() call later works as the SmartMatrix Library adapts its DMA descriptor memory usage to the amount of DMA RAM available. Also using the new dmaRamToKeepFreeBytes parameter when calling matrix.begin() will try to keep that amount of DMA RAM free. The SD Library requires around 28000 bytes free, so you can call matrix.begin(28000) before SD.begin().
      • Todo: malloc matrixUpdateFrames buffers before other smaller buffers as they are (by far) the largest buffers to allocate?

There's a catchall GitHub Issue for incomplete features in the ESP32 Port

Hardware

The library has been tested with Espressif's ESP32 Dev Kit C, and ESP32-PICO-KIT-V4.

You can hook the ESP32 Dev Kit C directly up to a panel, following the circuit that's documented in code in Sprite_TM's ESP32 I2S Parallel example, and in the MatrixHardware_ESP32_V0.h header. You'll need to change the definition of GPIOPINOUT in that header to ESP32_FORUM_PINOUT if you're not using the SmartLED Shield circuit. You also may find the I2S clock rate is too high without level shifting and the image is not stable. Lower ESP32_I2S_CLOCK_SPEED in the same file. Lowering from 20MHz to 10-15MHz seems to work.

Note: this isn't necessarily the best pinout, as it might conflict with some lines used for setting boot state. You may need to hold down the GPIO0/BOOT button on your Dev Kit C (or enter boot mode first by holding GPIO0 low while toggling Reset/EN) if you can't reliably connect to the ESP32 when programming.

Look at the MatrixHardware_ESP32_V0.h and other MatrixHardware_ESP32* header files for other hardware options, including some easily assembled THT boards with level shifters meant for driving LED strips, but possible to wire up to a HUB75 connector. This kit on Tindie (also available assembled) is an option to connect up an ESP32 to a panel - though note it doesn't have 5V level shifters.

Some panels won't work with the 3.3V levels output by the ESP32, and you'll need 5V level shifting buffers like the SmartLED Shield designs use.

Additionally, the shields have some other features that make them preferable to using just an ESP32 (and optionally level shifting buffers):

  • The 5x ADDX lines are output using the RGB data lines and stored using an external latch, freeing up more pins on the ESP32
  • With the addition of the external latch, there are only 8 bits of data to output via I2S, and so each clock cycle's data fits into a uint8_t instead of uint16_t. With the I2S peripheral in 8-bit mode instead of 16-bit mode, the amount of RAM used to store refresh buffers is cut in half
  • Wiring is so much easier

Schematics, Eagle PCB files, and BOMs are in the extras/hardware folder. There's both a THT and SMT shield, neither have been fully tested. I've tested the THT version excluding the APA102 LED circuit. A contributor to the project has tested the matrix driving portion of the SMT shield. There are no plans to release the THT or SMT shields, but you're free to build them (or even sell them if you provide support) yourself. You can find some parts for the THT shield in the SMT BOM listed under a "THT" column.

Note: I have no plans to release the ESP32 shields I designed as a product. If you want to use them as is (please remove the Pixelmatix/SmartLED branding) or modify them and sell them yourself, feel free! Just make sure to note to your customers that the ESP32 port isn't finished, and only has community support. Contact me if you're selling them so I can point people to your product.