• Stars
    star
    310
  • Rank 134,926 (Top 3 %)
  • Language
    C++
  • License
    MIT License
  • Created over 6 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

💾 Customizable menu system for M5Stack, M5Unified and ESP32-Chimera-Core - loads apps from the Micro SD card. Easily add you own apps

License: MIT Gitter arduino-library-badge PlatformIO Registry

Arduino Build Platformio Build

Library Downloads

M5Stack-SD-Updater


Click to enlarge


ABOUT


🏭 M5Stack-SD-Menu EXAMPLE SKETCH PREREQUISITES:


Micro SD Card (TF Card) - formatted using FAT32. Max size 32 Gb. SDCard is recommended but the SDUpdater supports other filesystems such as SdFat, SD_MMC and LittleFS (SPIFFS will soon be deprecated).


Make sure you have the following libraries: - they should be installed in: ~/Arduino/libraries

All those are available in the Arduino Library Manager or by performing a manual installation.


🍱 UNPACKING THE BINARIES

obsolete For your own lazyness, you can use @micutil's awesome M5Burner and skip the next steps. https://github.com/micutil/M5Burner_Mic/releases ... or customize your own menu and make the installation manually :

1) Open the examples/M5Stack-SD-Update sketch from the Arduino ID Examples menu.


2) Download the SD-Content 💾 folder from the release page and unzip it into the root of the SD Card. Then put the SD Card into the M5Stack. This zip file comes preloaded with precompiled apps and the relative meta information for the menu.


3) Compile and flash the M5Stack-SD-Menu.ino example.
This sketch is the menu app. It shoul reside in the root directory of a micro SD card for persistence and also executed once.

Once flashed it will copy itself on OTA2 partition and on the SDCard, then rolled back and executed from the OTA2 partition.

Thanks to @Lovyan03 this self-propagation logic is very convenient: by residing on OTA2 the menu.bin will always be available for fast re-loading.


4) Make application sketches compatible with the SD-Updater Menu .

The snippet of code in the M5Stack-SDLoader-Snippet.ino sketch can be used as a model to make any ESP32 sketch compatible with the SD-Updater menu.

In your sketch, find the line where the core library is included:

   // #include <M5Stack.h>
   // #include <M5Core2.h>
   // #include <LovyanGFX.h>
   // #include <M5GFX.h>
   // #include <ESP32-Chimera-Core.h>
   // #include <M5StickC.h>
   // #include <M5Unified.h>

And add this after the include:

    // #define SDU_ENABLE_GZ // optional: support for gzipped firmwares
    #include <M5StackUpdater.h>

In your setup() function, find the following statements:

    M5.begin();
    // Serial.begin(115200);

And add this after serial is started:

    checkSDUpdater( SD );

Then do whatever you need to do (button init, timer, network signal) in the setup and the loop. Your app will run normally except at boot (e.g. if the Button A is pressed), when it can load the /menu.bin binary from the filesystem, or go on with it application duties.

⚠️Touch UI has no buttons, this raises the problem of detecting a 'pushed' state when the touch is off. As a compensation, an UI lobby will be visible for 2 seconds after every ESP.restart(). The visibility of the lobby can be forced in the setup :

    checkSDUpdater( SD, MENU_BIN, 2000 );

Custom SD-Load scenarios can be achieved using non default values:

    M5.begin();

    checkSDUpdater(
      SD,           // filesystem (SD, SD_MMC, SPIFFS, LittleFS, PSRamFS)
      MENU_BIN,     // path to binary (default = /menu.bin, empty string = rollback only)
      5000          // wait delay, (default=0, will be forced to 2000 upon ESP.restart() or with headless build )
      TFCARD_CS_PIN // optional for SD use only, usually default=4 but your mileage may vary)
    );

Headless setup can bypass onWaitForAction lobby option with their own button/sensor/whatever detection routine.

    Serial.begin( 115200 );

    if(digitalRead(BUTTON_A_PIN) == 0) {
      Serial.println("Will Load menu binary");
      updateFromFS(SD);
      ESP.restart();
    }

Headless setup can also be customized in complex integrations:

    Serial.begin( 115200 );

    SDUCfg.setCSPin( TFCARD_CS_PIN );
    SDUCfg.setFS( &SD );

    // set your own button response trigger

    static int buttonState;

    SDUCfg.setSDUBtnA( []() {
      return buttonState==LOW ? true : false;
    });

    SDUCfg.setSDUBtnPoller( []() {
      buttonState = digitalRead( 16 );
    });

    // Or set your own serial input trigger
    // SDUCfg.setWaitForActionCb( mySerialActionTrigger );

    SDUpdater sdUpdater( &SDUCfg );

    sdUpdater.checkSDUpdaterHeadless( MENU_BIN, 30000 ); // wait 30 seconds for serial input

Use one of following methods to get the app on the filesystem:

  • Have the app copy itself to filesystem using BtnC from the lobby or implement saveSketchToFS( SD, "/my_application.bin" ); from an option inside your app.

  • Manually copy it to the filesystem:

    • In the Arduino IDE menu go to "Sketch / Export Compiled Binary".
    • Rename the file to remove unnecessary additions to the name. The filename will be saved as "filename.ino.esp32.bin". Edit the name so it reads "filename.bin". This is purely for display purposes. The file will work without this change.

⌾ SD-Updater customizations:

These callback setters are populated by default but only fit the best scenario (M5Stack with display+buttons).

⚠️ If no supported combination of display/buttons exists, it will fall back to headless behaviour and will only accept update/rollback signals from Serial.

As a result, any atypical setup (e.g. headless+LittleFS) should make use of those callback setters:

  SDUCfg.setCSPin       ( TFCARD_CS_PIN );      // const int
  SDUCfg.setFS          ( &FS );                // fs::FS* (SD, SD_MMC, SPIFFS, LittleFS, PSRamFS)
  SDUCfg.setProgressCb  ( myProgress );         // void (*myProgress)( int state, int size )
  SDUCfg.setMessageCb   ( myDrawMsg );          // void (*myDrawMsg)( const String& label )
  SDUCfg.setErrorCb     ( myErrorMsg );         // void (*myErrorMsg)( const String& message, unsigned long delay )
  SDUCfg.setBeforeCb    ( myBeforeCb );         // void (*myBeforeCb)()
  SDUCfg.setAfterCb     ( myAfterCb );          // void (*myAfterCb)()
  SDUCfg.setSplashPageCb( myDrawSplashPage );   // void (*myDrawSplashPage)( const char* msg )
  SDUCfg.setButtonDrawCb( myDrawPushButton );   // void (*myDrawPushButton)( const char* label, uint8_t position, uint16_t outlinecolor, uint16_t fillcolor, uint16_t textcolor )
  SDUCfg.setWaitForActionCb( myActionTrigger ); // int  (*myActionTrigger)( char* labelLoad, char* labelSkip, unsigned long waitdelay )
  SDUCfg.setSDUBtnPoller( myButtonPoller );     // void (*myButtonPoller)()
  SDUCfg.setSDUBtnA( myBtnAPushedcb );          // bool (*myBtnAPushedcb )()
  SDUCfg.setSDUBtnB( myBtnBPushedcb );          // bool (*myBtnBPushedcb )()
  SDUCfg.setSDUBtnC( myBtnCPushedcb );          // bool (*myBtnCPushedcb )()

Set custom action trigger for update, rollback, save and skip lobby options:

  // int myActionTrigger( char* labelLoad,  char* labelSkip, unsigned long waitdelay )
  // return values: 1=update, 0=rollback, -1=skip
  SDUCfg.setWaitForActionCb( myActionTrigger );

  // Or separately if a UI is available:

  static int buttonAState;
  static int buttonBState;
  static int buttonCState;

  SDUCfg.setSDUBtnPoller( []() {
    buttonAState = digitalRead( 32 );
    buttonBState = digitalRead( 33 );
    buttonCState = digitalRead( 13 );
    delay(50);
  });

  SDUCfg.setSDUBtnA( []() {
    return buttonState==LOW ? true : false;
  });

  SDUCfg.setSDUBtnB( []() {
    return buttonState==LOW ? true : false;
  });

  SDUCfg.setSDUBtnC( []() {
    return buttonState==LOW ? true : false;
  });

Example:

static int myActionTrigger( char* labelLoad,  char* labelSkip, char* labelSave, unsigned long waitdelay )
{
  int64_t msec = millis();
  do {
    if( Serial.available() ) {
      String out = Serial.readStringUntil('\n');
      if(      out == "update" )  return SDU_BTNA_MENU; // load "/menu.bin"
      else if( out == "rollback") return SDU_BTNA_ROLLBACK; // rollback to other OTA partition
      else if( out == "save")     return SDU_BTNC_SAVE; // save current sketch to SD card
      else if( out == "skip" )    return SDU_BTNB_SKIP; // do nothing
      else Serial.printf("Ignored command: %s\n", out.c_str() );
    }
  } while( msec > int64_t( millis() ) - int64_t( waitdelay ) );
  return -1;
}

void setup()
{
  Serial.begin(115200);

  SDUCfg.setAppName( "My Application" );         // lobby screen label: application name
  SDUCfg.setAuthorName( "by @myself" );          // lobby screen label: application author
  SDUCfg.setBinFileName( "/MyApplication.bin" ); // if file path to bin is set for this app, it will be checked at boot and created if not exist

  SDUCfg.setWaitForActionCb( myActionTrigger );

  checkSDUpdater( SD );

}

Set custom progress (for filesystem operations):

  // void (*myProgress)( int state, int size )
  SDUCfg.setProgressCb( myProgress );

Set custom notification/warning messages emitter:

  // void (*myDrawMsg)( const String& label )
  SDUCfg.setMessageCb( myDrawMsg );

Set custom error messages emitter:

  // void (*myErrorMsg)( const String& message, unsigned long delay )
  SDUCfg.setErrorCb( myErrorMsg );

Set pre-update actions (e.g. capture display styles):

  // void (*myBeforeCb)()
  SDUCfg.setBeforeCb( myBeforeCb );

Set post-update actions (e.g. restore display styles):

  // void (*myAfterCb)()
  SDUCfg.setAfterCb( myAfterCb );

Set lobby welcome message (e.g. draw UI welcome screen):

  // void (*myDrawSplashPage)( const char* msg )
  SDUCfg.setSplashPageCb( myDrawSplashPage );

Set buttons drawing function (useful with Touch displays)

  // void (*myDrawPushButton)( const char* label, uint8_t buttonIndex, uint16_t outlinecolor, uint16_t fillcolor, uint16_t textcolor )
  SDUCfg.setButtonDrawCb( myDrawPushButton );

Set buttons state polling function (typically M5.update()

  // void(*myButtonPollCb)();
  SDUCfg.setSDUBtnPoller( myButtonPollCb );

Set each button state getter function, it must return true when the state is "pushed".

  SDUCfg.setSDUBtnA( myBtnAPushedcb ); // bool (*myBtnAPushedcb )()
  SDUCfg.setSDUBtnB( myBtnBPushedcb ); // bool (*myBtnBPushedcb )()
  SDUCfg.setSDUBtnC( myBtnCPushedcb ); // bool (*myBtnCPushedcb )()


📚 SD-Menu loading usage:

Default behaviour: when an app is loaded in memory, booting the M5Stack with the Button A pushed will load and run menu.bin from the filesystem (or from OTA2 if using persistence).

Custom behaviour: the Button A push event car be replaced by any other means (Touch, Serial, Network, Sensor).

Ideally that SD-Menu application should list all available apps on the sdcard and provide means to load them on demand.

For example in the SD-Menu application provided with the examples of this repository, booting the M5Stack with the Button A pushed will power it off.

The built-in Download utility of the SD-Menu is has been moved to the AppStore.ino example, as a result the menu.bin size is reduced and loads faster. This is still being reworked though.

Along with the default SD-Menu example of this repository, some artwork/credits can be added for every uploaded binary. The default SD-Menu application will scan for these file types:

  • .bin compiled application binary

  • .jpg image/icon (max 100x100)

  • .json file with dimensions descriptions:

{"width":120,"height":120,"authorName":"tobozo","projectURL":"http://short.url","credits":"** http://very.very.long.url ~~"}


⚠️ The jpg/json file names must match the bin file name, case matters! jpg/json meta files are optional but must both be set if provided. The value for "credits" JSON property will be scrolled on the top of the screen while the value for projectURL JSON property will be rendered as a QR Code in the info window. It is better provide a short URL for projectURL so the resulting QR Code has more error correction.



🚫 LIMITATIONS:

  • SPIFFS/LittleFS libraries limit their file names (including path) to 32 chars but 16 is recommended as it is also printed in the lobby screen.
  • Long file names will eventually get covered by the jpg image, better stay under 16 chars (not including the extension).
  • Short file names may be treated as 8.3 (e.g 2048.bin becomes 2048.BIN).
  • FAT specifications prevent having more than 512 files on the SD Card, but this menu is limited to 256 Items anyway.

🔘 OPTIONAL:

  • The lobby screen at boot can be customized using SDUCfg.setAppName, SDUCfg.setAuthorName and SDUCfg.setBinFileName. When set, the app name and the binary path will be visible on the lobby screen, and an extra button Button C labelled Save is added to the UI. Pushing this button while the M5Stack is booting will create or overwrite the sketch binary to the SD Card. This can be triggered manually by using saveSketchToFS(SD, fileName, TFCARD_CS_PIN).
  SDUCfg.setAppName( "My Application" ); // lobby screen label: application name
  SDUCfg.setBinFileName( "/MyApplication.bin" ); // if file path to bin is set for this app, it will be checked at boot and created if not exist
  • It can also be disabled (although this requires to use the early callback setters):
#define SDU_HEADLESS
#include "M5StackUpdater.h"
  • Although not extensively tested, the default binary name to be loaded (/menu.bin) can be changed at compilation time by defining the MENU_BIN constant:
#define MENU_BIN "/my_custom_launcher.bin"
#include "M5StackUpdater.h"
  • Gzipped firmwares are supported when SDU_ENABLE_GZ macro is defined or when ESP32-targz.h was previously included. The firmware must have the .gz. extension and be a valid gzip file to trigger the decompression.
#define SDU_ENABLE_GZ // enable support for gzipped firmwares
#include "M5StackUpdater.h"

void setup()
{
  checkSDUpdater( SD, "/menu.gz", 2000 );
}
  • The JoyPSP and M5Stack-Faces Controls for M5Stack SD Menu necessary code are now disabled in the menu example but the code stays here and can be used as a boilerplate for any other two-wires input device.

⚠️ KNOWN ISSUES

  • SD was not declared in this scope: make sure your #include <SD.h> is made before including <M5StackUpdater.h>
  • Serial message [ERROR] No filesystem selected or [ERROR] No valid filesystem selected: try SDUCfg.setFS( &SD ) prior to calling the SDUpdater.

🛣 ROADMAP:

  • Completely detach the UI/Display/Touch/Buttons from the codebase
  • Support gzipped binaries - Migrate Downloader / WiFiManager to external apps
  • esp-idf support
  • Contributors welcome!

#️⃣ REFERENCES:


🎬 Video demonstration https://youtu.be/myQfeYxyc3o
🎬 Video demo of Pacman + sound Source
🎬 Video demo of NyanCat Source
🎓 Macsbug's article on M5Stack SD-Updater 🇯🇵 🇬🇧 (google translate)

🙏 CREDITS:


👍 M5Stack M5Stack https://github.com/m5stack/M5Stack
👍 M5StackSam Tom Such https://github.com/tomsuch/M5StackSAM
👍 ArduinoJSON Benoît Blanchon https://github.com/bblanchon/ArduinoJson/
👍 QRCode Richard Moore https://github.com/ricmoo/qrcode
👍 @Reaper7 Reaper7 https://github.com/reaper7
👍 @PartsandCircuits PartsandCircuits https://github.com/PartsandCircuits
👍 @lovyan03 らびやん https://github.com/lovyan03
👍 @matsumo Matsumo https://github.com/matsumo
👍 @riraosan Riraosan https://github.com/riraosan
👍 @ockernuts ockernuts https://github.com/ockernuts

More Repositories

1

ESP32-BLECollector

ᛡᛒ BLE Scanner + Data persistence on SD Card for M5Stack, M5Core2, Odroid-Go, ESP32-Wrover-Kit and other models
C
256
star
2

ESP32-USB-Soft-Host

An Arduino wrapper to @sdima1357's usb_soft_host esp-idf example
C
249
star
3

ESP32-Chimera-Core

ESP32-Chimera-Core 👾 is an arduino library for M5Stack, Odroid-Go, D-Duino-32-XS and other ESP32/TFT/SD bundles
C++
153
star
4

ESP32-targz

🗜️ An Arduino library to unpack/uncompress tar, gz, and tar.gz files on ESP32 and ESP8266
C++
119
star
5

WUD-Ducky

An ESP32-S2 RubberDucky script parser, with Mouse/PenDrive support 🦆
C
94
star
6

ESP32-BLEBeaconSpam

BLE Beacon spam on ESP32
C++
50
star
7

ESP32-PsRamFS

🐏 💾 RamDisk for ESP32-Arduino using PsRam and fs::FS
C
46
star
8

Rotatey_Cube

🧊 3D gyrocsopic rotating cube on Arduino using an MPU650 and SSD1306 OLED
C++
40
star
9

ESP32-3DPrinter-Bridge

A network <=> ESP32 <=> USB (FTDI) <=> 3D printer Bridge
C++
38
star
10

YAMLDuino

YAML <=> JSON converter for ESP32, ESP8266, RP2040 and possibly other devices
C
34
star
11

M5Tube

Video player for M5Stack with Docker conversion support
C++
30
star
12

ESP8266-Hobo-Clock

Self adjustable ESP8266 clock without RTC and NTP
Arduino
24
star
13

esp32-DCF77_Analyzer_Clock

⏰ An ESP32 TFT interpretation of the @deruiter's DCF77-Analyzer-Clock-V2.0
C
23
star
14

ESP8266SDUpdater

💾 ESP8266 Prequel to M5Stack-SD-Updater
C++
23
star
15

esp8266_deauther_serial

Serial version of spacehuhn/esp8266_deauther
C
22
star
16

WiFiChanViz

WiFi scanner with visual persistence, intended to find the idlest channel e.g. to assign to a ZigBee device
C
20
star
17

ESP32-ENC28J60

ENC28J60 Ethernet driver for ESP32-Arduino 2.0.5, lwip compliant
C
19
star
18

ESP32-Raytracer

An ESP32 study based on Dmitry V. Sokolov's tinyraytracer https://github.com/ssloy/tinyraytracer
C
19
star
19

Rotatey_Balls

🧊 3D animated gyro balls with ESP32, SSD1306 and MPU6050
Arduino
17
star
20

M5Stack-ESP12-Shield

ESP12 shield for M5Stack - Flash your ESP through the M5!
C
16
star
21

ImgurUploader

🖼️ imgur.com 📤 image/video uploader library for ESP32
C++
14
star
22

signal-logr

Wifi Location signal logger written in NodeJS for Raspberry Pi now with RTL-SDR support
JavaScript
14
star
23

ESP32-GifPlayer

GIF player Demo for M5Stack, Odroid-GO, ESP32-Wrover-Kit, LoLinD32-Pro, D-Duino32-XS, and more...
C++
13
star
24

ESP32-SIDView

SidWiz viewer for ESP32 [WORK IN PROGRESS]
C++
13
star
25

M5Rotatey_Cube

🧊 Modified M5Stack/MPU9250 gyro/accel demo sketch
C++
11
star
26

ESP32-Hector

M5Stack sprite+ULP-sound demo
C
10
star
27

ESP32-AmigaBoingBall

An interpretation of the famous Amiga Boing Ball Demo on ESP32 with the help of @bodmer's TFT_eSPI
C++
10
star
28

ESP32-Tesseract

4D 🧊 Hypercube animation demo for ESP32/TFT based on Space Dandy's Catherine
C
10
star
29

LGFXMeter

LGFX based Gauge Decoration and Animation library 🧭
C++
10
star
30

jqsheep

This jQuery plugin animates sheeps around page elements
JavaScript
10
star
31

M5Stack-Raytracer

An ESP32 study based on Dmitry V. Sokolov's tinyraytracer https://github.com/ssloy/tinyraytracer
C
9
star
32

TFT_GIF

Animated GIF on ST7735 with ESP8266
C
9
star
33

ESP32-MSGEQ7

🔊 📈 MSGEQ7 based AudioSpectrum Visualizer for ESP32 and TFT
C++
8
star
34

M5Stack-Rickroll

Offline rickrolling for your M5Stack
C
8
star
35

ESP32-BLETimeServer

Time server module for ESP32-BLECollector
C++
7
star
36

M5Stack-NyanCat

A NyanCat for your M5Stack
C
6
star
37

YOURLS-GeoShortURL

🔗 YOURLS plugin to add per-country support to existing short URLs
PHP
6
star
38

kde-thumbnailer-qoi

KDE thumbnailer for QOI (Quite OK Image Format)
CMake
6
star
39

esp32-qemu-sim

Github action to run ESP32 compiled binaries in QEmu and collects output logs
Shell
6
star
40

tobozo.github.io

HTML
5
star
41

node-deauther

A HTTP to Serial bridge for esp8266_deauther
JavaScript
5
star
42

WiFiManagerTz

A NTP/Timezone extension to @tzapu's WiFiManager
C++
5
star
43

SerialServer

ESP8266WebServer to Serial syntactic substitute
C++
4
star
44

ESPWifiBench

ESP8266 / ESP32 Wifi bench
Arduino
4
star
45

ghetto_blaster

Rotary controls for OLED Ghetto Blaster
C
4
star
46

SpinningRat

Horizontally spinning rat 🐀 for M5Unified
C
3
star
47

rpi-camsentry

Pan / Tilt controls + head tracking + video streaming over websocket from RaspiCam
JavaScript
3
star
48

M5Core2-SoundBouncer

M5Stack / M5Core2 demo animating 16 metronomes
C
3
star
49

ESP32-reSID

reSID is a Vice MOS6581/MOS8580 Sound Interface Device Emulator for ESP32
C
2
star
50

ESP32-Flocker

🏗️ App-suite builder for ESP32
Shell
1
star
51

php-tor-control-port

CSS
1
star
52

dotfiles

1
star