Need help with M5Stack-SD-Updater?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

tobozo
205 Stars 31 Forks MIT License 955 Commits 82 Opened issues

Description

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

Services available

!
?

Need anything else?

Contributors list

# 17,446
C
Shell
Arduino
CSS
802 commits
# 453,054
sd-card
C++
C
m5stack
42 commits
# 123,963
C
C++
Arduino
m5stack
3 commits
# 51,599
C++
C
Arduino
esp-idf
1 commit
# 401,137
Shell
sd-card
C++
C
1 commit
# 741,813
sd-card
C++
C
m5stack
1 commit

License: MIT Build Status Gitter arduino-library-badge

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 SD_MMC, SPIFFS, LittleFS and PSRamFS.


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 :floppy_disk: 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 
    // #include 
    // #include 
    // #include 
    // #include 
    // #include 

</m5stickc.h></esp32-chimera-core.h></m5gfx.h></lovyangfx.h></m5core2.h></m5stack.h>

And add this after the include:

    #include 

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( &amp;SD );
SDUCfg.setWaitForActionCb( mySerialActionTrigger ); // set your own serial input trigger

SDUpdater sdUpdater( &amp;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.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 )

Set custom action trigger for

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

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  1; // load "/menu.bin"
      else if( out == "rollback") return  0; // rollback to other OTA partition
      else if( out == "save")     return  2; // save current sketch to SD card
      else if( out == "skip" )    return -1; // 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.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):

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

Set custom notification/warning messages emitter:

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

Set custom error messages emitter:

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

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

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

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

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

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

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

Set buttons drawing function (useful with Touch displays)

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



📚 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 build-in Download utility of the SD-Menu is currently limited to ESP32-Wrover builds as it uses a significant part of memory and flash space. While it is being reworked and moved to an external app, moving the files manually is the only solution for non-psram devices.

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 200x100)

  • .json file with dimensions descriptions:

{"width":128,"height":128,"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:

  • SD Library limits file names (including path) to 32 chars but 16 is recommended.
  • 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.
  • Long file names will eventually get covered by the jpg image, better stay under 16 chars (not including the extension).


🔘 OPTIONAL:

  • The lobby screen at boot can be customized using
    SDUCfg.setAppName
    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 recommended (because not extensevely 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"
  • 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 
    is made before including
    
    
  • 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:



| | | | | ------------ |:------------------------ | :------------------------------------------- | | :clapper: | Video demonstration | https://youtu.be/myQfeYxyc3o | | :clapper: | Video demo of Pacman + sound | Source | | :clapper: | 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 |

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.