Showing preview only (900K chars total). Download the full file or copy to clipboard to get everything.
Repository: tobozo/M5Stack-SD-Updater
Branch: master
Commit: 02559369aa29
Files: 168
Total size: 846.5 KB
Directory structure:
gitextract_couysrsq/
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── scripts/
│ │ └── semver.sh
│ ├── tools/
│ │ ├── SD-Apps/
│ │ │ ├── after_deploy.sh
│ │ │ ├── before_deploy.sh
│ │ │ ├── before_install.sh
│ │ │ ├── functions.sh
│ │ │ ├── gen-apps.sh
│ │ │ ├── gen-menu.sh
│ │ │ ├── get-deps.sh
│ │ │ ├── get-precompiled.sh
│ │ │ ├── install.sh
│ │ │ ├── install_master.sh
│ │ │ ├── install_unstable.sh
│ │ │ └── script.sh
│ │ ├── common.sh
│ │ ├── cron.sh
│ │ ├── deploy.sh
│ │ ├── git-describe.awk
│ │ ├── hooker.php
│ │ ├── ino2fw.sh
│ │ └── mkfw-build.sh
│ └── workflows/
│ ├── ArduinoBuild.yml
│ ├── PlatformioBuild.yml
│ ├── cron.yml
│ └── onrelease.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── LICENSE
├── README.md
├── component.mk
├── examples/
│ ├── AppStore/
│ │ ├── AppStore.ino
│ │ ├── main/
│ │ │ └── main.cpp
│ │ ├── modules/
│ │ │ ├── AppStoreActions/
│ │ │ │ ├── AppStoreActions.cpp
│ │ │ │ └── AppStoreActions.hpp
│ │ │ ├── AppStoreMain/
│ │ │ │ ├── AppStoreMain.cpp
│ │ │ │ └── AppStoreMain.hpp
│ │ │ ├── AppStoreUI/
│ │ │ │ ├── AppStoreUI.cpp
│ │ │ │ └── AppStoreUI.hpp
│ │ │ ├── Assets/
│ │ │ │ ├── Assets.cpp
│ │ │ │ ├── Assets.h
│ │ │ │ └── Assets.hpp
│ │ │ ├── CertsManager/
│ │ │ │ ├── CertsManager.cpp
│ │ │ │ └── CertsManager.hpp
│ │ │ ├── Console/
│ │ │ │ ├── Console.cpp
│ │ │ │ └── Console.hpp
│ │ │ ├── Downloader/
│ │ │ │ ├── Downloader.cpp
│ │ │ │ └── Downloader.hpp
│ │ │ ├── FSUtils/
│ │ │ │ ├── FSUtils.cpp
│ │ │ │ └── FSUtils.hpp
│ │ │ ├── MenuItems/
│ │ │ │ ├── MenuItems.cpp
│ │ │ │ └── MenuItems.hpp
│ │ │ ├── MenuUtils/
│ │ │ │ ├── MenuUtils.cpp
│ │ │ │ └── MenuUtils.hpp
│ │ │ ├── Registry/
│ │ │ │ ├── Registry.cpp
│ │ │ │ └── Registry.hpp
│ │ │ └── misc/
│ │ │ ├── compile_time.h
│ │ │ ├── config.h
│ │ │ ├── controls.h
│ │ │ ├── core.h
│ │ │ ├── i18n.h
│ │ │ └── lang/
│ │ │ ├── i18n.cn.h
│ │ │ ├── i18n.en.h
│ │ │ ├── i18n.jp.h
│ │ │ └── i18n.kr.h
│ │ └── platformio.ini
│ ├── CopySketchToFS/
│ │ └── CopySketchToFS.ino
│ ├── FactoryLauncher/
│ │ ├── FactoryLauncher.ino
│ │ └── partitions.csv
│ ├── GzLauncher/
│ │ └── GzLauncher.ino
│ ├── Headless/
│ │ └── Headless.ino
│ ├── LGFX-SDLoader-Snippet/
│ │ ├── LGFX-SDLoader-Snippet.ino
│ │ └── M5Stack_Buttons.h
│ ├── M5Core2-SDLoader-Snippet/
│ │ └── M5Core2-SDLoader-Snippet.ino
│ ├── M5Stack-FW-Menu/
│ │ ├── M5Stack-FW-Menu.ino
│ │ ├── ReadMe.md
│ │ ├── partitions/
│ │ │ ├── partitions-16MB-factory-4-apps.csv
│ │ │ └── partitions-16MB-factory-6-apps.csv
│ │ ├── platformio.ini
│ │ └── src/
│ │ └── main.cpp
│ ├── M5Stack-LittleFS-Snippet/
│ │ └── M5Stack-LittleFS-Snippet.ino
│ ├── M5Stack-SD-Menu/
│ │ ├── M5Stack-SD-Menu.ino
│ │ ├── SAM.h
│ │ ├── SD-Content/
│ │ │ └── json/
│ │ │ ├── Colours_Demo.json
│ │ │ ├── CrackScreen.json
│ │ │ ├── Downloader.json
│ │ │ ├── FlappyBird.json
│ │ │ ├── Lora_Frequency_Hopping.json
│ │ │ ├── LovyanLauncher.json
│ │ │ ├── MultiApps-Adv.json
│ │ │ ├── NyanCat.json
│ │ │ ├── NyanCat_Ext.json
│ │ │ ├── Oscilloscope.json
│ │ │ ├── PacketMonitor.json
│ │ │ ├── Pacman-JoyPSP.json
│ │ │ ├── Pixel-Fun.json
│ │ │ ├── Raytracer.json
│ │ │ ├── Rickroll.json
│ │ │ ├── RotateyCube.json
│ │ │ ├── SWRasterizer.json
│ │ │ ├── Sokoban.json
│ │ │ ├── SpaceDefense.json
│ │ │ ├── SpaceShooter.json
│ │ │ ├── Tetris.json
│ │ │ ├── Thermal-Camera.json
│ │ │ ├── TobozoLauncher.json
│ │ │ ├── Tube.json
│ │ │ ├── WiFiScanner.json
│ │ │ ├── arduinomegachess.json
│ │ │ ├── d_invader.json
│ │ │ ├── drawNumber.json
│ │ │ ├── menu.json
│ │ │ └── mp3-player.json
│ │ ├── assets/
│ │ │ └── workflow.dia
│ │ ├── assets.h
│ │ ├── certificates.h
│ │ ├── compile_time.h
│ │ ├── controls.h
│ │ ├── core.h
│ │ ├── downloader.h
│ │ ├── fsformat.h
│ │ ├── i18n.h
│ │ ├── main/
│ │ │ └── main.cpp
│ │ ├── menu.h
│ │ ├── partition_manager.h
│ │ ├── partitions_16MB_4_or_6_apps.csv
│ │ ├── partitions_16MB_8apps.csv
│ │ ├── platformio.ini
│ │ ├── registry.default.h
│ │ ├── registry.h
│ │ └── wifi_manager.h
│ ├── M5Stack-SDLoader-Snippet/
│ │ └── M5Stack-SDLoader-Snippet.ino
│ ├── M5StickC-SPIFFS-Loader-Snippet/
│ │ └── M5StickC-SPIFFS-Loader-Snippet.ino
│ ├── M5Unified/
│ │ └── M5Unified.ino
│ ├── MultiFS/
│ │ └── MultiFS.ino
│ ├── SdFatUpdater/
│ │ └── SdFatUpdater.ino
│ └── Test/
│ └── build_test/
│ ├── dev_lib_deps.ini
│ ├── main/
│ │ └── main.cpp
│ └── platformio.ini
├── library.json
├── library.properties
└── src/
├── ConfigManager/
│ ├── ConfigManager.cpp
│ └── ConfigManager.hpp
├── FS/
│ ├── ffat.hpp
│ ├── littlefs.hpp
│ ├── sd.hpp
│ ├── sd_mmc.hpp
│ ├── sdfat.hpp
│ └── spiffs.hpp
├── M5StackUpdater.h
├── M5StackUpdater.hpp
├── PartitionManager/
│ ├── NVS/
│ │ ├── NVSUtils.cpp
│ │ └── NVSUtils.hpp
│ ├── PartitionManager.cpp
│ ├── PartitionManager.hpp
│ └── Partitions/
│ ├── PartitionUtils.cpp
│ └── PartitionUtils.hpp
├── SDUpdater/
│ ├── SDUpdater_Class.cpp
│ ├── SDUpdater_Class.hpp
│ └── Update_Interface.hpp
├── UI/
│ ├── Touch.hpp
│ ├── UI.hpp
│ └── common.hpp
├── gitTagVersion.h
└── misc/
├── assets.h
├── config.h
└── types.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
.github/tools/SD-Apps/* linguist-vendored
.github/tools/* linguist-vendored
.github/tools/scripts/* linguist-vendored
#.github/tools/*.sh -linguist-detectable
*.sh linguist-detectable=false
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [lovyan03, tobozo] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
custom: # Replace with a single custom sponsorship URL
================================================
FILE: .github/scripts/semver.sh
================================================
#!/bin/bash
function patchpropertyfiles {
old_tag=$1
new_tag=$2
echo "Patching property/json/tag files $old_tag => $new_tag"
if [ -f "$GITHUB_WORKSPACE/library.json" ]; then
sed -i -e "s/\"$old_tag\"/\"$new_tag\"/g" $GITHUB_WORKSPACE/library.json
cat $GITHUB_WORKSPACE/library.json
fi
if [ -f "$GITHUB_WORKSPACE/library.properties" ]; then
sed -i -e "s/version=$old_tag/version=$new_tag/g" $GITHUB_WORKSPACE/library.properties
cat $GITHUB_WORKSPACE/library.properties
fi
if [ -f "$GITHUB_WORKSPACE/src/gitTagVersion.h" ]; then
sed -i -e "s/\"$old_tag\"/\"$new_tag\"/g" $GITHUB_WORKSPACE/src/gitTagVersion.h
cat $GITHUB_WORKSPACE/src/gitTagVersion.h
fi
}
if [ $GITHUB_EVENT_NAME == "workflow_dispatch" ]; then
echo "Workflow dispatched event, guessing version from properties file"
localtag=`cat $GITHUB_WORKSPACE/library.properties | grep version`
RELEASE_TAG=${localtag//version=/ }
minor=true
else
if [ ! $GITHUB_EVENT_NAME == "release" ]; then
echo "Wrong event '$GITHUB_EVENT_NAME'!"
exit 1
fi
EVENT_JSON=`cat $GITHUB_EVENT_PATH`
action=`echo $EVENT_JSON | jq -r '.action'`
if [ ! $action == "published" ]; then
echo "Wrong action '$action'. Exiting now..."
exit 0
fi
draft=`echo $EVENT_JSON | jq -r '.release.draft'`
if [ $draft == "true" ]; then
echo "It's a draft release. Exiting now..."
exit 0
fi
RELEASE_PRE=`echo $EVENT_JSON | jq -r '.release.prerelease'`
RELEASE_TAG=`echo $EVENT_JSON | jq -r '.release.tag_name'`
RELEASE_BRANCH=`echo $EVENT_JSON | jq -r '.release.target_commitish'`
RELEASE_ID=`echo $EVENT_JSON | jq -r '.release.id'`
RELEASE_BODY=`echo $EVENT_JSON | jq -r '.release.body'`
echo "Event: $GITHUB_EVENT_NAME, Repo: $GITHUB_REPOSITORY, Path: $GITHUB_WORKSPACE, Ref: $GITHUB_REF"
echo "Action: $action, Branch: $RELEASE_BRANCH, ID: $RELEASE_ID"
echo "Tag: $RELEASE_TAG, Draft: $draft, Pre-Release: $RELEASE_PRE"
fi
# Increment a version string using Semantic Versioning (SemVer) terminology.
major=false;
minor=false;
patch=false;
while getopts ":MmpF:" Option
do
case $Option in
M ) major=true;;
m ) minor=true;;
p ) patch=true;;
F )
version=${OPTARG}
if [ "$version" == "auto" ]; then
# default to patch
patch=true
else
forcedversion=true
echo "Forcing version to $version"
fi
;;
* ) echo "NOTHING TO DO";;
esac
done
if [ $OPTIND -eq 1 ]; then
echo "No options were passed, assuming patch level"
patch=true
fi
if [ -z ${RELEASE_TAG} ]
then
echo "Couldn't determine version"
exit 1
fi
if [ "$forcedversion" != "true" ]; then
# Build array from version string.
a=( ${RELEASE_TAG//./ } )
major_version=0
# If version string is missing or has the wrong number of members, show usage message.
if [ ${#a[@]} -ne 3 ]; then
echo "usage: $(basename $0) [-Mmp] major.minor.patch"
exit 1
fi
# Increment version numbers as requested.
if [ $major == "true" ]; then
echo "Raising MAJOR"
# Check for v in version (e.g. v1.0 not just 1.0)
if [[ ${a[0]} =~ ([vV]?)([0-9]+) ]]; then
v="${BASH_REMATCH[1]}"
major_version=${BASH_REMATCH[2]}
((major_version++))
a[0]=${v}${major_version}
else
((a[0]++))
major_version=a[0]
fi
a[1]=0
a[2]=0
fi
if [ $minor == "true" ]; then
echo "Raising MINOR"
((a[1]++))
a[2]=0
fi
if [ $patch == "true" ]; then
echo "Raising PATCH"
((a[2]++))
fi
version=$(echo "${a[0]}.${a[1]}.${a[2]}")
fi
patchpropertyfiles $RELEASE_TAG $version
================================================
FILE: .github/tools/SD-Apps/after_deploy.sh
================================================
#!/bin/bash
curl -v -H "Authorization: token $GH_TOKEN" --retry 5 "https://api.github.com/repos/tobozo/M5Stack-SD-Updater/releases" | jq -r ".[] | select(.tag_name==\"$TRAVIS_TAG\")" | jq ".assets[] | select(.name=\"$ARCHIVE_ZIP\")" | jq "select(.browser_download_url | contains(\"untagged\") == true ) .browser_download_url"
# curl -v `curl -v -H "Authorization: token $GH_TOKEN" --retry 5 "https://api.github.com/repos/tobozo/M5Stack-SD-Updater/releases" | jq -r ".[] | select(.tag_name | contains(\"untagged\"))" | jq -r ".assets[]" | jq "select(.name==\"SD-Apps-Folder.zip\")" | jq -r "select(.browser_download_url | contains(\"untagged\") ) .browser_download_url"`
================================================
FILE: .github/tools/SD-Apps/before_deploy.sh
================================================
#!/bin/bash
cd $PWD
#if ! [[ $TRAVIS_TAG ]]; then
# git config --global user.email "travis@travis-ci.org"
# git config --global user.name "Travis CI"
# git tag ${TRAVIS_TAG}
#fi
#cd $REPO_NAME
pwd
cd $TRAVIS_BUILD_DIR
export git_version_last="$(git describe --abbrev=0 --tags --always)"
export git_version_next="v$(echo $git_version_last | awk -F . '{ printf "%d.%d.%d", $1,$2,$3 + 1}')"
#cd ..
echo $TRAVIS_BRANCH | grep "unstable" && export prerelease=true || export prerelease=false
git tag ${git_version_next}
echo "Before deploy Travis tag : $TRAVIS_TAG"
echo "Git version last : $git_version_last"
echo "Git version next : $git_version_next"
echo "Travis Branch : $TRAVIS_BRANCH"
echo "Is pre-release : $prerelease"
cd /home/travis/build/tobozo/
if [ -f $REPO_NAME/src/gitTagVersion.h ]; then
echo "#define M5_SD_UPDATER_VERSION F(\"${TRAVIS_TAG}\")" > $REPO_NAME/src/gitTagVersion.h
else
echo "Can't patch $REPO_NAME/src/gitTagVersion.h !!!"
sleep 5
exit 1
fi
zip -r $TRAVIS_BUILD_DIR/$REPO_NAME.zip $REPO_NAME -x *.git* -x "$REPO_NAME/examples/M5Stack-SD-Menu/SD-Apps" -x "$REPO_NAME/examples/M5Stack-SD-Menu/SD-Content"
if [ -f $TRAVIS_BUILD_DIR/$REPO_NAME.zip ]; then
echo "Successfully Created $TRAVIS_BUILD_DIR/$REPO_NAME.zip :-)";
else
echo "Failed to create $TRAVIS_BUILD_DIR/$REPO_NAME.zip !!!";
sleep 5
exit 1
fi
cd $M5_SD_BUILD_DIR
zip -r $TRAVIS_BUILD_DIR/$ARCHIVE_ZIP ./
if [ -f $TRAVIS_BUILD_DIR/$ARCHIVE_ZIP ]; then
echo "Successfully created $TRAVIS_BUILD_DIR/$ARCHIVE_ZIP :-)";
else
echo "Failed to create $TRAVIS_BUILD_DIR/$ARCHIVE_ZIP !!!";
sleep 5
exit 1
fi
cd $TRAVIS_BUILD_DIR
# export BODY=$(cat CHANGELOG.md) # boo! Travis doesn't like multiline body
================================================
FILE: .github/tools/SD-Apps/before_install.sh
================================================
#!/bin/bash
# REQUIRES: $IDE_VERSION, $M5_SD_BUILD_DIR
#date -u
#uname -a
#git fetch -t
#env | sort
#git log `git describe --tags --abbrev=0 HEAD^ --always`..HEAD --oneline
if [[ "$IDE_VERSION" != "" ]]; then
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
sleep 3
export DISPLAY=:1.0
export JAVA_ARGS="-Djavax.jmdns.level=OFF"
wget --quiet "http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz" # &>/dev/null 2>&1
if [ -f arduino-$IDE_VERSION-linux64.tar.xz ]; then
echo "Downloaded arduino-$IDE_VERSION-linux64.tar.xz"
else
echo "Failed to download arduino-$IDE_VERSION-linux64.tar.xz"
sleep 5
exit 1
fi
tar xf arduino-$IDE_VERSION-linux64.tar.xz &>/dev/null
mv arduino-$IDE_VERSION ~/arduino-ide
rm arduino-$IDE_VERSION-linux64.tar.xz
export PATH=$PATH:~/arduino-ide
mkdir -p $M5_SD_BUILD_DIR
else
echo "NO IDE VERSION !!"
sleep 5
exit 1
fi
================================================
FILE: .github/tools/SD-Apps/functions.sh
================================================
#!/bin/bash
function movebin {
find /tmp -name \*.partitions.bin -exec rm {} \; #<-- you need that backslash before and space after the semicolon
#find /tmp -name \*.ino.elf -exec rename 's/.ino.elf/.ino.bin/' {} \; # sometimes arduino produces ELF, sometimes it's BIN
find /tmp -name \*.ino.bin -exec rename 's/.ino.bin/.bin/' {} \; #
find /tmp -name \*.bin -exec rename 's/(_for)?(_|-)?(m5)_?(stack)?(-|_)?//ig' {} \; #
export DIRTY_BIN_FILE=`basename $( find /tmp -name \*.bin )`
export BIN_FILE="${DIRTY_BIN_FILE^}"
export DIRTY_FILE_BASENAME=${DIRTY_BIN_FILE%.bin}
export FILE_BASENAME=${BIN_FILE%.bin}
find /tmp -name \*.bin -exec mv {} $M5_SD_BUILD_DIR/ \; #<-- you need that backslash before and space after the semicolon
if [ "$BIN_FILE" != "$DIRTY_BIN_FILE" ]; then
mv $M5_SD_BUILD_DIR/$DIRTY_BIN_FILE $M5_SD_BUILD_DIR/$BIN_FILE
echo "[++++] UpperCasedFirst() $DIRTY_BIN_FILE to $BIN_FILE"
fi
echo $BIN_FILE
}
function injectupdater {
export outfile=$PATH_TO_INO_FILE;
echo "***** Injecting $1"
awk '/#include <M5Stack.h>/{print;print "#include <M5StackUpdater.h>\nSDUpdater sdUpdater;";next}1' $outfile > tmp && mv tmp $outfile;
awk '/M5.begin()/{print;print " if(digitalRead(BUTTON_A_PIN) == 0) { sdUpdater.updateFromFS(SD); ESP.restart(); } ";next}1' $outfile > tmp && mv tmp $outfile;
# the M5StackUpdater requires Wire.begin(), inject it if necessary
egrep -R "Wire.begin()" || (awk '/M5.begin()/{print;print " Wire.begin();";next}1' $outfile > tmp && mv tmp $outfile);
# the display driver changed, get rid of default rotation in setup
sed -i -e 's/M5.Lcd.setRotation(0);/\/\//g' $outfile
# remove any hardcoded credentials so wifi auth can be done from another app (e.g. wifimanager)
sed -i -e 's/WiFi.begin(ssid, password);/WiFi.begin();/g' $outfile
sed -i -e 's/WiFi.begin(SSID, PASSWORD);/WiFi.begin();/g' $outfile
echo "***** Injection successful"
}
function populatemeta {
echo "***** Populating meta"
export IMG_NAME=${FILE_BASENAME}_gh.jpg
export REPO_URL=`git config remote.origin.url`
export REPO_OWNER_URL=`echo ${REPO_URL%/*}`
export REPO_USERNAME=$(echo "$REPO_URL" | cut -d "/" -f4)
export AVATAR_URL=$REPO_OWNER_URL.png?size=200
export JSONFILE="$M5_SD_BUILD_DIR/json/$FILE_BASENAME.json"
export IMGFILE="$M5_SD_BUILD_DIR/jpg/$FILE_BASENAME.jpg"
export AVATARFILE="$M5_SD_BUILD_DIR/jpg/${FILE_BASENAME}_gh.jpg"
if [ -f $JSONFILE ]; then
echo "JSON Meta file $JSONFILE exists, should check for contents or leave it be"
else
if [ -f "$M5_SD_BUILD_DIR/json/$DIRTY_FILE_BASENAME.json" ]; then
echo "[++++] UpperCasedFirst() $DIRTY_FILE_BASENAME.json, renaming other meta components"
mv $M5_SD_BUILD_DIR/json/$DIRTY_FILE_BASENAME.json $JSONFILE
mv $M5_SD_BUILD_DIR/jpg/$DIRTY_FILE_BASENAME.jpg $IMGFILE &>/dev/null
mv $M5_SD_BUILD_DIR/jpg/${DIRTY_FILE_BASENAME}_gh.jpg $AVATARFILE &>/dev/null
sed -i -e "s/$DIRTY_FILE_BASENAME/$FILE_BASENAME/g" $JSONFILE &>/dev/null
else
echo "[++++] No $JSONFILE JSON Meta file found, creating from the ether"
export REPO_SHORTURL=`git.io $REPO_URL`
if [ "" != "$REPO_SHORTURL" ]; then
echo "{\"width\":120,\"height\":120, \"authorName\":\"@$REPO_USERNAME\", \"projectURL\": \"$REPO_SHORTURL\",\"credits\":\"$REPO_OWNER_URL\"}" > $JSONFILE
else
echo "{\"width\":120,\"height\":120, \"authorName\":\"@$REPO_USERNAME\", \"projectURL\": \"$REPO_URL\",\"credits\":\"$REPO_OWNER_URL\"}" > $JSONFILE
fi
fi
fi
cat $JSONFILE
# no gist in URL is valid to retrieve the profile pic
AVATAR_URL=`sed 's/gist.//g' <<< $AVATAR_URL`
if [ ! -f $AVATARFILE ]; then
echo "**** Will download avatar from $AVATAR_URL and save it as $AVATARFILE"
wget –-quiet $AVATAR_URL --output-document=temp
convert temp -resize 120x120 $AVATARFILE
identify $AVATARFILE
rm temp
fi
echo "***** Populating successful"
}
================================================
FILE: .github/tools/SD-Apps/gen-apps.sh
================================================
#!/bin/bash
my_dir="$(dirname "$0")"
source $my_dir/functions.sh
readonly ARDUINO_CI_SCRIPT_ARDUINO_OUTPUT_FILTER_REGEX='(^\[SocketListener\(travis-job-*|^ *[0-9][0-9]*: [0-9a-g][0-9a-g]*|^dns\[query,[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*:[0-9][0-9]*, length=[0-9][0-9]*, id=|^dns\[response,[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*:[0-9][0-9]*, length=[0-9][0-9]*, id=|^questions:$|\[DNSQuestion@|type: TYPE_IGNORE|^\.\]$|^\.\]\]$|^.\.\]$|^.\.\]\]$)'
readonly ARDUINO_CI_SCRIPT_SUCCESS_EXIT_STATUS=0
readonly ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS=1
cp -R $TRAVIS_BUILD_DIR/examples/M5Stack-SD-Menu/SD-Content/jpg $M5_SD_BUILD_DIR/
cp -R $TRAVIS_BUILD_DIR/examples/M5Stack-SD-Menu/SD-Content/json $M5_SD_BUILD_DIR/
cp -R $TRAVIS_BUILD_DIR/examples/M5Stack-SD-Menu/SD-Content/mp3 $M5_SD_BUILD_DIR/
export hidecompilelogs=1
for D in *; do
if [ -d "${D}" ]; then
echo "moving to ${D}";
cd ${D};
# ls -la
egrep -R M5StackUpdater && egrep -R updateFromFS && export m5enabled=1 || export m5enabled=0;
if (( $m5enabled == 1 )); then
case "$D" in
'Pixel-Fun-M5Stack')
echo "Renaming $D ino file"
mv PixelFun.ino Pixel-Fun-M5Stack.ino
sed -i -e 's/ILI9341/M5Display/g' Mover.cpp # https://github.com/neoxharsh/Pixel-Fun-M5Stack/issues/1
#export hidecompilelogs=0
export hidecompilelogs=1
;;
'M5Stack_LovyanToyBox')
export hidecompilelogs=1
cd LovyanToyBox
;;
'M5Stack-SetWiFi_Mic')
echo "Duplicating Meta";
cp -Rf microSD/jpg $M5_SD_BUILD_DIR/
cp -Rf microSD/json $M5_SD_BUILD_DIR/
;;
'M5Tube')
#export hidecompilelogs=0
;;
#*)
#;;
esac
export PATH_TO_INO_FILE="$(find ${SDAPP_FOLDER}/${D} -type f -iname *.ino)";
else
export hidecompilelogs=1
case "$D" in
'M5Stack_LovyanToyBox')
export hidecompilelogs=1
cd LovyanToyBox
;;
'M5StackSandbox')
export hidecompilelogs=1
cd SWRasterizer
if [ -d "Sd-Content" ]; then
cp -Rf Sd-Content/* $M5_SD_BUILD_DIR/
fi
;;
'd_invader')
echo "Should replace esp_deep_sleep => esp_sleep"
# esp_deep_sleep => esp_sleep
;;
'M5Stack_CrackScreen')
echo "Fixing path to crack.jpg"
sed -i 's/\/crack.jpg/\/jpg\/crack.jpg/g' M5Stack_CrackScreen.ino
cp crack.jpg $M5_SD_BUILD_DIR/jpg/crack.jpg
;;
'M5Stack-CrazyAsteroids')
cat Crazy_Asteroid.ino EntrySection.ino ExitSection.ino printAsteroid.ino printSpaceShip.ino > out.blah
rm *.ino
mv out.blah M5Stack-CrazyAsteroids.ino
;;
'M5Stack_Particle_demo')
# this is an Arduino compatible Platformio project
mv main.cpp M5Stack_Particle_demo.ino
sed -i -e 's/define LCD_WIDTH 320/define LCD_WIDTH M5.Lcd.width() \/\//g' M5Stack_Particle_demo.ino
sed -i -e 's/define LCD_HEIGHT 240/define LCD_HEIGHT M5.Lcd.height() \/\//g' M5Stack_Particle_demo.ino
;;
'M5Stack_WebRadio_Avator')
echo "Patching esp8266Audio with getLevel()"
sed -i -e 's/bool SetOutputModeMono/int getLevel();\nbool SetOutputModeMono/g' ~/Arduino/libraries/ESP8266Audio-master/src/AudioOutputI2S.h
sed -i -e 's/include "AudioOutputI2S.h"/include "AudioOutputI2S.h"\n\n int aout_level = 0; int AudioOutputI2S::getLevel() { return aout_level; }/g' ~/Arduino/libraries/ESP8266Audio-master/src/AudioOutputI2S.cpp
sed -i -e 's/int16_t l/aout_level = (int)sample[RIGHTCHANNEL];\nint16_t l/g' ~/Arduino/libraries/ESP8266Audio-master/src/AudioOutputI2S.cpp
;;
'M5Stack-WiFiScanner')
# remove unnecessary include causing an error
sed -i -e 's/#include <String.h>/\/\//g' M5Stack-WiFiScanner.ino
;;
#'M5Stack-MegaChess')
# # fix rotation problem caused by display driver changes (now applied globally)
# sed -i -e 's/M5.Lcd.setRotation(0);/\/\//g' arduinomegachess_for_m5stack.ino
#;;
#'M5Stack-Pacman-JoyPSP')
#;;
#'M5Stack-SpaceShooter')
#;;
#'M5Stack-ESP32-Oscilloscope')
#;;
#'M5Stack-NyanCat')
#;;
#'M5Stack-Rickroll')
#;;
'M5Stack_NyanCat_Ext')
echo "Renaming file to prevent namespace collision"
mv M5Stack_NyanCat.ino M5Stack_NyanCat_Ext.ino
wget -–quiet https://github.com/jimpo/nyancat/raw/master/nyancat.mp3 --output-document=$M5_SD_BUILD_DIR/mp3/NyanCat.mp3
sed -i 's/\/NyanCat.mp3/\/mp3\/NyanCat.mp3/g' M5Stack_NyanCat_Ext.ino
;;
'M5Stack_lifegame')
echo "Renaming file to .ino"
mv M5Stack_lifegame M5Stack_lifegame.ino
;;
'M5Stack-Tetris')
echo "Renaming Tetris to M5Stack-Tetris + changing path to bg image"
mv Tetris.ino M5Stack-Tetris.ino
sed -i 's/\/tetris.jpg/\/jpg\/tetris_bg.jpg/g' M5Stack-Tetris.ino
cp tetris.jpg $M5_SD_BUILD_DIR/jpg/tetris_bg.jpg
;;
#'SpaceDefense-m5stack')
#;;
'M5Stack_FlappyBird_game')
# put real comments to prevent syntax error
sed -i -e 's/#By Ponticelli Domenico/\/\/By Ponticelli Domenico/g' M5Stack_FlappyBird.ino
;;
#'M5Stack-PacketMonitor')
#;;
#'M5Stack_Sokoban')
#;;
'M5Stack-Thermal-Camera')
echo "Renaming to M5Stack-Thermal-Camera.ino"
mv thermal_cam_interpolate.ino M5Stack-Thermal-Camera.ino
;;
'mp3-player-m5stack')
echo "Changing mp3 path in sketch"
# TODO: fix this
sed -i 's/createTrackList("\/")/createTrackList("\/mp3")/g' mp3-player-m5stack.ino
;;
esac
export PATH_TO_INO_FILE="$(find ${SDAPP_FOLDER}/${D} -type f -iname *.ino)";
injectupdater # $PATH_TO_INO_FILE
fi
echo "**** Compiling ${PATH_TO_INO_FILE}";
#arduino --preserve-temp-files --verify --board $BOARD $PATH_TO_INO_FILE >> $SDAPP_FOLDER/out.log && movebin && populatemeta
set +o errexit
# shellcheck disable=SC2086
# eval \"arduino --preserve-temp-files --verify --board $BOARD $PATH_TO_INO_FILE\" &>/dev/null | tr --complement --delete '[:print:]\n\t' | tr --squeeze-repeats '\n' | grep --extended-regexp --invert-match "$ARDUINO_CI_SCRIPT_ARDUINO_OUTPUT_FILTER_REGEX"
if (( $hidecompilelogs == 0 )); then
arduino --preserve-temp-files --verbose-build --verify --board $BOARD $PATH_TO_INO_FILE
else
arduino --preserve-temp-files --verbose-build --verify --board $BOARD $PATH_TO_INO_FILE &>/dev/null
fi
# local -r arduinoPreferenceSettingExitStatus="${PIPESTATUS[0]}"
export arduinoPreferenceSettingExitStatus="${PIPESTATUS[0]}"
set -o errexit
#arduino --preserve-temp-files --verify --board $BOARD $PATH_TO_INO_FILE | tr --complement --delete '[:print:]\n\t' | tr --squeeze-repeats '\n' | grep --extended-regexp --invert-match "$ARDUINO_CI_SCRIPT_ARDUINO_OUTPUT_FILTER_REGEX"
# local -r arduinoInstallPackageExitStatus="${PIPESTATUS[0]}"
#if [[ "$arduinoPreferenceSettingExitStatus" != "$ARDUINO_CI_SCRIPT_SUCCESS_EXIT_STATUS" ]]; then
movebin && populatemeta
#else
# echo "**** Bad exit status"
#fi
ls $M5_SD_BUILD_DIR -la;
cd $SDAPP_FOLDER
fi
done
ls $M5_SD_BUILD_DIR/jpg -la;
ls $M5_SD_BUILD_DIR/json -la;
# egrep -R M5StackUpdater $SDAPP_FOLDER/*
# egrep -R updateFromFS $SDAPP_FOLDER/*
================================================
FILE: .github/tools/SD-Apps/gen-menu.sh
================================================
#!/bin/bash
# Generate TobozoLauncher.bin and BetaLauncher.bin
cd $TRAVIS_BUILD_DIR;
arduino --pref "compiler.warning_level=none" --save-prefs &>/dev/null
arduino --pref "build.warn_data_percentage=75" --save-prefs &>/dev/null
arduino --pref "boardsmanager.additional.urls=https://dl.espressif.com/dl/package_esp32_index.json" --save-prefs &>/dev/null
arduino --install-boards esp32:esp32 &>/dev/null
arduino --board $BOARD --save-prefs &>/dev/null
export inofile=$SDAPP_FOLDER/../$EXAMPLE.ino
export outfile=$SDAPP_FOLDER/../downloader.h
if [ -f $inofile ]; then
echo "Compiling $inofile"
arduino --preserve-temp-files --verbose-build --verify $inofile &>/dev/null
find /tmp/arduino* -name \*.partitions.bin -exec rm {} \; #
find /tmp/arduino* -name \*.bin -exec mv {} $M5_SD_BUILD_DIR/TobozoLauncher.bin \; #
if [ -f $M5_SD_BUILD_DIR/TobozoLauncher.bin ]; then
cp $M5_SD_BUILD_DIR/TobozoLauncher.bin $M5_SD_BUILD_DIR/menu.bin
else
echo "ERROR: Failed to compile $inofile, aborting"
sleep 5
exit 1
fi
else
echo "ERROR: cannot compile menu.bin"
sleep 5
exit 1
fi
if [ -f $outfile ]; then
echo "Attempting to enable unstable channel by patching $outfile"
sed -i -e 's/"\/sd-updater"/"\/sd-updater\/unstable"/g' $outfile
grep unstable $outfile && export patchok=1 || export patchok=0
if (( $patchok == 1 )); then
echo "Compiling Beta $inofile"
arduino --preserve-temp-files --verbose-build --verify $inofile &>/dev/null
find /tmp/arduino* -name \*.partitions.bin -exec rm {} \; #
find /tmp/arduino* -name \*.bin -exec mv {} $M5_SD_BUILD_DIR/BetaLauncher.bin \; #
if [ -f $M5_SD_BUILD_DIR/BetaLauncher.bin ]; then
# fine
echo "SUCCESS: compiled BetaLauncher.bin from $inofile"
else
echo "ERROR: Failed to compile BetaLauncher.bin from $inofile, aborting"
sleep 5
exit 1
fi
else
echo "ERROR: Patching unstable channel failed !!";
sleep 5
exit 1
fi
else
echo "ERROR: cannot compile BetaLauncher.bin"
sleep 5
exit 1
fi
echo "Fake Binary" >> $M5_SD_BUILD_DIR/Downloader.bin
echo "Launchers Compilation successful"
================================================
FILE: .github/tools/SD-Apps/get-deps.sh
================================================
#!/bin/bash
# TODO: foreach this from JSON
wget --quiet https://github.com/adafruit/Adafruit_NeoPixel/archive/master.zip --output-document=Adafruit_NeoPixel.zip
unzip -d ~/Arduino/libraries Adafruit_NeoPixel.zip
wget --quiet https://github.com/adafruit/Adafruit_AMG88xx/archive/1.0.2.zip --output-document=Adafruit_AMG88xx.zip
unzip -d ~/Arduino/libraries Adafruit_AMG88xx.zip
wget --quiet https://github.com/bblanchon/ArduinoJson/archive/6.x.zip --output-document=Arduinojson-master.zip
unzip -d ~/Arduino/libraries Arduinojson-master.zip
# wget https://github.com/tomsuch/M5StackSAM/archive/master.zip --output-document=M5StackSAM-master.zip
wget --quiet https://github.com/tobozo/M5StackSAM/archive/patch-1.zip --output-document=M5StackSAM-master.zip
unzip -d ~/Arduino/libraries M5StackSAM-master.zip
#wget https://github.com/m5stack/M5Stack/archive/master.zip --output-document=M5Stack.zip
#curl -v --retry 5 "https://api.github.com/repos/M5Stack/M5Stack/releases/latest?access_token=$GH_TOKEN" | jq -r ".zipball_url" | wget --output-document=M5Stack.zip -i -
#unzip -d ~/Arduino/libraries M5Stack.zip
wget --quiet https://github.com/tobozo/ESP32-Chimera-Core/archive/master.zip --output-document=ESP32-Chimera-Core.zip
unzip -d ~/Arduino/libraries ESP32-Chimera-Core.zip
wget --quiet https://github.com/earlephilhower/ESP8266Audio/archive/master.zip --output-document=ESP8266Audio.zip
unzip -d ~/Arduino/libraries ESP8266Audio.zip
wget --quiet https://github.com/Seeed-Studio/Grove_BMP280/archive/1.0.1.zip --output-document=Grove_BMP280.zip
unzip -d ~/Arduino/libraries Grove_BMP280.zip
wget --quiet https://github.com/Gianbacchio/ESP8266_Spiram/archive/master.zip --output-document=ESP8266_Spiram.zip
unzip -d ~/Arduino/libraries ESP8266_Spiram.zip
wget --quiet http://www.buildlog.net/blog/wp-content/uploads/2018/02/Game_Audio.zip --output-document=Game_Audio.zip
mkdir -p ~/Arduino/libraries/Game_Audio
unzip -d ~/Arduino/libraries/Game_Audio Game_Audio.zip
wget --quiet https://github.com/lovyan03/M5Stack_TreeView/archive/master.zip --output-document=M5Stack_TreeView.zip
unzip -d ~/Arduino/libraries M5Stack_TreeView.zip
wget --quiet https://github.com/lovyan03/M5Stack_OnScreenKeyboard/archive/master.zip --output-document=M5Stack_OnScreenKeyboard.zip
unzip -d ~/Arduino/libraries M5Stack_OnScreenKeyboard.zip
wget --quiet https://github.com/kosme/arduinoFFT/archive/master.zip --output-document=arduinoFFT.zip
unzip -d ~/Arduino/libraries arduinoFFT.zip
rm -f *.zip
================================================
FILE: .github/tools/SD-Apps/get-precompiled.sh
================================================
#!/bin/bash
#- curl -v --retry 5 "https://api.github.com/repos/lovyan03/M5Stack_LovyanLauncher/releases/latest?access_token=$GH_TOKEN" | jq -r "".assets[0].browser_download_url"" | wget --output-document=M5Burner.zip -i -
#- unzip -d /tmp M5Burner.zip
#- cp /tmp/M5Burner/firmwares/LovyanLauncher/LovyanLauncher.bin $M5_SD_BUILD_DIR/
#- rm -Rf /tmp/M5Burner
# TODO: foreach this from JSON source ( URL / channel )
cd /tmp
wget --quiet https://github.com/lovyan03/M5Stack_LovyanLauncher/archive/master.zip --output-document=M5Stack_LovyanLauncher.zip
unzip -d /tmp M5Stack_LovyanLauncher.zip
cp -Rf /tmp/M5Stack_LovyanLauncher-master/LovyanLauncher/build/* $M5_SD_BUILD_DIR/
rm -Rf /tmp/M5Stack_LovyanLauncher*
wget --quiet https://github.com/lovyan03/M5Stack_LovyanToyBox/archive/master.zip --output-document=M5Stack_LovyanToyBox.zip
unzip -d /tmp M5Stack_LovyanToyBox.zip
cp -Rf /tmp/M5Stack_LovyanToyBox-master/LovyanToyBox/build/* $M5_SD_BUILD_DIR/
rm -Rf /tmp/M5Stack_LovyanToyBox*
wget --quiet https://github.com/robo8080/SD_Updater_TestData/archive/master.zip --output-document=SD-Apps.zip
unzip -d /tmp SD-Apps.zip
cp -uf /tmp/SD_Updater_TestData-master/*.bin $M5_SD_BUILD_DIR/
cp -Rf /tmp/SD_Updater_TestData-master/jpg $M5_SD_BUILD_DIR/
cp -Rf /tmp/SD_Updater_TestData-master/json $M5_SD_BUILD_DIR/
rm -Rf /tmp/SD_Updater_TestData*
wget --quiet https://github.com/mongonta0716/M5Stack-Avatar-fugu1/archive/master.zip --output-document=M5Stack-Avatar-fugu1.zip
unzip -d /tmp M5Stack-Avatar-fugu1.zip
cp -Rf /tmp/M5Stack-Avatar-fugu1-master/Avatar_fugu/jpg $M5_SD_BUILD_DIR/
cp -Rf /tmp/M5Stack-Avatar-fugu1-master/Avatar_fugu/json $M5_SD_BUILD_DIR/
cp -Rf /tmp/M5Stack-Avatar-fugu1-master/Avatar_fugu/*.bin $M5_SD_BUILD_DIR/
rm -Rf /tmp/M5Stack-Avatar-fugu1-master
wget --quiet https://github.com/EiichiroIto/m5apple2/archive/master.zip --output-document=m5apple2.zip
unzip -d /tmp m5apple2.zip
cp -Rf /tmp/m5apple2-master/bin/* $M5_SD_BUILD_DIR/
rm -Rf /tmp/m5apple2*
wget --quiet https://github.com/phillowcompiler/2048_M5Stack/archive/master.zip --output-document=2048_M5Stack.zip
unzip -d /tmp 2048_M5Stack.zip
cp -Rf /tmp/2048_M5Stack-master/build/* $M5_SD_BUILD_DIR/
rm -Rf /tmp/2048_M5Stack*
cd $M5_SD_BUILD_DIR
# force lowercase extensions
find . -name '*.*' -exec sh -c 'a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
================================================
FILE: .github/tools/SD-Apps/install.sh
================================================
#!/bin/bash
gem install git.io
if [ -f $SDAPP_FOLDER/install_$TRAVIS_BRANCH.sh ]; then
source $SDAPP_FOLDER/install_$TRAVIS_BRANCH.sh
cd $TRAVIS_BUILD_DIR;
mkdir -p ~/Arduino/libraries
# link the project's folder into the libraries folder
ln -s $PWD ~/Arduino/libraries/.
else
echo "No install script for this branch"
fi
echo "Installing extra libraries"
source $SDAPP_FOLDER/get-deps.sh
================================================
FILE: .github/tools/SD-Apps/install_master.sh
================================================
#!/bin/bash
git submodule update --init --recursive
cd $SDAPP_FOLDER
# pull latest code from submodules
git submodule foreach --recursive git pull origin master
================================================
FILE: .github/tools/SD-Apps/install_unstable.sh
================================================
#!/bin/bash
#git submodule update --init --recursive
#cd $SDAPP_FOLDER
## pull latest code from submodules
#git submodule foreach --recursive git pull origin master
================================================
FILE: .github/tools/SD-Apps/script.sh
================================================
#!/bin/bash
source $SDAPP_FOLDER/gen-menu.sh
if [ "$TRAVIS_BRANCH" != "master" ]; then
# UNSTABLE
export git_version_last="$(git describe --abbrev=0 --tags --always)"
export git_version_next="v$(echo $git_version_last | awk -F . '{ printf "%d.%d.%d", $1,$2,$3 + 1}')"
echo "Computed last version : $git_version_last"
echo "Computed next version : $git_version_next"
echo "Will download last binaries"
#export LAST_SDAPP_FILE="SD-Apps-Folder.zip"
#curl --retry 5 "https://api.github.com/repos/tobozo/M5Stack-SD-Updater/releases/latest?access_token=$GH_TOKEN" | jq -r ".assets[0].browser_download_url" | wget --output-document=$LAST_SDAPP_FILE -i -
curl -H "Authorization: token $GH_TOKEN" --retry 5 "https://api.github.com/repos/tobozo/M5Stack-SD-Updater/releases" | jq -r ".[] | select(.tag_name==\"$git_version_last\")" | jq -r ".assets[] | select(.name==\"$ARCHIVE_ZIP\") .browser_download_url" | wget --output-document=$ARCHIVE_ZIP -q -i -
if [ -f $ARCHIVE_ZIP ]; then
echo "$ARCHIVE_ZIP found"
ls $ARCHIVE_ZIP -la
else
echo "ERROR: Could not find a valid $ARCHIVE_ZIP from latest releases, time to tune up jq queries ?"
sleep 5
exit 1
fi
unzip -d /tmp/$ARCHIVE_ZIP $ARCHIVE_ZIP
cp -Ruf /tmp/$ARCHIVE_ZIP/* $M5_SD_BUILD_DIR/
echo "Fetching precompiled projects"
source $SDAPP_FOLDER/get-precompiled.sh
ls $M5_SD_BUILD_DIR/ -la
sleep 15 # give some time to the logs to come up
else
# MASTER
#if [ ! -z "$TRAVIS_TAG" ]; then
# # zip the package if tagged build
# tools/build-release.sh -a$ESP32_GITHUB_TOKEN
#else
# # run cmake and sketch tests
# tools/check_cmakelists.sh && tools/build-tests.sh
#fi
source $SDAPP_FOLDER/gen-apps.sh
fi
================================================
FILE: .github/tools/common.sh
================================================
#!/bin/bash
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
sleep 3
echo "[DEBUG] WORK_DIR=$WORK_DIR"
echo "[DEBUG] WORK_SPACE=$WORK_SPACE"
echo "[DEBUG] repo_branch=$REPO_BRANCH"
echo "[DEBUG] M5_SD_BUILD_DIR=$M5_SD_BUILD_DIR"
echo "[DEBUG] M5_BURNER_DIR=$M5_BURNER_DIR"
echo "[DEBUG] APPLICATION_FOLDER=$APPLICATION_FOLDER"
echo "[DEBUG] SKETCHBOOK_FOLDER=$SKETCHBOOK_FOLDER"
export DISPLAY=:1.0
export PATH=$PATH:~/arduino-ide
export logDir=$WORK_DIR/artifacts
export arduino_installed=0
export platformio_installed=0
export needs_firmware=0
export repo_branch=$REPO_BRANCH
export varTagName=`$WORK_SPACE/tools/git-describe.awk $WORK_SPACE`
prn() { printf "$(date '+%Y/%m/%d %H:%M:%S') [$1] "; printf "$2\n"; }
msg() { prn MSG "$1"; }
log() { [ ${VERBOSE} -ge 1 ] || return 0; prn LOG "$1" >&2; }
dbg() { [ ${VERBOSE} -ge 2 ] || return 0; prn DBG "$1" >&2; }
err() { prn ERR "$1" >&2; [ -n "${2}" ] && exit ${2}; }
if [[ "$M5_SD_BUILD_DIR" == "" ]]; then
echo "[ERROR] Workflow did not set M5_SD_BUILD_DIR variable"
exit 1
fi
mkdir -p $M5_SD_BUILD_DIR
if [[ "$M5_BURNER_DIR" != "" ]]; then
mkdir -p $M5_BURNER_DIR/firmware_4MB
fi
mkdir -p ~/Arduino/libraries
mkdir -p $logDir
# set -o xtrace
# set -v
function m5burner_json {
# $1: path to the JSON file (required, will be overwritten)
# create JSON file as in M5Burner standards
if [[ "$1" == "" ]]; then
echo "[ERROR] No m5burner.json path provided"
exit 1
fi
tee $1 << XXX
{
"name": "$SDCardAppNameSpace",
"description": "$repo_desc",
"keywords": "M5Stack-App-Registry generated firwmare",
"author": "$repo_author",
"repository": "$repo_url",
"firmware_category": [
{
"Stack-4MB": {
"path": "firmware_4MB",
"device": [
"M5Stack 4MB Model (default partition)"
],
"default_baud": 921600
}
}
],
"version": "$varTagName",
"framework": "$platform"
}
XXX
}
function get_arduino_app {
# $1: URL to the repo (required)
# $2: repo name (required)
if [ "$1" == "" ]; then
echo "[FAIL] Can't install a repo without a URL!!"
exit 1
else
appURL=$1
# TODO: find out if the URL points to a repo or to a zip file
fi
if [ "$2" == "" ]; then
echo "[FAIL] Can't install a repo without a name!!"
exit 1
else
repoName=$2
fi
cd $WORK_DIR
if [ "$3" == "" ]; then
echo "[INFO] Cloning $repoName @ $appURL into $WORK_DIR"
git clone --depth=1 $appURL $repoName
else
branchName=$3
git clone --depth=1 -b $branchName $appURL $repoName
fi
cd $repoName
if [[ "$pre_hook" != "" && "$pre_hook" != "null" ]]; then
echo "[INFO] Running pre-hook : $pre_hook"
eval $pre_hook
fi
}
function gen_arduino_app {
# requires : $REPO_URL, $REPO_NAME, $REPO_BRANCH, $inofile, $outfile, $M5_SD_BUILD_DIR, $WORK_DIR
cd $WORK_DIR
if [ ! -d "$repo_name" ]; then
get_arduino_app $repo_url $repo_name $repo_branch
else
cd $WORK_DIR
cd $repo_name
fi
#if [[ "$pre_hook" != "" && "$pre_hook" != "null" ]]; then
# echo "[INFO] Running pre-hook : $pre_hook"
# eval $pre_hook
#fi
egrep -R M5StackUpdater && egrep -R updateFromFS && export m5enabled=1 || export m5enabled=0;
if (( $m5enabled == 1 )); then
echo "[INFO] This app is already using the Sd-Updater library"
else
injectupdater
fi
copy_assets
if [ -f $inofile ]; then
echo "[INFO] Compiling $inofile"
arduino --preserve-temp-files --verbose-build --verify $inofile >> $logDir/compilation.log
#movebin "/tmp/arduino_build*"
newmovebin "/tmp/arduino_build*"
#if [ -f $M5_SD_BUILD_DIR/$outfile ]; then
# echo "[SUCCESS] $inofile has been compiled and saved into $outfile"
#else
# echo "[ERROR] Failed to compile $inofile, aborting"
# sleep 5
# exit 1
#fi
#populatemeta
newpopulatemeta
else
echo "[ERROR] could not compile $outfile from $inofile"
sleep 5
exit 1
fi
if [[ "$post_hook" != "" && "$post_hook" != "null" ]]; then
echo "[INFO] Running post-hook : $post_hook"
eval $post_hook
fi
}
function install_arduino_lib {
# $1: URL to the zip file (required)
# $2: archive name (optional)
if [ "$1" == "" ]; then
echo " [FAIL] Can't install a library without a URL!!"
exit 1
else
repoURL=$1
# TODO: check that the url points to a zip file
if [[ "$repoURL" == *.zip ]]; then
echo " [INFO] library URL is a zip file"
else
echo " [WARNING] library URL is not a zip file, will be extrapolated"
if [[ "$repoURL" == *"github"* ]]; then
if [[ "${repoURL: -1}" == "/" ]]; then
repoURL=${repoURL}archive/master.zip
else
repoURL=$repoURL/archive/master.zip
fi
else
echo " [ERROR] Can't extrapolate from a non github address, URL needs to point to a zip file"
fi
fi
fi
if [ "$2" == "" ]; then
if [[ "$repoURL" == *"github"* ]]; then
# repoOwner=`echo ${repoURL} | cut -d/ -f4`
repoName=`echo ${repoURL} | cut -d/ -f5`
# repoSlug="${repoOwner}_${repoName}"
zipName="${repoName}.zip"
else
#echo ${repoURL} | cut -d/ -f5
zipName="archive.zip"
fi
else
zipName=$2
fi
if [ "$3" == "" ]; then
# no dirname provided
libdir=~/Arduino/libraries
if [[ "$repoURL" == *"github"* && "$zipName" == "archive.zip" ]]; then
# TODO extract lib name from repoURL ?
libdir=~/Arduino/libraries/${zipName%.zip}
fi
else
libdir=~/Arduino/libraries/$3
fi
mkdir -p $libdir
ls -la
HTTP_RESPONSE=$(curl -L --silent --write-out "HTTPSTATUS:%{http_code}" $repoURL --output $zipName)
ls $zipName -la
if [ $? -ne 0 ]; then
echo " [FAIL] $? => aborting";
exit 1;
fi
unzip -qq -o -d $libdir $zipName && export zip_worked=true
if [ "$zip_worked" == "true" ]; then
echo " [OK] $repoURL"
rm $zipName
ls $libdir -la
else
echo " [FAIL] Bad Zip archive for $repoURL"
sleep 5
exit 1
fi
}
function copy_assets {
# assets inventory
if [ "$precompiled" == "1" ]; then
echo "Assets:"
echo "-------"
assetFolders=`jq -r '.repo.precompiled | keys[] as $k | "\($k)"' $appJSONPath`
for assetFolder in $(echo $assetFolders | sed 's/[[:space:]]/\n/g'); do # | sed s/:/\\n/
echo " [$assetFolder]"
if [[ "$assetFolder" == "bin" ]]; then
destpath=$M5_SD_BUILD_DIR
if [[ "$include_bin_assets" == "0" ]]; then
continue
fi
else
destpath=$M5_SD_BUILD_DIR/$assetFolder
mkdir -p $destpath
fi
for asset in $(echo "$jsonCode" | jq -r .repo.precompiled.$assetFolder[]); do
echo " - $asset"
# TODO: copy those files to the build folder
basename_asset=`basename $asset`
cp "$asset" "$destpath/$basename_asset"
done
done
echo
fi
}
function get_remote_app {
if [[ "$1" == "" ]]; then
echo "[ERROR] No appJSONPath provided"
exit 1
fi
appJSONPath=/tmp/app.json
echo "[INFO] Fetching meta file $1 into $appJSONPath"
HTTP_RESPONSE=$(curl -L --silent --write-out "HTTPSTATUS:%{http_code}" $1 --output $appJSONPath)
if [ $? -ne 0 ]; then
echo "[FAIL] $? => aborting";
sleep 5
exit 1;
fi
source=0
precompiled=0
has_deps=0
has_source=0
jsonCode=`cat $appJSONPath`
# echo "Fetched json code:\n${jsonCode}\n"
repo_url=`echo "${jsonCode}" | jq -r .repo.url`
repo_name=`echo "${jsonCode}" | jq -r .repo.name`
repo_desc=`echo "${jsonCode}" | jq -r .repo.description`
repo_author=`echo "${jsonCode}" | jq -r .author.name`
repo_updated_at=`echo "${jsonCode}" | jq -r .repo.updated_at`
libDepsResp=`echo "$jsonCode" | jq -r .repo.source.lib_deps`
sourceFilesResp=`echo "$jsonCode" | jq -r .repo.source.ino`
expectedOutFileName=`echo "$jsonCode" | jq -r .repo.source.bin_name`
SDCardAppNameSpace=`echo "$jsonCode" | jq -r .repo.source.sd_namespace`
platform=`echo "$jsonCode" | jq -r .repo.source.platform`
pre_hook=`echo "$jsonCode" | jq -r '.repo.source | .["pre-hook"]'`
post_hook=`echo "$jsonCode" | jq -r '.repo.source | .["post-hook"]'`
app_name=`echo "$jsonCode" | jq -r .repo.name`
# collect bundle types
for row in $(echo "${jsonCode}" | jq -r .repo.type[]); do
echo "Found available bundle format: ${row}"
if [ "${row}" == "source" ]; then
source=1 # json has source tree
fi
if [ "${row}" == "precompiled" ]; then
precompiled=1 # json has bin tree
fi
done
echo
# evaluate if the source version has library dependencies and is usable
if [[ "$libDepsResp" == "null" || "$libDepsResp" == "" || "$libDepsResp" == '[]' ]]; then
has_deps=0 # json has no library deps
else
has_deps=1 # json has library deps
fi
if [ "$sourceFilesResp" == "null" ]; then
echo "[INFO] Json has no ino_file"
has_source=0 # json has no ino_file
else
has_source=1 # json has ino_file
export ino_array=`echo "$jsonCode" | jq -r .repo.source.ino[]`
export array_size=`echo $ino_array | wc -l`
fi
if [ "$has_source" == "1" ]; then
if [[ "$expectedOutFileName" == "null" || "$expectedOutFileName" == "" ]]; then
if [[ "$SDCardAppNameSpace" == "null" || "$SDCardAppNameSpace" == "" ]]; then
echo "[ERROR] Neither bin_name nor sd_namespace found in JSON"
echo "[ERROR] Please implement repo.source.bin_name or repo.source.sd_namespace in package $1"
echo "[INFO] TODO: hook delete and regen JSON"
exit 1
else
expectedOutFileName="$SDCardAppNameSpace.bin"
echo "[INFO] Using namespace : $SDCardAppNameSpace";
echo "[WARNING] Forced bin_name from namespace : $SDCardAppNameSpace"
fi
else
echo "[INFO] Using bin_name = $expectedOutFileName"
if [[ "$SDCardAppNameSpace" == "null" || "$SDCardAppNameSpace" == "" ]]; then
SDCardAppNameSpace=${expectedOutFileName%.bin}
echo "[WARNING] Forced namespace from bin_name : $SDCardAppNameSpace"
else
echo "[INFO] Using namespace : $SDCardAppNameSpace";
fi
fi
else
SDCardAppNameSpace=${repo_name}
fi
include_bin_assets=0
# build information
if [ "$source" == "1" ]; then
echo "Source code:"
echo "------------"
echo " Platform: $platform"
if [[ $platform == *"arduino"* ]]; then
board=`echo "$jsonCode" | jq -r .repo.source.board`
install_arduino
get_arduino_app $repo_url $repo_name $repo_branch
# install_arduino_libraries
echo " Board: $board"
if [ "$has_deps" == "1" ]; then
echo " Library requirements:"
for libraryURL in $(echo "${jsonCode}" | jq -r .repo.source.lib_deps[]); do
install_arduino_lib $libraryURL
done
else
install_arduino_libraries
fi
if [ "$has_source" == "1" ]; then
if [[ "$inofile_forced" != "" ]]; then
# echo " [FORCED] Will conpile $inofile_forced"
inofile=$inofile_forced
gen_arduino_app
else
if [ $array_size > 1 ]; then
for inofile in $(echo $ino_array | sed 's/\n/\n/g'); do
# echo " [ARRAY] Will compile $inofile"
gen_arduino_app
break
done
else
inofile=$ino_array
# echo " [INFO] Will compile $ino_array"
gen_arduino_app
fi
fi
else
# JSON says there's no ino/cpp source on this repo, use the binaries
# get_arduino_app $repo_url $repo_name
echo "Can't use the source on this one, let's hope the binaries are fine"
include_bin_assets=1
copy_assets
fi
else
# platformio ?
# get_arduino_app $repo_url $repo_name
if [ "$source" == "1" ]; then
get_arduino_app $repo_url $repo_name $repo_branch
install_platformio
if [ "$has_deps" == "1" ]; then
echo " Library requirements:"
for libraryURL in $(echo "${jsonCode}" | jq -r .repo.source.lib_deps[]); do
echo " [INFO] installing library $libraryURL"
python -m platformio lib install $libraryURL
done
fi
gen_platformio_app
else
echo "Can't use the source on this one, let's hope the binaries are fine"
include_bin_assets=1
copy_assets
fi
fi
echo
fi
find $M5_SD_BUILD_DIR
cd $M5_SD_BUILD_DIR
if [[ "${FILE_BASENAME}" != "" ]]; then
zipFileName=${FILE_BASENAME}.zip
else
zipFileName=${SDCardAppNameSpace}.zip
fi
zip -r $zipFileName ./*
rm $M5_SD_BUILD_DIR/*.bin
if [[ "$needs_firmware" != "0" ]]; then
case $APP_BOARD in
m5stack)
echo "[INFO] Project needs M5Burner zip file too"
cd $M5_BURNER_DIR
m5burner_json m5burner.json
# m5BurnerDirName=${SDCardAppNameSpace}-v${varTagName}/${SDCardAppNameSpace}
# mkdir -p ${m5BurnerDirName} && mv firmware_4MB m5burner.json ${m5BurnerDirName}/
zipFileName="M5Burner-$zipFileName"
# cd ${m5BurnerDirName}
# zip -r $zipFileName ./*
zip -r $zipFileName *
#zip -jrq $zipFileName ${m5BurnerDirName}
cp $zipFileName $M5_SD_BUILD_DIR/
echo "[INFO] M5Burner zip file created: $zipFileName"
;;
odroid)
echo "[INFO] Project needs OdroidFW zip file too"
if [ ! -f $IMGFILE ]; then
echo "[ERROR] can't make a firmware without a pic"
exit 1
fi
cd $M5_BURNER_DIR
# git clone https://github.com/othercrashoverride/odroid-go-firmware.git -b factory
# cd odroid-go-firmware/tools/mkfw
# make
# chmod +x mkfw
# git clone https://github.com/lstux/OdroidGO/
# chmod +x OdroidGO/ino2fw/mkfw-build.sh OdroidGO/ino2fw/ino2fw.sh
$WORK_SPACE/tools/mkfw-build.sh
ls $WORK_DIR/$repo_name/$inofile -la
ls $IMGFILE -la
set -o xtrace
set -v
#echo "[INFO] sending command: $WORK_SPACE/tools/ino2fw.sh -t $IMGFILE -l $SDCardAppNameSpace -d \"${repo_desc}\" $WORK_DIR/$repo_name/$inofile"
#$WORK_SPACE/tools/ino2fw.sh -t $IMGFILE -l $SDCardAppNameSpace -d "${repo_desc}" $WORK_DIR/$repo_name/$inofile
# cp $M5_SD_BUILD_DIR/$expectedOutFileName firmware.bin
echo "[INFO] sending command: $WORK_SPACE/tools/ino2fw.sh -t $IMGFILE -l $SDCardAppNameSpace -d \"${repo_desc}\" $M5_BURNER_DIR/$expectedOutFileName"
$WORK_SPACE/tools/ino2fw.sh -t $IMGFILE -l $SDCardAppNameSpace -d "${repo_desc}" $M5_BURNER_DIR/$expectedOutFileName
pwd
ls -la
cd $M5_BURNER_DIR
# cp $M5_BURNER_DIR/$expectedOutFileName
echo "$M5_BURNER_DIR :"
ls $M5_BURNER_DIR -la
zipFileName="OdroidFW-$zipFileName"
zip -r $zipFileName ${SDCardAppNameSpace}.fw
cp $zipFileName $M5_SD_BUILD_DIR/
#ls -la
#cp $zipFileName $M5_SD_BUILD_DIR/
# ino2fw.sh [options] {sketch_dir|sketch_file.ino}
# Create a .fw file for Odroid-Go from arduino sketch
# options:
# -b build_path : build in build_path [/tmp/ino2fw]
# -t tile.png : use specified image as tile (resized to 86x48px)
# -l label : set application label
# -d description : set application description
# -v : increase verbosity level
# -h : display help message
#ffmpeg -i $IMGFILE -f rawvideo -pix_fmt rgb565 tile.raw
#cp $M5_SD_BUILD_DIR/$expectedOutFileName firmware.bin
#ls -la
#filesize=$(stat -c%s "$M5_BURNER_DIR/$expectedOutFileName")
#blocks=$(($filesize/64))
#normsize=$(((1+$blocks)*64))
#echo "[INFO] will mkfw '$expectedOutFileName' with hardkernel's tool"
#echo "[TODO] ./mkfw ${SDCardAppNameSpace} tile.raw 0 16 $normsize ${SDCardAppNameSpace} $M5_BURNER_DIR/$expectedOutFileName"
## ./mkfw test tile.raw 0 16 1048576 app $expectedOutFileName
#dbg "./mkfw ${SDCardAppNameSpace} tile.raw 0 16 $normsize ${SDCardAppNameSpace} $expectedOutFileName"
#mv firmware.fw $M5_BURNER_DIR/${SDCardAppNameSpace}.fw
#cd $M5_BURNER_DIR
#zipFileName="OdroidFW-$zipFileName"
#zip -r $zipFileName $expectedOutFileName
#cp $zipFileName $M5_SD_BUILD_DIR/
echo "[INFO] OdroidFW zip file created: $zipFileName"
;;
*)
echo "[ERROR] bad APP_BOARD value: $APP_BOARD"
exit 1
;;
esac
fi
cd $M5_SD_BUILD_DIR
}
function install_platformio {
egrep -R M5StackUpdater && egrep -R updateFromFS && export m5enabled=1 || export m5enabled=0;
if (( $m5enabled == 1 )); then
echo "[INFO] This app is already using the Sd-Updater library"
else
if [[ "$inofile" == "" ]]; then
export ino_array=`echo "$jsonCode" | jq -r .repo.source.ino[]`
# export array_size=`echo $ino_array | wc -l`
inofile=$ino_array
fi
injectupdater
fi
if [[ "$platformio_installed" != "0" ]]; then
echo "plaformio already installed"
return
fi
pip install -U https://github.com/platformio/platformio/archive/develop.zip
python -m platformio platform install https://github.com/platformio/platform-espressif32.git#feature/stage
python -m platformio lib install m5stack-sd-updater
python -m platformio lib install https://github.com/tobozo/ESP32-Chimera-Core
platformio_installed=1
}
function gen_platformio_app {
if [[ "$pre_hook" != "" && "$pre_hook" != "null" ]]; then
echo "[INFO] Running pre-hook : $pre_hook"
eval $pre_hook
fi
copy_assets
echo "[INFO] Compiling $app_name"
python -m platformio run # >> $logDir/compilation.log
#movebin ".pio/"
#if [ -f $M5_SD_BUILD_DIR/$outfile ]; then
# echo "[SUCCESS] $inofile has been compiled and saved into $outfile"
#else
# echo "[ERROR] Failed to compile $inofile, aborting"
# sleep 5
# exit 1
#fi
#populatemeta
newmovebin ".pio/"
newpopulatemeta
if [[ "$post_hook" != "" && "$post_hook" != "null" ]]; then
echo "[INFO] Running pre-hook : $post_hook"
eval $post_hook
fi
}
function install_arduino_libraries {
install_arduino_lib https://github.com/adafruit/Adafruit_NeoPixel/archive/master.zip Adafruit_NeoPixel.zip
install_arduino_lib https://github.com/adafruit/Adafruit_AMG88xx/archive/1.0.2.zip Adafruit_AMG88xx.zip
install_arduino_lib https://github.com/bblanchon/ArduinoJson/archive/6.x.zip Arduinojson-master.zip
install_arduino_lib https://github.com/tobozo/M5StackSAM/archive/patch-1.zip M5StackSAM-master.zip
install_arduino_lib https://github.com/earlephilhower/ESP8266Audio/archive/master.zip ESP8266Audio.zip
install_arduino_lib https://github.com/Seeed-Studio/Grove_BMP280/archive/1.0.1.zip Grove_BMP280.zip
install_arduino_lib https://github.com/Gianbacchio/ESP8266_Spiram/archive/master.zip ESP8266_Spiram.zip
install_arduino_lib http://www.buildlog.net/blog/wp-content/uploads/2018/02/Game_Audio.zip Game_Audio.zip Game_Audio
install_arduino_lib https://github.com/lovyan03/M5Stack_TreeView/archive/master.zip M5Stack_TreeView.zip
install_arduino_lib https://github.com/lovyan03/M5Stack_OnScreenKeyboard/archive/master.zip M5Stack_OnScreenKeyboard.zip
install_arduino_lib https://github.com/kosme/arduinoFFT/archive/master.zip arduinoFFT.zip
}
function install_arduino {
if [[ "$arduino_installed" != "0" ]]; then
echo "arduino already installed"
return
fi
cd $WORK_DIR
if [[ "$platform" != "" ]]; then
BOARD="esp32:esp32:$board:FlashFreq=80"
else
echo "[INFO] Using default board"
BOARD="esp32:esp32:m5stack-core-esp32:FlashFreq=80"
fi
if [[ "$platform" != "" ]]; then
export JAVA_ARGS="-Djavax.jmdns.level=OFF"
wget --quiet "http://downloads.arduino.cc/$platform-linux64.tar.xz" # &>/dev/null 2>&1
if [ -f $platform-linux64.tar.xz ]; then
echo "[OK] Downloaded $platform-linux64.tar.xz"
else
echo "[FAIL] Failed to download $platform-linux64.tar.xz"
sleep 5
exit 1
fi
tar xf $platform-linux64.tar.xz &>/dev/null
mv $platform ~/arduino-ide
rm $platform-linux64.tar.xz
# export PATH=$PATH:~/arduino-ide
releaseAddr=`sed "s/\/tag\//\/download\//g"<<<$(curl -s -D - https://github.com/espressif/arduino-esp32/releases/latest/ -o /dev/null | grep -oP 'Location: \K.*(?=\r)')/package_esp32_index.json`
echo "[OK] Successfully unpacked $platform-linux64.tar.xz and added ~/arduino-ide to PATH"
arduino --pref "compiler.warning_level=none" --save-prefs &>/dev/null
arduino --pref "build.warn_data_percentage=75" --save-prefs &>/dev/null
arduino --pref "boardsmanager.additional.urls=$releaseAddr" --save-prefs &>/dev/null
# arduino --pref "boardsmanager.additional.urls=https://dl.espressif.com/dl/package_esp32_index.json" --save-prefs &>/dev/null
arduino --install-boards esp32:esp32 &>/dev/null
arduino --board $BOARD --save-prefs &>/dev/null
echo "[OK] Successfully installed esp32 board in Arduino IDE"
# mkdir -p ~/Arduino/libraries
wget --quiet https://github.com/tobozo/M5Stack-SD-Updater/archive/$SD_UPDATER_BRANCH.zip --output-document=SD-Updater.zip
unzip -qq -d ~/Arduino/libraries SD-Updater.zip
echo "[OK] Successfully installed SD-Updater library from $SD_UPDATER_BRANCH branch"
# TODO: move this somewhere else
cd ~/Arduino/libraries
git clone $M5_CORE_URL
echo "[OK] Successfully installed M5 Core from $M5_CORE_URL"
export arduino_installed=1
else
echo "[FAIL] NO platform provided !!"
sleep 5
exit 1
fi
}
#
# function parse_yaml (lol, you wish)
#
# https://stackoverflow.com/questions/5014632/how-can-i-parse-a-yaml-file-from-a-linux-shell-script
#
# usage: parse_yaml sample.yml
#
# Data example:
#
# ## global definitions
# global:
# debug: yes
# verbose: no
# debugging:
# detailed: no
# header: "debugging started"
# output:
# file: "yes"
#
# Will output:
#
# global_debug="yes"
# global_verbose="no"
# global_debugging_detailed="no"
# global_debugging_header="debugging started"
# output_file="yes"
#
#
function parse_yaml {
local prefix=$2
local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
sed -ne "s|^\($s\):|\1|" \
-e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
awk -F$fs '{
indent = length($1)/2;
vname[indent] = $2;
for (i in vname) {if (i > indent) {delete vname[i]}}
if (length($3) > 0) {
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
}
}'
}
function newmovebin {
if [ "$1" == "" ]; then
binpath="/tmp/arduino_build*"
else
binpath=$1
fi
echo "[INFO] Searching $binpath"
if [[ "$needs_firmware" != "0" ]]; then # if [[ "$M5_BURNER_DIR" != "" ]]; then
case $APP_BOARD in
m5stack)
echo "[INFO] Building M5Burner firmware package"
find ~/.arduino15/packages/esp32/ -name "boot_app0.bin" -exec cp {} $M5_BURNER_DIR/firmware_4MB/boot_0xe000.bin \; #
find ~/.arduino15/packages/esp32/ -name "bootloader_qio_80m.bin" -exec cp {} $M5_BURNER_DIR/firmware_4MB/bootloader_0x1000.bin \; #
find $binpath -name \*partitions.bin -exec mv {} $M5_BURNER_DIR/firmware_4MB/partitions_0x8000.bin \; #
find $binpath -name \*.bin -exec cp {} $M5_BURNER_DIR/firmware_4MB/${SDCardAppNameSpace}_0x10000.bin \; #
echo "[INFO] Resuming on SD package"
;;
odroid)
echo "[INFO] Building Odroid-Go FW package"
find $binpath -name \*.bin -exec cp {} $M5_BURNER_DIR/$expectedOutFileName \; #<-- you need that backslash before and space after the semicolon
# TODO:
# git clone https://github.com/othercrashoverride/odroid-go-firmware.git -b factory
# cd odroid-go-firmware/tools/mkfw
# make
;;
*)
echo "[ERROR] bad APP_BOARD value: $APP_BOARD"
exit 1
;;
esac
else
find $binpath -name \*partitions.bin -exec rm {} \; #<-- you need that backslash before and space after the semicolon
fi
find $binpath -name \*.bin -exec mv {} $M5_SD_BUILD_DIR/$expectedOutFileName \; #<-- you need that backslash before and space after the semicolon
echo "[INFO] Kept bin file: $expectedOutFileName"
if [ ! -f $M5_SD_BUILD_DIR/$expectedOutFileName ]; then
echo "[ERROR] bin copy failed : $M5_SD_BUILD_DIR/$expectedOutFileName"
echo "[TODO]: escalate this"
exit 1
fi
}
function movebin {
binpath=$1
if [ "$binpath" == "" ]; then
echo "[ERROR] Can't move bin file without path"
exit 1
fi
# remove the "partitions.bin" file first
find $binpath -name \*partitions.bin -exec rm {} \; #<-- you need that backslash before and space after the semicolon
# blind rename ".ino.bin" to ".bin" (Arduino IDE does that)
find $binpath -name \*.ino.bin -exec rename -v 's/.ino.bin/.bin/' {} \; # wut
if [[ "$platform" == "platformio" || "$DIRTY_BIN_FILE" == "firmware.bin" ]]; then
n=`find $binpath -name *.bin`
dn=$(dirname $n)
fn=$(basename $n)
cfn=`echo "$fn" | perl -pe 's/(_for)?(_|-)?(m5)_?(stack)?(-|_)?//ig'`
mv "$n" "${dn}/${app_name^}.bin"
export DIRTY_BIN_FILE=`basename $( find $binpath -name \*.bin )`
export BIN_FILE="${app_name^}.bin"
export DIRTY_FILE_BASENAME=${app_name%.bin}
# echo ${hello//[0-9]/}
else
if [ "$expectedOutFileName" == "null" ]; then
# use sketch default bin filename
find $binpath -name \*.bin -exec rename -v 's/(_for)?(_|-)?(m5)_?(stack)?(-|_)?//ig' {} \; #
export DIRTY_BIN_FILE=`basename $( find $binpath -name \*.bin )`
export BIN_FILE="${DIRTY_BIN_FILE^}"
export DIRTY_FILE_BASENAME=${DIRTY_BIN_FILE%.bin}
else
# use inherited bin filename
n=`find $binpath -name *.bin`
dn=$(dirname $n)
mv "$n" "${dn}/${expectedOutFileName}"
export DIRTY_BIN_FILE=`basename $( find $binpath -name \*.bin )`
export BIN_FILE="$expectedOutFileName"
export DIRTY_FILE_BASENAME=${expectedOutFileName%.bin}
fi
fi
export FILE_BASENAME=${BIN_FILE%.bin}
find $binpath -name \*.bin -exec mv {} $M5_SD_BUILD_DIR/$BIN_FILE \; #<-- you need that backslash before and space after the semicolon
#if [ "$BIN_FILE" != "$DIRTY_BIN_FILE" ]; then
# mv $M5_SD_BUILD_DIR/$DIRTY_BIN_FILE $M5_SD_BUILD_DIR/$BIN_FILE
# echo "[++++] UpperCasedFirst() $DIRTY_BIN_FILE to $BIN_FILE"
#fi
echo "[INFO] Keeping bin file: $BIN_FILE"
export outfile=$BIN_FILE
}
function injectupdater {
echo "[INFO] Injecting target $inofile"
awk '/#include <M5Stack.h>/{print;print "#include <M5StackUpdater.h>\nSDUpdater sdUpdater;";next}1' $inofile > tmp && mv tmp $inofile;
awk '/M5.begin()/{print;print " if(digitalRead(BUTTON_A_PIN) == 0) { sdUpdater.updateFromFS(SD); ESP.restart(); } ";next}1' $inofile > tmp && mv tmp $inofile;
# the M5StackUpdater requires Wire.begin(), inject it if necessary
egrep -R "Wire.begin()" || (awk '/M5.begin()/{print;print " Wire.begin();";next}1' $inofile > tmp && mv tmp $inofile);
# the display driver changed, get rid of default rotation in setup
sed -i -e 's/M5.Lcd.setRotation(0);/\/\//g' $inofile
# remove any hardcoded credentials so wifi auth can be done from another app (e.g. wifimanager)
sed -i -e 's/WiFi.begin(ssid, password);/WiFi.begin();/g' $inofile
sed -i -e 's/WiFi.begin(SSID, PASSWORD);/WiFi.begin();/g' $inofile
echo "[OK] Injection successful"
}
function newpopulatemeta {
# $SDCardAppNameSpace
echo "***** Populating meta"
IMG_NAME=${SDCardAppNameSpace}_gh.jpg
REPO_URL=`git config remote.origin.url`
REPO_OWNER_URL=`echo ${REPO_URL%/*}`
REPO_USERNAME=$(echo "$REPO_URL" | cut -d "/" -f4)
AVATAR_URL=$REPO_OWNER_URL.png?size=200
JSONFILE="$M5_SD_BUILD_DIR/json/$SDCardAppNameSpace.json"
IMGFILE="$M5_SD_BUILD_DIR/jpg/$SDCardAppNameSpace.jpg"
AVATARFILE="$M5_SD_BUILD_DIR/jpg/${SDCardAppNameSpace}_gh.jpg"
if [ ! -f $JSONFILE ]; then
echo "[WARNING] JSON Meta file $JSONFILE does not exists, will be created from the ether"
mkdir -p $M5_SD_BUILD_DIR/json
REPO_SHORTURL=`git.io $REPO_URL`
if [ "" != "$REPO_SHORTURL" ]; then
echo "{\"width\":120,\"height\":120, \"authorName\":\"@$REPO_USERNAME\", \"projectURL\": \"$REPO_SHORTURL\",\"credits\":\"$REPO_OWNER_URL\"}" > $JSONFILE
else
echo "{\"width\":120,\"height\":120, \"authorName\":\"@$REPO_USERNAME\", \"projectURL\": \"$REPO_URL\",\"credits\":\"$REPO_OWNER_URL\"}" > $JSONFILE
fi
fi
if [ ! -f $AVATARFILE ]; then
mkdir -p $M5_SD_BUILD_DIR/jpg
AVATAR_URL=`sed 's/gist.//g' <<< $AVATAR_URL` # no gist in URL is valid to retrieve the profile pic
echo "[WARNING] No avatar file found, will download from $AVATAR_URL and save it as $AVATARFILE"
#wget --quiet $AVATAR_URL --output-document=temp
wget $AVATAR_URL --output-document=temp
convert temp -resize 120x120 -type TrueColor $AVATARFILE
identify $AVATARFILE
rm temp
fi
if [ ! -f $IMGFILE ]; then
mkdir -p $M5_SD_BUILD_DIR/jpg
# TODO: search in SD-Apps-folder.zip
echo "[TODO] search in SD-Apps-folder.zip"
echo "[WARNING] No img file found, will use a copy of avatar file $AVATARFILE"
cp $AVATARFILE $IMGFILE
fi
echo "***** Populating successful"
}
function populatemeta {
echo "***** Populating meta"
export IMG_NAME=${FILE_BASENAME}_gh.jpg
export REPO_URL=`git config remote.origin.url`
export REPO_OWNER_URL=`echo ${REPO_URL%/*}`
export REPO_USERNAME=$(echo "$REPO_URL" | cut -d "/" -f4)
export AVATAR_URL=$REPO_OWNER_URL.png?size=200
export JSONFILE="$M5_SD_BUILD_DIR/json/$FILE_BASENAME.json"
export IMGFILE="$M5_SD_BUILD_DIR/jpg/$FILE_BASENAME.jpg"
export AVATARFILE="$M5_SD_BUILD_DIR/jpg/${FILE_BASENAME}_gh.jpg"
if [ -f $JSONFILE ]; then
echo "JSON Meta file $JSONFILE exists, should check for contents or leave it be"
else
if [ -f "$M5_SD_BUILD_DIR/json/$DIRTY_FILE_BASENAME.json" ]; then
echo "[++++] UpperCasedFirst() $DIRTY_FILE_BASENAME.json, renaming other meta components"
mv $M5_SD_BUILD_DIR/json/$DIRTY_FILE_BASENAME.json $JSONFILE
mv $M5_SD_BUILD_DIR/jpg/$DIRTY_FILE_BASENAME.jpg $IMGFILE &>/dev/null
mv $M5_SD_BUILD_DIR/jpg/${DIRTY_FILE_BASENAME}_gh.jpg $AVATARFILE &>/dev/null
sed -i -e "s/$DIRTY_FILE_BASENAME/$FILE_BASENAME/g" $JSONFILE &>/dev/null
else
echo "[++++] No $JSONFILE JSON Meta file found, creating from the ether"
mkdir -p $M5_SD_BUILD_DIR/json
mkdir -p $M5_SD_BUILD_DIR/jpg
export REPO_SHORTURL=`git.io $REPO_URL`
if [ "" != "$REPO_SHORTURL" ]; then
echo "{\"width\":120,\"height\":120, \"authorName\":\"@$REPO_USERNAME\", \"projectURL\": \"$REPO_SHORTURL\",\"credits\":\"$REPO_OWNER_URL\"}" > $JSONFILE
else
echo "{\"width\":120,\"height\":120, \"authorName\":\"@$REPO_USERNAME\", \"projectURL\": \"$REPO_URL\",\"credits\":\"$REPO_OWNER_URL\"}" > $JSONFILE
fi
fi
fi
cat $JSONFILE
# no gist in URL is valid to retrieve the profile pic
AVATAR_URL=`sed 's/gist.//g' <<< $AVATAR_URL`
if [ ! -f $AVATARFILE ]; then
echo "**** Will download avatar from $AVATAR_URL and save it as $AVATARFILE"
wget --quiet $AVATAR_URL --output-document=temp
convert temp -resize 120x120 $AVATARFILE
identify $AVATARFILE
rm temp
fi
echo "***** Populating successful"
}
================================================
FILE: .github/tools/cron.sh
================================================
#!/bin/bash
# set -o xtrace
git remote rm origin
git remote add origin https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.git
git config --global user.name $GITHUB_ACTOR
git config --global user.email $GITHUB_ACTOR@users.noreply.github.com
nextAW=`curl -L --silent $WORKFLOW_URL | jq -r .need_workflow[0]`
if [[ "$nextAW" == "" || "$nextAW" == "null" ]]; then
echo "No workflow in queue"
exit 0;
fi
nextURL=`echo "$nextAW" | jq -r .workflow_url`;
if [[ "$nextURL" == "" || "$nextURL" == "null" ]]; then
echo "No workflow_url in first queued item, something wrong on remote? JSON chunk: $nextAW"
exit 0;
fi
workflowFilename="${nextURL##*/}"
cd .github/workflows
if [ -f $workflowFilename ]; then
echo "Deleting $workflowFilename"
git rm --cached $workflowFilename
git commit -m "Auto-removed $workflowFilename from cron workflow"
fi
wget "$nextURL" --output-document=$workflowFilename
touch $workflowFilename
git add $workflowFilename
# TODO: also remove unwanted/obsoleted workflow files
git commit -m "Auto-committed from cron workflow"
git push -u origin master
# now generate a comment if necessary
comments_url=`echo "$nextAW" | jq -r .comments_url`;
if [[ "$comments_url" == "" || "$comments_url" == "null" ]]; then
echo "This workflow has no comments_url"
exit 0;
fi
# and post the comment
commentMessage='The app has been scheduled for rebuild'
# errorless jq call
allComments=`curl -L --silent $comments_url | jq -r ".[]|. as \\$in|try (try .body catch error( {\"error\": \\$in })) catch {}"`
# echo "Captured comments: $allComments"
# avoid posting dupe comment
if [[ $allComments == *"$commentMessage"* ]]; then
echo "Rebuild comment already posted"
else
jsonData='{"body":"The app has been scheduled for rebuild"}'
curlAuth="Authorization: token $GITHUB_TOKEN"
HTTP_RESPONSE=$(curl -X POST -H "$curlAuth" -H "Content-Type: application/json" --data "$jsonData" -L --silent --write-out "HTTPSTATUS:%{http_code}" $comments_url)
HTTP_BODY=$(echo $HTTP_RESPONSE | sed -e 's/HTTPSTATUS\:.*//g')
HTTP_STATUS=$(echo $HTTP_RESPONSE | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
if [[ "$HTTP_STATUS" == "201" ]]; then
echo " ---> GitHub server HTTP response to POST Comment: $HTTP_STATUS"
else
echo " ---> Comment post failed, HTTP_RESPONSE : $HTTP_RESPONSE"
fi
fi
================================================
FILE: .github/tools/deploy.sh
================================================
#!/bin/bash
json_escape () {
printf '%s' "$1" | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))'
#printf '%s' "$1" | php -r 'echo json_encode(file_get_contents("php://stdin"));'
}
set -e
#Cmdline options
# -t: tag (*_RC* determines prerelease version, can be overriden be -p)
# -a: GitHub API access token
# -s: GitHub repository slug (user/repo)
# -p: prerelease true/false
# -f: files to upload (ie assets. delim = ';', must come quoted)
# -d: directory to upload (by adding dir contents to assets)
while getopts ":t:,:a:,:s:,:p:,:f:,:d:,:b:" opt; do
case $opt in
t)
varTagName=$OPTARG
echo "TAG: $varTagName" >&2
;;
a)
varAccessToken=$OPTARG
echo "ACCESS TOKEN: $varAccessToken" >&2
;;
s)
varRepoSlug=$OPTARG
echo "REPO SLUG: $varRepoSlug" >&2
;;
p)
varPrerelease=$OPTARG
echo "PRERELEASE: $varPrerelease" >&2
;;
f)
varAssets=$OPTARG
echo "ASSETS: $varAssets" >&2
;;
d)
varAssetsDir=$OPTARG
echo "ASSETS DIR: $varAssetsDir" >&2
;;
b)
varAssetsPlatform=$OPTARG
echo "ASSETS PLATFORM: $varAssetsPlatform" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
if [ -z $varAssetsPlatform ]; then
varAssetsPlatform=m5stack
fi
# use TravisCI env as default, if available
if [ -z $varTagName ] && [ ! -z $TRAVIS_TAG ]; then
varTagName=$TRAVIS_TAG
fi
if [ -z $varTagName ]; then
# varTagName=`$WORK_SPACE/tools/git-describe.awk https://github.com/tobozo/M5Stack-App-Registry`
varTagName=`$WORK_SPACE/tools/git-describe.awk $WORK_SPACE`
if [ -z $varTagName ]; then
echo "No tag name available => aborting"
exit 1
fi
fi
#Check tag name for release/prerelease (prerelease tag contains '_RC' as for release-candidate. case-insensitive)
shopt -s nocasematch
if [ -z $varPrerelease ]; then
if [[ $varTagName == *-RC* ]]; then
varPrerelease=true
else
varPrerelease=false
fi
fi
shopt -u nocasematch
#
# Prepare Markdown release notes:
#################################
# - annotated tags only, lightweight tags just display message of referred commit
# - tag's description conversion to relnotes:
# first 3 lines (tagname, commiter, blank): ignored
# 4th line: relnotes heading
# remaining lines: each converted to bullet-list item
# empty lines ignored
# if '* ' found as a first char pair, it's converted to '- ' to keep bulleting unified
echo
echo Preparing release notes
echo -----------------------
echo "Tag's message:"
relNotesRaw=`git show -s --format=%b $varTagName`
readarray -t msgArray <<<"$relNotesRaw"
arrLen=${#msgArray[@]}
#process annotated tags only
if [ $arrLen > 3 ] && [ "${msgArray[0]:0:3}" == "tag" ]; then
ind=3
while [ $ind -lt $arrLen ]; do
if [ $ind -eq 3 ]; then
releaseNotes="#### ${msgArray[ind]}"
releaseNotes+=$'\r\n'
else
oneLine="$(echo -e "${msgArray[ind]}" | sed -e 's/^[[:space:]]*//')"
if [ ${#oneLine} -gt 0 ]; then
if [ "${oneLine:0:2}" == "* " ]; then oneLine=$(echo ${oneLine/\*/-}); fi
if [ "${oneLine:0:2}" != "- " ]; then releaseNotes+="- "; fi
releaseNotes+="$oneLine"
releaseNotes+=$'\r\n'
#debug output
echo " ${oneLine}"
fi
fi
let ind=$ind+1
done
fi
echo "$releaseNotes"
# - list of commits (commits.txt must exit in the output dir)
commitFile=$varAssetsDir/commits.txt
if [ -s "$commitFile" ]; then
releaseNotes+=$'\r\n##### Commits\r\n'
echo
echo "Commits:"
IFS=$'\n'
for next in `cat $commitFile`
do
IFS=' ' read -r commitId commitMsg <<< "$next"
commitLine="- [$commitId](https://github.com/$varRepoSlug/commit/$commitId) $commitMsg"
echo " $commitLine"
releaseNotes+="$commitLine"
releaseNotes+=$'\r\n'
done
rm -f $commitFile
fi
# Check possibly existing release for current tag
echo
echo "Processing GitHub release record for $varTagName:"
echo "-------------------------------------------------"
echo " - check $varTagName possible existence..."
# (eg build invoked by Create New Release GHUI button -> GH default release pack created immediately including default assests)
HTTP_RESPONSE=$(curl -L --silent --write-out "HTTPSTATUS:%{http_code}" https://api.github.com/repos/$varRepoSlug/releases/tags/$varTagName?access_token=$varAccessToken)
if [ $? -ne 0 ]; then echo "FAILED: $? => aborting"; exit 1; fi
HTTP_BODY=$(echo $HTTP_RESPONSE | sed -e 's/HTTPSTATUS\:.*//g')
HTTP_STATUS=$(echo $HTTP_RESPONSE | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
echo " ---> GitHub server HTTP response: $HTTP_STATUS"
# if the release exists, append/update recent files to its assets vector
if [ $HTTP_STATUS -eq 200 ]; then
releaseId=$(echo $HTTP_BODY | jq -r '.id')
echo " - $varTagName release found (id $releaseId)"
#Merge release notes and overwrite pre-release flag. all other attributes remain unchanged:
# 1. take existing notes from server (added by release creator)
releaseNotesGH=$(echo $HTTP_BODY | jq -r '.body')
# - strip possibly trailing CR
if [ "${releaseNotesGH: -1}" == $'\r' ]; then
releaseNotesTemp="${releaseNotesGH:0:-1}"
else
releaseNotesTemp="$releaseNotesGH"
fi
# - add CRLF to make relnotes consistent for JSON encoding
releaseNotesTemp+=$'\r\n'
# 2. #append generated relnotes (usually commit oneliners)
releaseNotes="$releaseNotesTemp$releaseNotes"
# 3. JSON-encode whole string for GH API transfer
releaseNotes=$(json_escape "$releaseNotes")
# 4. remove extra quotes returned by python (dummy but whatever)
releaseNotes=${releaseNotes:1:-1}
#Update current GH release record
echo " - updating release notes and pre-release flag:"
curlData="{\"body\": \"$releaseNotes\",\"prerelease\": $varPrerelease}"
echo " <data.begin>$curlData<data.end>"
echo
#echo "DEBUG: curl --data \"$curlData\" https://api.github.com/repos/$varRepoSlug/releases/$releaseId?access_token=$varAccessToken"
curl --data "$curlData" https://api.github.com/repos/$varRepoSlug/releases/$releaseId?access_token=$varAccessToken
if [ $? -ne 0 ]; then echo "FAILED: $? => aborting"; exit 1; fi
echo " - $varTagName release record successfully updated"
#... or create a new release record
else
releaseNotes=$(json_escape "$releaseNotes")
releaseNotes=${releaseNotes:1:-1}
echo " - release $varTagName not found, creating a new record:"
curlData="{\"tag_name\": \"$varTagName\",\"target_commitish\": \"master\",\"name\": \"v$varTagName\",\"body\": \"$releaseNotes\",\"draft\": false,\"prerelease\": $varPrerelease}"
echo " <data.begin>$curlData<data.end>"
#echo "DEBUG: curl --data \"${curlData}\" https://api.github.com/repos/${varRepoSlug}/releases?access_token=$varAccessToken | jq -r '.id'"
releaseId=$(curl --data "$curlData" https://api.github.com/repos/$varRepoSlug/releases?access_token=$varAccessToken | jq -r '.id')
if [ $? -ne 0 ]; then echo "FAILED: $? => aborting"; exit 1; fi
echo " - $varTagName release record successfully created (id $releaseId)"
fi
# Assets defined by dir contents
if [ ! -z $varAssetsDir ]; then
varAssetsTemp=$(ls -p $varAssetsDir | grep -v / | tr '\n' ';')
for item in $(echo $varAssetsTemp | tr ";" "\n")
do
varAssets+=$varAssetsDir/$item;
varAssets+=';'
done
fi
function gen_json_hook {
if [[ "$1" == "" ]]; then
echo "[ERROR] No json path provided"
exit 1
fi
tee $1 << XXX
{
"action":"$json_action",
"app_slug":"$REMOTE_APP_SLUG",
"repo_slug":"$REMOTE_REPO_SLUG",
"base_name":"$filename",
"tag":"$varTagName",
"release_id":"$releaseId",
"remote_app_url":"$REMOTE_APP_URL",
"app_path":"$APP_PATH",
"platform":"$varAssetsPlatform",
"data":""
}
XXX
}
#Upload additional assets
if [ ! -z $varAssets ]; then
echo
echo "Uploading assets:"
echo "-----------------"
echo " Files to upload:"
echo " $varAssets"
echo
curlAuth="Authorization: token $varAccessToken"
for filename in $(echo $varAssets | tr ";" "\n")
do
echo " - ${filename}:"
base_name=$(basename $filename)
json_action="binary-added"
if [[ `echo $base_name | grep M5Burner` != '' ]]; then
json_action="firmware-added"
fi
if [[ `echo $base_name | grep OdroidFW` != '' ]]; then
json_action="firmware-added"
fi
gen_json_hook /tmp/payload.json
jsonCode=`cat /tmp/payload.json`
rm /tmp/payload.json
php $WORK_SPACE/tools/hooker.php "${GITHUB_HOOK_URL}" "action-deploy" "$jsonCode" "${GITHUB_HOOK_SECRET}"
if [ $? -eq 0 ]; then
echo "yay";
# TODO: confirm build success
else
echo "nay";
fi
done
fi
================================================
FILE: .github/tools/git-describe.awk
================================================
#!/usr/bin/awk -f
BEGIN {
if (ARGC != 2) {
print "git-describe-remote.awk $GITHUB_REPOSITORY"
exit
}
FS = "[ /^]+"
while ("git ls-remote " ARGV[1] "| sort -Vk2" | getline) {
if (!sha)
sha = substr($0, 1, 7)
tag = $3
}
while ("curl -s " ARGV[1] "/releases/tag/" tag | getline)
if ($3 ~ "commits")
com = $2
printf com ? "%s-%s-g%s\n" : "%s\n", tag, com, sha
}
================================================
FILE: .github/tools/hooker.php
================================================
<?php
/*
Bash usage example:
php hooker.php [Hook URL] [Event Name] [JSON Code] [Hook Secret]
php hooker.php "${hookURL}" "${eventName}" "${jsonCode}" "${hookSecret}";
if [ $? -eq 0 ]; then
echo "yay";
else
echo "nay";
fi
*/
function usage( $msg ) {
if( trim( $msg ) !='' ) {
echo "\n$msg\n";
}
echo "\n[Hooker Usage]\n";
//echo ' php hooker.php "${hookURL}" "${eventName}" "${jsonCode}" "${hookSecret}"'."\n";
echo " php hooker.php [Hook URL] [Event Name] [JSON Code] [Hook Secret]\n";
echo " php ".basename(__FILE__).' "https://my.site/hook" "my-custom-event" \'{"action":"custom","data":["blah","bleb","foo","bar"]}\''." \"My Secret pass\"\n\n";
}
if( basename( $argv[0] ) !== basename( __FILE__ ) ) {
usage( "[ERROR] API Mode only !!");
exit(1);
}
if( !isset($argv[1]) || !isset($argv[2]) || !isset($argv[3]) || !isset($argv[4]) ) {
usage( "[ERROR] Missing arg");
exit(1);
}
$hookURL = trim( $argv[1] );
$eventName = trim( $argv[2] );
$payload = trim( $argv[3] );
$hookSecret = trim( $argv[4] );
if( empty( $payload ) || empty ( $eventName ) || empty( $hookSecret ) || empty( $hookURL ) ) {
usage( "[ERROR] Empty arg" );
exit(1);
}
$json = json_decode( $payload, true );
if(! $json ) {
usage("\n[ERROR] Payload has invalid json: $payload\n\n");
exit(1);
}
if( !isset( $json['action'] ) ) {
usage("\n[ERROR] Payload has no action: $payload\n\n");
exit(1);
}
if( trim( $json['action'] ) == '' ) {
usage("\n[ERROR] Payload has empty action: $payload\n\n");
exit(1);
}
// turn full path + empty data into basename + b64 encoded data
if( isset( $json['base_name'] ) && file_exists( $json['base_name'] ) ) {
$file_path = $json['base_name'];
$json['data'] = 'fakedata';
$json['base_name'] = basename( $json['base_name'] );
echo "file $json[base_name] is reachable :-)\n";
} else {
echo "file $json[base_name] is not reachable :-(\n";
}
// re-encode
$payload = json_encode( $json );
$algo = 'sha256'; // go strong :-)
$rawPost = 'payload='.$payload; // simulate rawPost for GH hook compliance
$rawPostSize = strlen( $payload );
$hashed = hash_hmac($algo, $rawPost, $hookSecret);
$headers = sprintf( '-H "Content-Type: multipart/form-data" -H "User-Agent: PHP-M5-Registry-UA" -H "X-Github-Event: %s" -H "X-Hub-Signature: %s=%s"',
$eventName,
$algo,
$hashed
);
$cmd = "curl -X POST $headers -F 'payload=$payload' -F binary=@\"$file_path\" $hookURL";
echo "[Hooker CMD]\n\n\n\n\n".$cmd."\n\n\n\n\n";
exec( $cmd, $out );
echo "[Hook RESPONSE]\n\n\n\n\n".implode("\n", $out)."\n\n\n\n\n"; exit;
$context = stream_context_create( $opts );
$response = file_get_contents( $hookURL, false, $context );
if( ! $response ) {
usage( "[ERROR] Hooker cannot post comment (size: $rawPostSize): bad gh api response\n\n" );
exit(1);
}
if( !preg_match("/200/", $http_response_header[0]) ) {
usage( "[ERROR] Hooker got bad response code from post (size: $rawPostSize): $response / ".print_r($http_response_header, 1) );
exit(1);
} else {
echo json_encode([ 'headers' => $http_response_header, 'response_body' => $response ]);
exit(0);
}
================================================
FILE: .github/tools/ino2fw.sh
================================================
#!/bin/bash
# Credits: https://github.com/lstux/OdroidGO/
VERBOSE=0
ARDUINO_BOARD="esp32:esp32:odroid_esp32"
SKETCHBOOK_PATH=""
usage() {
exec >&2
[ -n "${1}" ] && printf "Error : ${1}\n"
printf "Usage : $(basename "${0}") [options] {sketch_dir|sketch_file.ino|sketch_file.bin}\n"
printf " Create a .fw file for Odroid-Go from arduino sketch\n"
printf "options:\n"
printf " -b build_path : build in build_path [/tmp/ino2fw]\n"
printf " -t tile.png : use specified image as tile (resized to 86x48px)\n"
printf " -l label : set application label\n"
printf " -d description : set application description\n"
printf " -v : increase verbosity level\n"
printf " -h : display this help message\n"
exit 1
}
prn() { printf "$(date '+%Y/%m/%d %H:%M:%S') [$1] "; printf "$2\n"; }
msg() { prn MSG "$1"; }
log() { [ ${VERBOSE} -ge 1 ] || return 0; prn LOG "$1" >&2; }
dbg() { [ ${VERBOSE} -ge 2 ] || return 0; prn DBG "$1" >&2; }
err() { prn ERR "$1" >&2; [ -n "${2}" ] && exit ${2}; }
checkbin() {
local bin="${1}" package="${2}"
which "${1}" >/dev/null 2>&1 && return 0
err "can't find '${1}' binary in PATH, make sure that '${2}' package is installed" 2
}
FW_TILE=""
FW_LABEL=""
FW_DESC=""
BUILD_PATH="/tmp/ino2fw"
while getopts b:t:l:d:vh opt; do case "${opt}" in
b) BUILD_PATH="${OPTARG}";;
t) [ -e "${OPTARG}" ] || usage "[ERROR] FW_TILE unreachable: ${OPT_ARG}"; FW_TILE="${OPTARG}";;
l) FW_LABEL="${OPTARG}";;
d) FW_DESC="${OPTARG}";;
v) VERBOSE=$(expr ${VERBOSE} + 1);;
*) usage "[WARNING] strange opt arg: ${OPT_ARG}";;
esac; done
shift $(expr ${OPTIND} - 1)
if [ -d "${1}" ]; then
SKETCH_DIR="$(readlink -m "${1}")"
SKETCH_FILE="${SKETCH_DIR}/$(basename "${SKETCH_DIR}").ino"
else
if [ -e "${1}" ]; then
SKETCH_FILE="$(readlink -m "${1}")"
SKETCH_DIR="$(dirname "${1}")"
else
usage "[ERROR] arg 1 ${1} does not exist"
fi
fi
[ -e "${SKETCH_FILE}" ] || usage "[ERROR] No '$(basename "${SKETCH_FILE}")' file found in '${SKETCH_DIR}'"
# TODO : what if no .arduino15/preferences.txt is available?
if ! [ -n "${SKETCHBOOK_PATH}" ]; then
if [ -e "${HOME}/.arduino15/preferences.txt" ]; then
SKETCHBOOK_PATH="$(sed -n 's/^sketchbook.path=//p' "${HOME}/.arduino15/preferences.txt")"
log "set sketchbook.path to '${SKETCHBOOK_PATH}'"
fi
fi
# Create ${BUILD_PATH} directory (will be used as build.path for arduino)
if ! [ -d "${BUILD_PATH}" ]; then
dbg "creating '${BUILD_PATH}' directory"
install -d "${BUILD_PATH}"
fi
if [[ $SKETCH_FILE == *".bin" ]]; then
cp ${SKETCH_FILE} ${BUILD_PATH}/
SKETCH_BIN="$(basename "${SKETCH_FILE}")"
fi
# Try to find a tile if none was specified on cmdline
if ! [ -e "${FW_TILE}" ]; then
FW_TILE="${SKETCH_DIR}/$(basename "${SKETCH_DIR}").png"
if [ -e "${FW_TILE}" ]; then
log "using '${FW_TILE}' as tile"
else
FW_TILE="${SKETCH_DIR}/tile.png"
if [ -e "${FW_TILE}" ]; then
log "using '${FW_TILE}' as tile"
else
log "no tile specified, using default"
checkbin base64 coreutils
FW_TILE="${BUILD_PATH}/tile.png"
sed -e '0,/^### EMBEDDED_BASE64_START tile.png/d' -e '/^### EMBEDDED_BASE64_END tile.png/,$d' "${0}" | base64 --decode > "${FW_TILE}"
fi
fi
fi
# Set application label if none was specified on cmdline
if ! [ -n "${FW_LABEL}" ]; then
FW_LABEL="$(sed -n 's/^[/\* ]*Label *: *//p' "${SKETCH_FILE}")"
if ! [ -n "${FW_LABEL}" ]; then
FW_LABEL="$(basename "${SKETCH_DIR}")"
read -p "Enter application label [${FW_LABEL}] : " label
[ -n "${label}" ] && FW_LABEL="${label}"
else
log "using label found in .ino file : ${FW_LABEL}"
fi
fi
# Set application description if none was specified on cmdline
if ! [ -n "${FW_DESC}" ]; then
FW_DESC="$(sed -n 's/^[/\* ]*Description *: *//p' "${SKETCH_FILE}")"
if ! [ -n "${FW_DESC}" ]; then
FW_DESC="$(basename "${SKETCH_DIR}") for Odroid-GO"
read -p "Enter application description [${FW_DESC}] : " desc
[ -n "${desc}" ] && FW_DESC="${desc}"
else
log "using description found in .ino file : ${FW_DESC}"
fi
fi
if [[ "$SKETCH_BIN" == "" ]]; then
echo "Compiling ino sketch"
checkbin arduino arduino
log "building sketch with arduino"
dbg "arduino --pref build.path=${BUILD_PATH} --pref sketchbook.path='${SKETCHBOOK_PATH}' --verify '${SKETCH_FILE}'"
arduino --pref build.path=${BUILD_PATH} --pref sketchbook.path="${SKETCHBOOK_PATH}" --verify "${SKETCH_FILE}" || exit 4
# Produce fw file using mkfw
SKETCH_BIN="$(basename "${SKETCH_DIR}").ino.bin"
else
echo "Skipping build as a bin name has been provided";
fi
# Create "raw" tile using ffmpeg
checkbin ffmpeg ffmpeg
log "creating tile.raw from ${FW_TILE}"
dbg "ffmpeg -i ${FW_TILE} -vf scale=86:48 -f rawvideo -pix_fmt rgb565 '${BUILD_PATH}/tile.raw'"
ffmpeg -i ${FW_TILE} -vf scale=86:48 -f rawvideo -pix_fmt rgb565 "${BUILD_PATH}/tile.raw" || exit 8
BIN_SIZE="$(du -sb "${BUILD_PATH}/${SKETCH_BIN}" | awk '{print $1}')"
log "building fw file"
dbg "mkfw '${FW_DESC}' tile.raw 0 16 ${BIN_SIZE} '${FW_LABEL}' '${SKETCH_BIN}'"
cd "${BUILD_PATH}" && mkfw "${FW_DESC}" tile.raw 0 16 ${BIN_SIZE} "${FW_LABEL}" "${SKETCH_BIN}" || exit 16
log "copying fw file to '${SKETCH_DIR}'"
cp firmware.fw "${SKETCH_DIR}/${FW_LABEL}.fw" || exit 32
ls -la
exit 0
### EMBEDDED_BASE64_START tile.png
iVBORw0KGgoAAAANSUhEUgAAAFYAAAAwCAYAAACL+42wAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAwRSURBVHhe7Zt5dBVFFsa/J7IlBAIkIYEgBILIJiCCih4VBCQIKIoCAgLuC+466Og4o+PMiAujsiiMIqhRUA6LoGxRYBSiAoIjKLIFw5awKdkTIJnvVtc7vqW635L3jv/kd06fJNX9qm99feveW/U6riqCGiLOWfpnDRGmRtgoUSNslKgRNkrUCBslIlIVlJ85jWX79uDbI4ex++SvOF5eikp2G1+nHlo3bITuCUnon5qG1AZx+hN/DPklxVi5PwffHc1DTuFJ/FZeBpfLhab16iO9YWP0SkrB4NbpqFurlv5E+FRL2L9++yVe+X4jik8cBWrXof9zAshBYxXStRyVlcDpU0CduhjZsRuevOBinN80ybomyvxCAZ/btB6zt20BSkuAs88GRDix0dNOsVGOUxVo0DQRj3TthWd7XmadD4OwhB2dtRQfbM4G6talobUtQ4NBCXyaLl6Ghk0S8N5VgzE0rZ0+GVmy8w5i5OpPkHtoP1CvnmWnPPRgOHPGcoTycozucQne7zdEnwiekISdt+snjFqSaXmnHO4nHg5ifFkJGjVOwObh49G2Ubw+UT0KKyrQY8Ec7DqwD4iJpZjaO8NBpGF/OF2BD68bjZHpHfSJwAQt7KBPP8by7ZxODRpWT1BfROCiAozltHuXHlwdnmFo+vsXnwGxFFQ8NFKIRLQxo1N3fHbNjbrRmaCEbfP+m8hhwAeDfFQQExgeatWPQeHtD6N+GKKkvjsdB4/mA+wjog/eE86wtMQU7B1zt26wJ6Cw7T6Yhd3M9qjLOOWEdCPxs5IeWMVYKr26GNNqycGEEUx8Y3UBZu71t9yH3sktdKMzB4oK0fI/r1j3qB3EA5E4L/dxJyt5BmKnhAxJbIEeCh2gHauHnTffqRvMOAo7isF/3g+bLS+wQ6YybyYJLKNdR1yWkopWDBdn0cA8irSJnr5gz8+oYBmmHo4M3sl4MYfXzr1+LG5p31k3mtnABHXp3GlWeHJ6cOqhSzKinbFxuImxslezFDSPaYAzPJdbWICv8g5g+a4frfGInU4JmdXFqC498EH/obrBH1th1x7MRZ/33wDiGpmFkI9xasTGxeOjAddiUKu2+oQZEXnS12vx7qYNgQWWvgt+w5SMG/Bw15660ZtPf9mDwZxNzH7OD0oE5b2HdLkQUy7ti3S53gHpd8SqJSgu/I2hzyasiH0s49aMvQdXNj9HN3pjK6zrtefsSxTd8fMsQ57q0Vs3Bs+QzxZgmcwEJ0/T4k4bfBPu63yBbrRYkbsXGZkznUWVzxcXohsL/uxhY1FPpnkIPL95A/7CstLWsVTpeApVDz6jG7wxjuq5jeutDzqI+s34B8ISVVg6aDg23/ogi3F6kxwmZDAN4zFx2UdYkrNLN0KtmjIyA3iqitVFWHjDOGy5cULIogpPc2zfjL9fjVWN2RfRhvlEFh8mjB7r+vffrOJfgronWtTPR9+NvqmtdGP1OOe9Gdh/7Ih9xSH3pOfuu+9JJNWPRcwUeohTyVdRjtrsq/zOx3iJzTUh8Dnr4X4yO0yeK87H+1U9TL188HPJRXt36mxp8FZ2Mo71ZqREFXLH3osBUnjTw4zIYDioNJZ8SXNeZ/JpYC9qWSnaJSaj4q7HHUVdum83sijYcoaUvXxoTlyV2lqNWcbuh/LaSizKoWY++Kk3Y/t31qrKF/EcllFz+l6jGyLHyiEjMILJxVZcDqCK9y7iElOVRSYoas+WaQHLIGHo3OnoP382BtET39y+Vbfao8YszmYKCdRqhuxD+OAnbBZLI1XP+cKl3WMXXan/iDzzWFkM7djN2igxoWpNwywSWEZ1bdEK3w4fpxsCEMNsL8tdlpGxwdS+5NGLOXZZ3vpCrbL27NB//I6XpQXi7vJh0zQ6VYFHbEqfSLGE5VV3ep1sfgQN7WreOAFbb5qgG0KjQZDCqrHzXn6IVtRMaeeBV/Jae4i164dvWU/TE7lEgvRDf9UN/jSd/RqaMbk4UcZs3Z8xa+aVA3WLmfqzXkaZGCqrKSekmCdVE/+sftpxz7qVWLE/h0vls+lJLmw/xuW59M3pncCxJvM4U1mF/NJiHJ/AasUmPLtefVZtffo5HuvkNaNu96ppvTz2IJeHRm+lAa0Tmuk/zJzIP4SfjuU7HrLfsDtAshBKmdGV15pimhs5x7BRINcGYI9UFbz3T0fzfxdVYGg5RlG2HeE52ndClu42ogqtE5KsWOsLNVPaeeAlbLGsUkzCchCNA+0VRJg5sotkysRueG7GoBsQV8eQaKOE0sD0sKmZ0s4DL2HryZM0fhAoMsUXD5o0a44O9GqnI42lUDqL/mAYz2WlsTpxw3P3fvGp/sOZtryn3LtDYjN04k+1gBB0KOicxHO0r0lSCsdvnTJRJIsZk0dTM6WdB14xdhXj0NUfveMfY1WpUclY9pRuiC4pc6ch7yRDRqDEwoGmNIrHoXETdUNwuF7iOGSRwWri5T4ZeLRrL33GGdfUf6jw4VedMJysZPIcIIlX43VFl6aJ5hgiHZWUoFS2BaPMgKXzkXfiWGBRBV5zmNdevWy+bggd5YVBoMYupaCp5KNm54t2HnhdlRKjVzWmcMBYNn0bFw9R5I61y7H6522hbajz2lU7tqnPBg2dRLxMhCoOUtjp2zYrDfwQrahZsmjngd9eQc8Fc7Hp8H7/rza0J1dxzR4NHvhyNaZ+vdaaoibcZpqSq1BUgPsv6YPXL+unG+yRJa2UXqc4pvbxTdAmiLjvmv4v6xdfj2XSujClJTb6LE78/PqOjl3NO07SIWPS5C1f64bIMSZrKUVd5yyqPFg5TLNJ4GenZq9VfQViSOt09GM9nXFOm6BEnfwdxyyb5KYwQK2UZj74XXmnLCsla5oGwGn3xKpP1IsPkaI7k2Xm9xspjM3LHGIH69CtI2/HFh7yu724caqvCz6eoxuqj4z1idWfmMOT2EGtlGY+GB4BcOuFsptjs3yj8ckzX6p2IssrKYJrxgvYKmHHtwpxI4YXnsSsoaPQlcV5Nx4zh4603yMV2NcWriCl7+o6gIxRxqoeuikEUSOllQGjsG+zBMEpm5WPTAcG8Rgu79YfPqAbQ+PBr7KQIqWLTG1ZIpqQezNuPt1nkNdUE+94WuzjOVtxpU/2nTz1eTzEe4XDVxybjFElLFMIkHtTI6WVAduvZqQCmLhiofryzYh8rLAAfTp0weKB16OhnUAeSHx+gut2hWnN7Ub3PemKAXhBdpUMTGI8ffG/q4A4h01v6UeWxjw9+YqB+FP3i/QJe2Qz5brlC7Fmxw/OfRcXYjrHfa/P10ZubIUVOs17Cz/mH3L2KlXfFaMNk8H49p1xqce3tIeKi9SLch/v2YHs3Tus2lT6MnmAG+mTcfTFgcPweDdnIV7c8g0mrVykvsKxFUCQmSHLYyaa3ukdMLxte/UCXPNY/S0tvV88dO7PP2DvgV8A2UySrVO7PtlXR640t0vMt8FRWMH1xmTrm06nlyikC3mfQESWHSd3l2KY+r6ehyz5nAYvSB/01MUjbsO1Qb7TtThnJ4bNn215l90muBuxSxKz2OlZYZzltpM2yk8nO7UWVfdM0g1mAgoruKb90xq009q9OogJshfBAR287SF6kk34seEgp2Xq269a/YiNgR5guIiNtWqxlnfephSCElZIfOd1HPv1uFV2RNJw8ZyiQlzOMLLuupt1Y3hcvjgTX+7cbuUFp3ATKiJRWSkSGjfF0QkP6EZnghZWuIuJZ1b2GstwmTbVQQSVopse9vn1Y9C3RWS+oFTfqi7KtLxLtvmqK7CEDc6Iu7iqe/OKq3VjYEISVpB3pXovfA/781hqyZsiTkHeF7mVxGBJJJxSwWbqcFCJbd0K636SMOWVoVDsFEHLStCSy9XsYWPQIsS30UMW1s3/jh/Boxu+QJaUJRLwxXBlPD3EPQDpWl6QE++UAdKLzuMyUl6GGH1uJ+uaKJPJ0CBvtezI3WvFX7FRvNhkp9goB/NJv/O6YErvq6wdvzAIW1hPZPqtyM3x+h8E6TaeUzEtrhF6JCartfmwNufqT/wxyDsTq2mr+38QfmUokrIwgXmjrfwPQrMUZLRsE5H3JiIibA3+RDB11uBJjbBRokbYKFEjbFQA/g9OsDPAO7z7oAAAAABJRU5ErkJggg==
### EMBEDDED_BASE64_END tile.png
================================================
FILE: .github/tools/mkfw-build.sh
================================================
#!/bin/sh
# Credits: https://github.com/lstux/OdroidGO/
BUILDDIR="/tmp/odroid-go-mkfw"
if ! [ -d "${BUILDDIR}" ]; then
install -d "${BUILDDIR}" || exit 1
fi
cd "${BUILDDIR}/" || exit 2
if ! [ -d "${BUILDDIR}/odroid-go-firmware" ]; then
cd "${BUILDDIR}/" && git clone https://github.com/othercrashoverride/odroid-go-firmware.git -b factory || exit 3
fi
make -C "${BUILDDIR}/odroid-go-firmware/tools/mkfw" || exit 4
if [ -n "$(which sudo)" ]; then
sudo install -v -oroot -groot -m755 "${BUILDDIR}/odroid-go-firmware/tools/mkfw/mkfw" "/usr/bin/mkfw" || exit 5
else
su -c "install -v -oroot -groot -m755 '${BUILDDIR}/odroid-go-firmware/tools/mkfw/mkfw' '/usr/bin/mkfw'" || exit 5
fi
rm -rf "${BUILDDIR}"
================================================
FILE: .github/workflows/ArduinoBuild.yml
================================================
name: ArduinoBuild
on:
push:
paths:
- '**.ino'
- '**.cpp'
- '**.hpp'
- '**.h'
- '**.c'
- '**ArduinoBuild.yml'
pull_request:
release:
types: [published]
jobs:
matrix_build:
name: ${{ matrix.matrix-context }}@${{ matrix.sdk-version }}
runs-on: ubuntu-latest
strategy:
matrix:
sdk-version:
#- 1.0.6
#- 2.0.0
#- 2.0.1
# - 2.0.2 # has broken SD Support
#- 2.0.3
#- 2.0.4
#- 2.0.5
#- 2.0.6
# - 2.0.7
# - 2.0.8
# - 2.0.9
- 2.0.11
- 2.0.12
- 2.0.13
matrix-context:
- M5Core2-test
- M5Stack-test
- M5StickC-test
- LGFX-test
- M5Unified-test
- S3Box-Test
#- TTGO-LoRa32-V2-test
- M5Stack
- M5Core2
- M5Fire
- OdroidGo
- SdFat-test
#exclude:
#- sdk-version: 2.0.2 # has broken SD Support
# There's no esp32s3box support before 2.0.3
#- { matrix-context: S3Box-Test, sdk-version: 1.0.6 }
#- { matrix-context: S3Box-Test, sdk-version: 2.0.0 }
#- { matrix-context: S3Box-Test, sdk-version: 2.0.1 }
#- { matrix-context: S3Box-Test sdk-version: 2.0.2 }
#- { matrix-context: S3Box-Test, sdk-version: 2.0.4 } # will be fixed in 2.0.5 https://github.com/espressif/arduino-esp32/pull/6962/files
#- { matrix-context: M5Core2-test, sdk-version: 1.0.6 } # M5Core2.h suddenly ceased to support espressif package 1.0.6/2.0.0 (I2S and I2C broken)
include:
# buildable sdk versions
#- sdk-version: 1.0.6
#- sdk-version: 2.0.0
#- sdk-version: 2.0.1
#- sdk-version: 2.0.5
#- sdk-version: 2.0.6
# - sdk-version: 2.0.7
# - sdk-version: 2.0.8
# - sdk-version: 2.0.9
- sdk-version: 2.0.11
- sdk-version: 2.0.12
- sdk-version: 2.0.13
# library health test sketches
- { matrix-context: M5Core2-test, arduino-board: m5stack-core2, sketch-names: M5Core2-SDLoader-Snippet.ino, required-libraries: "M5Core2", ... }
- { matrix-context: M5Stack-test, arduino-board: m5stack-core-esp32, sketch-names: M5Stack-SDLoader-Snippet.ino, required-libraries: "ESP32-Chimera-Core,LovyanGFX", ... }
- { matrix-context: S3Box-Test, arduino-board: esp32s3box, sketch-names: M5Stack-SDLoader-Snippet.ino, required-libraries: "ESP32-Chimera-Core,LovyanGFX", ... }
- { matrix-context: M5Unified-test, arduino-board: m5stack-core2, sketch-names: M5Unified.ino, required-libraries: "M5GFX,M5Unified", ... }
- { matrix-context: M5StickC-test, arduino-board: m5stick-c, sketch-names: M5StickC-SPIFFS-Loader-Snippet.ino, required-libraries: "M5StickC", ... }
- { matrix-context: LGFX-test, arduino-board: m5stack-core-esp32, sketch-names: LGFX-SDLoader-Snippet.ino, required-libraries: "LovyanGFX", ... }
- { matrix-context: SdFat-test, arduino-board: m5stack-core2, sketch-names: SdFatUpdater.ino, required-libraries: "SdFat,M5GFX,M5Unified", ... }
# Launcher and Appstore
- matrix-context: M5Stack
arduino-board: m5stack-core-esp32
sketch-names: M5Stack-SD-Menu.ino,AppStore.ino
launcher-name: M5stack-Launcher
appstore-name: M5stack-AppStore
required-libraries: "ESP32-Chimera-Core,LovyanGFX,ArduinoJson"
- matrix-context: M5Core2
arduino-board: m5stack-core2
sketch-names: M5Stack-SD-Menu.ino,AppStore.ino
launcher-name: M5Core2-Launcher
appstore-name: M5Core2-AppStore
required-libraries: "ESP32-Chimera-Core,LovyanGFX,ArduinoJson"
- matrix-context: M5Fire
arduino-board: m5stack-fire
sketch-names: M5Stack-SD-Menu.ino,AppStore.ino
launcher-name: M5Fire-Launcher
appstore-name: M5Fire-AppStore
required-libraries: "ESP32-Chimera-Core,LovyanGFX,ArduinoJson"
- matrix-context: OdroidGo
arduino-board: odroid_esp32
sketch-names: M5Stack-SD-Menu.ino,AppStore.ino
launcher-name: OdroidGo-Launcher
appstore-name: OdroidGo-AppStore
extra-fqbn: ":PartitionScheme=min_spiffs"
required-libraries: "ESP32-Chimera-Core,LovyanGFX,ArduinoJson,Button2"
#- matrix-context: TTGO-LoRa32-V2-test
#arduino-board: ttgo-lora32-v2
#sketch-names: TTGO-test.ino
#required-libraries: "ESP32-Chimera-Core,LovyanGFX,ArduinoJson"
- matrix-context: M5Atom
sdk-version: 2.0.13
arduino-board: m5stack-atom
sketch-names: M5Stack-SD-Menu.ino
launcher-name: M5Atom-Launcher
required-libraries: "ESP32-Chimera-Core,LovyanGFX,ArduinoJson,Button2"
- matrix-context: M5CoreS3
sdk-version: 2.0.13
arduino-board: m5stack-cores3
sketch-names: M5Stack-SD-Menu.ino,M5Stack-FW-Menu.ino
launcher-name: M5CoreS3-Launcher
factory-name: M5CoreS3-FW-Launcher
required-libraries: "ESP32-Chimera-Core,LovyanGFX,ArduinoJson,Button2"
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: ${{ matrix.matrix-context }}
uses: ArminJo/arduino-test-compile@v3.2.0
with:
platform-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json
#platform-url: ${{ matrix.platform-url }}
arduino-board-fqbn: esp32:esp32:${{ matrix.arduino-board}}${{ matrix.extra-fqbn }}
arduino-platform: esp32:esp32@${{ matrix.sdk-version }}
required-libraries: ESP32-targz,${{ matrix.required-libraries }}
extra-arduino-lib-install-args: --no-deps
# extra-arduino-cli-args: ${{ matrix.extra-arduino-cli-args }}
extra-arduino-cli-args: "--warnings default " # see https://github.com/ArminJo/arduino-test-compile/issues/28
sketch-names: ${{ matrix.sketch-names }}
set-build-path: true
build-properties: ${{ toJson(matrix.build-properties) }}
#debug-install: true
- name: Copy compiled binaries
if: startsWith(matrix.sketch-names, 'M5Stack-SD-Menu')
run: |
cp examples/M5Stack-SD-Menu/build/M5Stack-SD-Menu.ino.bin examples/M5Stack-SD-Menu/build/${{ matrix.launcher-name }}-${{ matrix.sdk-version }}.bin
FILE=examples/AppStore/build/AppStore.ino.bin && [ -f "$FILE" ] && cp $FILE examples/M5Stack-SD-Menu/build/${{ matrix.appstore-name }}-${{ matrix.sdk-version }}.bin || true
FILE=examples/M5Stack-FW-Menu/build/M5Stack-FW-Menu.ino.bin && [ -f "$FILE" ] && cp $FILE examples/M5Stack-SD-Menu/build/${{ matrix.factory-name }}-${{ matrix.sdk-version }}.bin || true
- name: Upload artifact ${{ matrix.matrix-context }}
uses: actions/upload-artifact@v3
if: startsWith(matrix.sketch-names, 'M5Stack-SD-Menu')
with:
name: ${{ matrix.matrix-context }}
path: |
examples/M5Stack-SD-Menu/build/${{ matrix.launcher-name }}-${{ matrix.sdk-version }}.bin
examples/M5Stack-SD-Menu/build/${{ matrix.appstore-name }}-${{ matrix.sdk-version }}.bin
examples/M5Stack-SD-Menu/build/${{ matrix.factory-name }}-${{ matrix.sdk-version }}.bin
post_build:
name: Gather Artefacts
runs-on: ubuntu-latest
# wait until matrix jobs are all finished
needs: matrix_build
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Create artifacts dir
#if: startsWith(github.ref, 'refs/tags/')
run: mkdir -p /home/runner/builds
- name: Download artifacts
uses: actions/download-artifact@v4.1.7
#if: startsWith(github.ref, 'refs/tags/')
with:
path: builds
- name: Release check
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
/home/runner/work/M5Stack-SD-Updater/M5Stack-SD-Updater/builds/**
================================================
FILE: .github/workflows/PlatformioBuild.yml
================================================
name: PlatformIOBuild
env:
PROJECT_DIR: examples/Test/build_test
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
on:
push:
paths:
- '**.ino'
- '**.ini'
- '**.cpp'
- '**.hpp'
- '**.h'
- '**.c'
- '**PlatformioBuild.yml'
pull_request:
workflow_dispatch:
jobs:
set_matrix:
name: Version planner ⊹
runs-on: ubuntu-latest
env:
max-versions: 3 # maximum core versions to test, starting at latest
outputs:
matrix: ${{steps.set-matrix.outputs.matrix}}
project_dir: ${{steps.set-matrix.outputs.project_dir}}
branch_name: ${{steps.set-matrix.outputs.branch_name}}
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup matrix
id: set-matrix
run: |
json=`curl -s "https://espressif.github.io/arduino-esp32/package_esp32_index.json"` # get a list of arduino-esp32 packages
core_versions_arr=(`echo $json | jq -r .packages[0].platforms[].version | awk "NR <= ${{ env.max-versions}}"`) # extract platform versions
core_versions_json=`printf '%s\n' "${core_versions_arr[@]}" | jq -R . | jq -s .` # convert to json
pio_envs_arr=(`cat ${{env.PROJECT_DIR}}/platformio.ini | awk 'match($0, /^\[env:([a-zA-Z0-9\-_])+/){print substr($0, RSTART+5, RLENGTH-5)}'`) # get pio [env:*] names
pio_envs_json=`printf '%s\n' "${pio_envs_arr[@]}" | jq -R . | jq -s .` # convert to json array
matrix=`printf '{"pio-env":%s,"platform-version":%s}' "$pio_envs_json" "$core_versions_json"` # create the matrix array
matrix="${matrix//'%'/'%25'}" # escape percent entities
matrix="${matrix//$'\n'/''}" # remove lf
matrix="${matrix//$'\r'/''}" # remove cr
echo "matrix=${matrix}" >> $GITHUB_OUTPUT
echo "project_dir=${{env.PROJECT_DIR}}" >> $GITHUB_OUTPUT
echo "branch_name=${{env.BRANCH_NAME}}" >> $GITHUB_OUTPUT
build:
name: ${{ matrix.pio-env }}@${{ matrix.platform-version }}
needs: set_matrix
runs-on: ubuntu-latest
strategy:
matrix: ${{fromJSON(needs.set_matrix.outputs.matrix)}}
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Cache pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v3
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
pio update
pio upgrade
- name: Run PlatformIO
run: |
cd ${{ needs.set_matrix.outputs.project_dir }}
export pio_ver=${{ matrix.platform-version }}
# append "test" profile to the current platformio.ini
echo "[env:test]">>platformio.ini
echo "extends = env:${{ matrix.pio-env }}">>platformio.ini
echo "platform = https://github.com/tasmota/platform-espressif32">>platformio.ini
echo "platform_packages = framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/${pio_ver}/esp32-${pio_ver}.zip">>platformio.ini
echo "">>platformio.ini
# keep cache and dev_lib_deps.ini unless running from the master branch
[[ "${{ needs.set_matrix.outputs.branch_name }}" == "master" ]] && rm dev_lib_deps.ini || echo "Develop!" && pio system prune -f
# install local version of the library
pio pkg install -e test --no-save --library file://$(realpath ../../../)
pio run -e test
================================================
FILE: .github/workflows/cron.yml
================================================
name: "Cron ➕ workflows"
on:
schedule:
- cron: '*/10 * * * *'
workflow_dispatch:
inputs:
dispatch-fqwn:
description: 'Dispatch workflow (fully qualified workflow name)'
required: false
default: ''
jobs:
check-lambda-app:
runs-on: ubuntu-latest
steps:
- name: "Next app checker"
if: github.event.inputs.dispatch-fqwn == ''
run: |
jsonW=`curl -L --silent https://phpsecu.re/api/registry/workflow`
nextAW=`echo $jsonW | grep '^{' | jq -r .need_workflow[0]`
if [[ "$nextAW" == "" || "$nextAW" == "null" ]]; then echo "[INFO] No workflow in queue"; exit 0; fi
fqwn=`echo "$nextAW" | jq -r .fqwn`;
echo "FQWN=$fqwn" >> $GITHUB_ENV
- name: "Dispatched setter (fqwn)"
if: github.event.inputs.dispatch-fqwn != ''
run: |
echo "FQWN=${{ github.event.inputs.dispatch-fqwn }}" >> $GITHUB_ENV
- name: "FQWN Parser"
if: env.FQWN != ''
run: |
fqwn=${{ env.FQWN }}
board="$(echo $fqwn | cut -d':' -f1)"
platform="$(echo $fqwn | cut -d':' -f2)"
folder="$(echo $fqwn | cut -d':' -f3)"
repo="$(echo $fqwn | cut -d':' -f4)"
app_slug="${repo//\//_}"
app_path="$folder/$repo.json"
jsonString="{\"board-name\":\"$board\", \"platform-name\":\"$platform\", \"remote-app-path\":\"$app_path\", \"remote-app-slug\":\"$app_slug\", \"remote-repo-slug\":\"$repo\"}"
echo "WORKFLOW_NAME=Dispatched Build" >> $GITHUB_ENV
echo "JSON_INPUTS=$jsonString" >> $GITHUB_ENV
- name: "Dispatch"
if: env.WORKFLOW_NAME != ''
uses: benc-uk/workflow-dispatch@v1
with:
workflow: ${{ env.WORKFLOW_NAME }}
repo: ${{ secrets.REGISTRY_REPOSITORY }}
token: ${{ secrets.GHTOKEN }}
inputs: '${{ env.JSON_INPUTS }}'
================================================
FILE: .github/workflows/onrelease.yml
================================================
on:
release:
types:
- published
workflow_dispatch:
inputs:
versiontype:
description: 'Version upgrade type (p)atch/(m)inor/(M)ajor/(F)orced'
required: true
default: 'p'
versionforced:
description: 'Optional (F)orced version, leave blank for auto-raise'
required: false
default: ''
jobs:
on_release_semver_next:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: master # this must match the main/master branch name !!
- name: Semver-Iterator
run: |
chmod +x $GITHUB_WORKSPACE/.github/scripts/semver.sh
echo "Running $GITHUB_WORKSPACE/.github/scripts/semver.sh -${{ github.event.inputs.versiontype }} ${{ github.event.inputs.versionforced }} "
$GITHUB_WORKSPACE/.github/scripts/semver.sh -${{ github.event.inputs.versiontype }} ${{ github.event.inputs.versionforced }}
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
commit-message: "raising version"
title: "Semver-Iterator auto-raise"
body: |
A version change occured
================================================
FILE: .gitignore
================================================
build
.pio/
.directory
================================================
FILE: .gitmodules
================================================
[submodule "examples/M5Stack-SD-Menu/SD-Apps/M5Stack_lifegame"]
path = examples/M5Stack-SD-Menu/SD-Apps/M5Stack_lifegame
url = https://gist.github.com/shioken/fa2c3f5923170d86dd358b313d6ab244
[submodule "examples/M5Stack-SD-Menu/SD-Apps/M5_reversi"]
path = examples/M5Stack-SD-Menu/SD-Apps/M5_reversi
url = https://gist.github.com/shioken/e90b9fa3b43d8b067adde77a75768efd
================================================
FILE: .travis.yml
================================================
sudo: required
language: python
python: 3.6
env:
global:
# The Arduino IDE will be installed at APPLICATION_FOLDER/arduino
- APPLICATION_FOLDER="${HOME}/arduino-ide"
- SKETCHBOOK_FOLDER="${HOME}/arduino-sketchbook"
before_install:
# TODO: undo
# remove submodules, we don't want those to be actually tested for compliance
#- git submodule status | git rm --cached `cut -d ' ' -f 3`
- git submodule status | rm -Rf `cut -d ' ' -f 3`
# Formatting checks:
# Check for files starting with a blank line
#- find . -path './.git' -prune -or -type f -print0 | xargs -0 -L1 bash -c 'head -1 "$0" | grep --binary-files=without-match --regexp="^$"; if [[ "$?" == "0" ]]; then echo "Blank line found at start of $0."; false; fi'
# Check for tabs
#- find . -path './.git' -prune -or -type f \( ! -iname ".gitmodules" \) -exec grep --with-filename --line-number --binary-files=without-match --regexp=$'\t' '{}' \; -exec echo 'Tab found.' \; -exec false '{}' +
# Check for trailing whitespace
#- find . -path './.git' -prune -or -type f -exec grep --with-filename --line-number --binary-files=without-match --regexp='[[:blank:]]$' '{}' \; -exec echo 'Trailing whitespace found.' \; -exec false '{}' +
# Check for non-Unix line endings
#- find . -path './.git' -prune -or -type f -exec grep --files-with-matches --binary-files=without-match --regexp=$'\r$' '{}' \; -exec echo 'Non-Unix EOL detected.' \; -exec false '{}' +
# Check for blank lines at end of files
#- find . -path './.git' -prune -or -type f -print0 | xargs -0 -L1 bash -c 'tail -1 "$0" | grep --binary-files=without-match --regexp="^$"; if [[ "$?" == "0" ]]; then echo "Blank line found at end of $0."; false; fi'
# Check for files that don't end in a newline (https://stackoverflow.com/a/25686825)
#- find . -path './.git' -prune -or -type f -print0 | xargs -0 -L1 bash -c 'if test "$(grep --files-with-matches --binary-files=without-match --max-count=1 --regexp='.*' "$0")" && test "$(tail --bytes=1 "$0")"; then echo "No new line at end of $0."; false; fi'
# this repository is broken and won't work as a submodule
#- git clone https://github.com/matsumo/m5stickc_tiny_menu examples/m5stickc_tiny_menu
#- git submodule update --init examples/m5stickc_tiny_menu/
#- git rm --cached examples/m5stickc_tiny_menu
#- git submodule sync examples/m5stickc_tiny_menu
#- git submodule update examples/m5stickc_tiny_menu
- git clone https://github.com/per1234/arduino-ci-script.git "${HOME}/scripts/arduino-ci-script"
- cd "${HOME}/scripts/arduino-ci-script"
# Get new tags from the remote
- git fetch --tags
# Checkout the latest tag
- git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
- source "${HOME}/scripts/arduino-ci-script/arduino-ci-script.sh"
#- set_script_verbosity 1
#- set_verbose_output_during_compilation "true"
# Check for library issues that don't affect compilation
- set_library_testing "true"
- set_application_folder "$APPLICATION_FOLDER"
- set_sketchbook_folder "$SKETCHBOOK_FOLDER"
- install_ide '("1.8.12" "1.8.13" "newest")'
# Install the library from the repository
- install_library
- install_library "M5Stack"
- install_library "M5StickC"
- install_library "ESP32-targz"
- install_library "ESP32-Chimera-Core"
#- install_library "https://github.com/tobozo/ESP32-Chimera-Core/archive/master.zip"
- install_library "LovyanGFX"
# make sure chimera-core does not collide with m5core
#- install_library "https://github.com/M5Stack/M5Core2.git" # "M5Core2" << Official repo slacking on the PR's :-(
- install_library "https://github.com/ropg/M5Core2/archive/master.zip"
- install_library 'https://github.com/bblanchon/ArduinoJson.git' # "ArduinoJSON"
- ls ${TRAVIS_BUILD_DIR} -la
- ls ${TRAVIS_BUILD_DIR}/examples/M5Stack-SD-Menu/ -la
- ls $SKETCHBOOK_FOLDER -la
- ls $SKETCHBOOK_FOLDER/libraries -la
- pwd
#- install_package "esp32:esp32" "https://dl.espressif.com/dl/package_esp32_index.json" # # esp32:esp32:m5stack-core-esp32
- install_package "esp32:esp32" "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json"
#- install_package "m5stack:esp32" "https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json"
# load custom boards from chimera-core
# - cp -R $SKETCHBOOK_FOLDER/libraries/ESP32-Chimera-Core-master/boards/* ~/.arduino15/packages/esp32/hardware/esp32/1.0.4/
- pip install pyserial
script:
# Compile all example sketches included with the library
# build_sketch arguments: sketch name, fqbn, allow failure, IDE version/list/range
- cd ${TRAVIS_BUILD_DIR}
- rm -Rf "${TRAVIS_BUILD_DIR}/examples/M5Stack-SD-Menu/SD-Apps"
#- git submodule foreach git fetch && git submodule deinit -f . && git submodule update --init # reload the submodules since 'check_library_manager_compliance' complains about missing files :-(
- check_library_manager_compliance "$TRAVIS_BUILD_DIR" # why this fails on submodules for an existing '.exe' file is beyond me ...
- set_ide_preference "compiler.warning_level=auto"
- build_sketch "${TRAVIS_BUILD_DIR}/examples/M5Stack-SDLoader-Snippet/M5Stack-SDLoader-Snippet.ino" "esp32:esp32:m5stack-core2" "false" "newest"
- build_sketch "${TRAVIS_BUILD_DIR}/examples/M5Stack-SDLoader-Snippet/M5Stack-SDLoader-Snippet.ino" "esp32:esp32:m5stack-core-esp32" "false" "newest"
- build_sketch "${TRAVIS_BUILD_DIR}/examples/M5StickC-SPIFFS-Loader-Snippet/M5StickC-SPIFFS-Loader-Snippet.ino" "esp32:esp32:m5stick-c" "false" "newest"
- build_sketch "${TRAVIS_BUILD_DIR}/examples/M5Stack-SD-Menu/M5Stack-SD-Menu.ino" "esp32:esp32:m5stack-core-esp32:FlashFreq=80,UploadSpeed=921600" "false" "newest"
- build_sketch "${TRAVIS_BUILD_DIR}/examples/LGFX-SDLoader-Snippet/LGFX-SDLoader-Snippet.ino" "esp32:esp32:m5stack-core-esp32:FlashFreq=80,UploadSpeed=921600" "false" "newest"
- build_sketch "${TRAVIS_BUILD_DIR}/examples/CopySketchToFS/CopySketchToFS.ino" "esp32:esp32:m5stack-core-esp32:FlashFreq=80,UploadSpeed=921600" "false" "newest"
#- build_sketch "${TRAVIS_BUILD_DIR}/examples/m5stickc_tiny_menu/m5stickc_tiny_menu.ino" "esp32:esp32:m5stick-c" "false" "newest"
after_script:
# Commit a report of the job results to the CI-reports repository
- USER_NAME="$(echo "$TRAVIS_REPO_SLUG" | cut -d'/' -f 1)"
- REPOSITORY_NAME="$(echo "$TRAVIS_REPO_SLUG" | cut -d'/' -f 2)"
- publish_report_to_repository "$REPORT_GITHUB_TOKEN" "https://github.com/${USER_NAME}/CI-reports.git" "$REPOSITORY_NAME" "build_$(printf "%05d\n" "${TRAVIS_BUILD_NUMBER}")" "false"
# Print a tab separated report of all sketch verification results to the log
- display_report
notifications:
email:
on_success: always
on_failure: always
webhooks:
urls:
- https://www.travisbuddy.com/
on_success: never
on_failure: always
================================================
FILE: LICENSE
================================================
MIT License
Copyright 2018 tobozo http://github.com/tobozo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
[](https://github.com/tobozo/M5Stack-SD-Updater/blob/master/LICENSE)
[](https://gitter.im/M5Stack-SD-Updater/community)
[](https://www.ardu-badge.com/M5Stack-SD-Updater)
[](https://registry.platformio.org/packages/libraries/tobozo/M5Stack-SD-Updater)



# M5Stack-SD-Updater
<br />
[](https://github.com/PartsandCircuits/M5Stack-SD-Updater/blob/master/SDUpdaterpic02.png)
<br />
## ABOUT
- **M5Stack-SD-Updater is an [Platform.io](https://platformio.org/lib/show/2575/M5Stack-SD-Updater)/[Arduino library](https://www.arduinolibraries.info/libraries/m5-stack-sd-updater) for [M5Stack](http://m5stack.com/) or [Odroid-Go](https://forum.odroid.com/viewtopic.php?t=31705) to package your apps on a SD card and load them from a menu such as the [default SD Menu example](https://github.com/tobozo/M5Stack-SD-Updater/tree/master/examples/M5Stack-SD-Menu) of this project or [@lovyan03](https://github.com/lovyan03)'s [Treeview based SD Menu](https://github.com/lovyan03/M5Stack_LovyanLauncher). For a Micropython compatible version of this library, see the [M5Stack_MicroPython custom_sdupdater](https://github.com/ciniml/M5Stack_MicroPython/tree/custom_sdupdater)**
- It is inspired by gamebuino, however it does not use a modified bootloader.
- [Video demonstration](https://www.youtube.com/watch?v=myQfeYxyc3o)
- This project by [tobozo](https://github.com/tobozo) - Demo built on M5Stack-SAM by Tom Such. Further credits listed below.
- Contributors welcome !
<br />
🏭 M5Stack-SD-Menu EXAMPLE SKETCH PREREQUISITES:
------------------------------------------------
<br />
**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).
<br />
**Make sure you have the following libraries:** - they should be installed in: `~/Arduino/libraries`
- [ESP32-Chimera-Core](https://github.com/tobozo/ESP32-Chimera-Core), [LovyanGFX](https://github.com/lovyan03/LovyanGFX), [M5GFX](https://github.com/m5stack/M5GFX), [M5Unified](https://github.com/m5stack/M5Unified) or [M5Stack Core](https://github.com/m5stack/M5Stack).
- [M5Stack-SD-Updater](https://github.com/tobozo/M5Stack-SD-Updater) (this library + its examples).
- [ArduinoJSON](https://github.com/bblanchon/ArduinoJson/) (Optional, used by SD-Menu).
- [ESP32-targz](https://github.com/tobozo/ESP32-targz) (Optional if using gzipped firmwares)
All those are available in the [Arduino Library Manager](https://www.arduinolibraries.info/libraries/m5-stack-sd-updater) or by performing a [manual installation](https://www.arduino.cc/en/Guide/Libraries#toc5).
<br />
🍱 UNPACKING THE BINARIES
-------------------------
**obsolete**
~~For your own lazyness, you can use @micutil's awesome [M5Burner](https://github.com/micutil/M5Burner_Mic) 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.**
<br />
**outdated binaries**
~~**2) Download the [SD-Content :floppy_disk:](https://github.com/tobozo/M5Stack-SD-Updater/releases/download/v0.4.1/SD-Apps-Folder.zip) 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](https://github.com/tobozo/M5Stack-SD-Updater/tree/master/examples/M5Stack-SD-Menu/SD-Apps) and the relative meta information for the menu.~~
<br />
**2) Compile and flash the `M5Stack-SD-Menu.ino` example.** <br />
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.
<br />
**3) Make application sketches compatible with the SD-Updater Menu .** <br />
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:
```C
// #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:
```C
// #define SDU_ENABLE_GZ // optional: support for gzipped firmwares
#include <M5StackUpdater.h>
```
In your `setup()` function, find the following statements:
```C++
M5.begin();
// Serial.begin(115200);
```
And add this after serial is started:
```C
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 :
```C
checkSDUpdater( SD, MENU_BIN, 2000 );
```
Custom SD-Load scenarios can be achieved using non default values:
```C
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.
```C
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:
```C++
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
```
<br />
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.
<br />
⌾ 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:
```C++
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:
```C++
// 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:
```C++
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):
```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 );
```
Set buttons state polling function (typically M5.update()
```C++
// void(*myButtonPollCb)();
SDUCfg.setSDUBtnPoller( myButtonPollCb );
```
Set each button state getter function, it must return true when the state is "pushed".
```C++
SDUCfg.setSDUBtnA( myBtnAPushedcb ); // bool (*myBtnAPushedcb )()
SDUCfg.setSDUBtnB( myBtnBPushedcb ); // bool (*myBtnBPushedcb )()
SDUCfg.setSDUBtnC( myBtnCPushedcb ); // bool (*myBtnCPushedcb )()
```
<br />
<br />
📚 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 ~~"}`
<br />
⚠️ 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.
<br />
<br />
🚫 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.
<br />
🔘 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)`.
```C++
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):
```C++
#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:
```C++
#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](https://github.com/tobozo/ESP32-targz) was previously included.
The firmware must have the `.gz.` extension and be a valid gzip file to trigger the decompression.
```C++
#define SDU_ENABLE_GZ // enable support for gzipped firmwares
#include "M5StackUpdater.h"
void setup()
{
checkSDUpdater( SD, "/menu.gz", 2000 );
}
```
- The JoyPSP and [M5Stack-Faces](https://github.com/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.
<br />
⚠️ 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.
<br /><br />
🏭 Factory Partition
--------------------
Abuse the OTA partition scheme and store up to 5 applications on the flash, plus the firmware loader.
⚠️ This scenario uses a special [firmware loader](https://github.com/tobozo/M5Stack-SD-Updater/tree/1.2.8/examples/M5Stack-FW-Menu) `M5Stack-FW-Menu`, a custom partition scheme, and a different integration of M5Stack-SD-Updater in the loadable applications.
Although it can work without the SD Card, `M5Stack-FW-Menu` can still act as a low profile replacement for the classic SD Card `/menu.bin`, and load binaries from the SD Card or other supported filesystems.
#### Requirements:
- Flash size must be 8MB or 16MB
- custom partitions.csv must have more than 2 OTA partitions followed by one factory partition (see annotated example below)
- loadable applications and firmware loader must share the same custom partitions scheme at compilation
- `SDUCfg.rollBackToFactory = true;` must be set in all loadable applications (see `Detect factory support`)
#### Custom partition scheme annotated example:
```csv
# 6 Apps + Factory
# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x5000
otadata, data, ota, 0xe000, 0x2000
ota_0, 0, ota_0, 0x10000, 0x200000 ,<< Default partition for flashing (UART, 2MB)
ota_1, 0, ota_1, 0x210000, 0x200000 ,<< Default partition for flashing (OTA, 2MB)
ota_2, 0, ota_2, 0x410000, 0x200000 ,<< Application (2MB)
ota_3, 0, ota_3, 0x610000, 0x200000 ,<< Application (2MB)
ota_4, 0, ota_4, 0x810000, 0x200000 ,<< Application (2MB)
ota_5, 0, ota_5, 0xA10000, 0x200000 ,<< Application (2MB)
firmware, app, factory, 0xC10000, 0x0F0000 ,<< Factory partition holding the firmware menu (960KB)
spiffs, data, spiffs, 0xD00000, 0x2F0000 ,<< SPIFFS (2MB)
coredump, data, coredump, 0xFF0000, 0x10000
```
#### Quick Start:
- Set a custom partition scheme according to the requirements (see annotated example above)
- Compile and flash the [M5Stack-FW-Menu]https://github.com/tobozo/M5Stack-SD-Updater/tree/master/examples/M5Stack-FW-Menu)
- On first run the `M5Stack-FW-Menu` firmware loader will automatically populate the factory partition and restart from there
Then for every other app you want to store on the flash:
- Set the same custom partition scheme as `M5Stack-FW-Menu`
- Add `SDUCfg.rollBackToFactory = true;` and second argument must be empty e.g. `checkSDUpdater( SD, "", 5000, TFCARD_CS_PIN )`
- Compile your app
- Copy the binary to the SD Card (e.g. copy the bin manually or use the `Save SD` option from the app's SD-Updater lobby)
- Use `FW Menu` option from the app's SD-Updater lobby (note: **it should load the firmware loader, not the /menu.bin from the SD Card**)
- Use the firmware loader menu `Manage Partitions/Add Firmware` to copy the recently added app to one of the available slots
Note: the firmware loader can copy applications from any filesystem (SD, SD_MMC, SPIFFS, LittleFS, FFat).
#### Detect factory support
```cpp
#if M5_SD_UPDATER_VERSION_INT >= VERSION_VAL(1, 2, 8)
// New SD Updater support, requires version >=1.2.8 of https://github.com/tobozo/M5Stack-SD-Updater/
if( Flash::hasFactoryApp() ) {
SDUCfg.rollBackToFactory = true;
SDUCfg.setLabelMenu("FW Menu");
SDUCfg.setLabelRollback("Save FW");
checkFWUpdater( 5000 );
} else
#endif
{
checkSDUpdater( SD, MENU_BIN, 5000, TFCARD_CS_PIN );
}
```
🛣 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!
<br />
#️⃣ REFERENCES:
--------------
<br />
| | | |
| ------------ |:------------------------ | :------------------------------------------- |
| :clapper: | Video demonstration | https://youtu.be/myQfeYxyc3o |
| :clapper: | [Video demo of Pacman + sound](https://youtu.be/36fgNCecoEg) | [Source](https://github.com/tobozo/M5Stack-Pacman-JoyPSP) |
| :clapper: | [Video demo of NyanCat](https://youtu.be/Zxh2mtWwfaE) | [Source](https://github.com/tobozo/M5Stack-NyanCat) |
| 🎓 | [Macsbug's article on M5Stack SD-Updater](https://macsbug.wordpress.com/2018/03/12/m5stack-sd-updater/) | [🇯🇵](https://macsbug.wordpress.com/2018/03/12/m5stack-sd-updater/) [🇬🇧](https://translate.google.com/translate?hl=en&sl=ja&tl=en&u=https%3A%2F%2Fmacsbug.wordpress.com%2F2018%2F03%2F12%2Fm5stack-sd-updater%2F) (google translate)|
<br />
🙏 CREDITS:
-----------
<br />
| | | | |
| ------ |:------------------- | :--------------- | :------------------------------------------- |
| 👍 | 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 |
================================================
FILE: component.mk
================================================
#
# Main Makefile. This is basically the same as a component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
COMPONENT_SRCDIRS := src
COMPONENT_ADD_INCLUDEDIRS := src
================================================
FILE: examples/AppStore/AppStore.ino
================================================
#include "main/main.cpp"
================================================
FILE: examples/AppStore/main/main.cpp
================================================
#include "../modules/AppStoreMain/AppStoreMain.cpp"
void setup()
{
AppStore::setup();
}
void loop()
{
AppStore::loop();
}
#if !defined ARDUINO
extern "C" {
void loopTask(void*)
{
setup();
for(;;) {
loop();
}
}
void app_main()
{
xTaskCreatePinnedToCore( loopTask, "loopTask", 8192, NULL, 1, NULL, 1 );
}
}
#endif
================================================
FILE: examples/AppStore/modules/AppStoreActions/AppStoreActions.cpp
================================================
#pragma once
#include <ArduinoJson.h>
#include "AppStoreActions.hpp"
#include "../AppStoreUI/AppStoreUI.hpp"
#include "../Registry/Registry.hpp"
#include "../FSUtils/FSUtils.hpp"
#include "../Downloader/Downloader.hpp"
extern AppRegistry Registry;
extern LogWindow *Console;
namespace UIDo
{
using namespace FSUtils;
using namespace UILists;
using namespace UIUtils;
using namespace RegistryUtils;
void BtnA()
{
// Action button
UI->execBtnA();
}
void BtnB()
{
// Navigation button
//UI->pageDown();
UI->menuUp();
}
void BtnC()
{
// Navigation button
UI->menuDown();
}
void checkSleepTimer()
{
if( lastpush + MsBeforeSleep < millis() ) { // go to sleep if nothing happens for a while
if( brightness > 1 ) { // slowly dim the screen first
brightness--;
if( brightness %10 == 0 ) {
Serial.print("(\".¬.\") ");
}
if( brightness %30 == 0 ) {
Serial.print(" Yawn... ");
}
if( brightness %7 == 0 ) {
Serial.println(" .zzZzzz. ");
}
UI->getGfx()->setBrightness( brightness );
lastpush = millis() - (MsBeforeSleep - brightness*10); // exponential dimming effect
return;
}
gotoSleep();
}
}
void gotoSleep()
{
Serial.println( GOTOSLEEP_MESSAGE );
#ifdef ARDUINO_M5STACK_Core2
esp_sleep_enable_ext0_wakeup(GPIO_NUM_39, 0); // gpio39 == touch INT
#else
#if defined HAS_POWER || defined HAS_IP5306
// M5Fire / M5Stack / Odroid-Go
M5.setWakeupButton( BUTTON_B_PIN );
//M5.powerOFF();
#endif
#endif
delay(100);
M5.Lcd.fillScreen( UI->getTheme()->BgColor );
M5.Lcd.sleep();
M5.Lcd.waitDisplay();
esp_deep_sleep_start();
}
void clearTLS()
{
// cleanup /cert/ and /.registry/ folders
Console = new LogWindow();
cleanDir( SD_CERT_PATH );
cleanDir( appRegistryFolder.c_str() );
delete Console;
buildRootMenu();
}
void clearApps()
{
int resp = modalConfirm( MODAL_DELETEALL_TITLE, nullptr, MODAL_DELETEALL_MSG, MENUACTION_APPDELETE, MENU_BTN_CANCEL, MENU_BTN_NO );
if( resp == HID_BTN_A ) {
#if !defined HAS_RTC
setTimeFromLastFSAccess(); // set the time before cleaning up the folder
#endif
Console = new LogWindow();
cleanDir( CATALOG_DIR );
// cleanDir( ROOT_DIR ); // <<< TODO: filter from app list
// cleanDir( DIR_jpg ); // <<< TODO: filter from app list
// cleanDir( DIR_json ); // <<< TODO: filter from app list
cleanDir( "/tmp/" );
delete Console;
}
buildRootMenu();
}
void setNtpServer()
{
uint8_t menuId = UI->getListID();
if( menuId > 0 ) { // first element is "back to root menu"
NTP::setServer( menuId-1 );
}
buildRootMenu();
}
void installApp( const char* appName )
{
// TODO: confirmation dialog
if( !Downloader::downloadApp( String(appName) ) ) {
log_e("Download of %s app failed");
}
}
void installApp()
{
MenuActionLabels* tempLabels = UI->getMenuActionLabels();
installApp( UI->getListItemTitle() );
UI->setMenuActionLabels( tempLabels );
}
void deleteApp( const char* appName )
{
drawInfoWindow( String( "Deleting " + String(appName) ).c_str() );
removeInstalledApp( String( appName ) );
}
void deleteApp()
{
deleteApp( UI->getListItemTitle() );
}
void removeHiddenApp()
{
// TODO: open appinfowindow + hide button
String appName = String( UI->getListItemTitle() );
drawInfoWindow( String( "Unhiding " + appName).c_str() );
toggleHiddenApp( UI->getListItemTitle(), false );
}
void addHiddenApp()
{
// TODO: open appinfowindow + hide button
String appName = String( UI->getListItemTitle() );
drawInfoWindow( String( "Hiding " + appName).c_str() );
toggleHiddenApp( UI->getListItemTitle(), true );
}
void downloadCatalog()
{
bool loop = true;
do {
loop = Downloader::downloadGzCatalog()
? false
: modalConfirm( MODAL_DOWNLOADFAIL_TITLE, nullptr, MODAL_DOWNLOADFAIL_MSG, MENU_BTN_RETRY, MENU_BTN_CANCEL, MENU_BTN_NO ) == HID_BTN_A
;
} while( loop == true );
buildRootMenu();
}
void idle()
{
}
void modal()
{
cycleAppAssets();
}
#if !defined FS_CAN_CREATE_PATH
void doFSChecks()
{
#if !defined HAS_RTC
setTimeFromLastFSAccess();
#endif
scanDataFolder(); // do SD health checks, create folders
}
#endif
};
namespace UIDraw
{
using namespace MenuItems;
using namespace UIUtils;
using namespace Downloader;
using namespace RegistryUtils;
void drawDownloaderMenu( const char* title, const char* body )
{
UI->windowClr( UI->getTheme()->MenuColor );
UI->setMenuActionLabels( &RefreshAppsActionButtons );
UI->drawMenu( false );
drawStatusBar();
if( title != nullptr ) {
drawInfoWindow( title, body );
}
}
void drawList( bool renderButtons )
{
if( renderButtons ) {
UI->drawMenu( false );
drawStatusBar();
}
UI->showList();
}
void drawStatusBar()
{
UITheme* theme = UI->getTheme();
LGFX* gfx = UI->getGfx();
ForkIcon.draw( gfx, 4, (TITLEBAR_HEIGHT-2)/2-ForkIcon.height/2 );
drawTextShadow(gfx, UI->channel_name, ForkIcon.width+8, (TITLEBAR_HEIGHT-2)/2, theme->TextColor, theme->TextShadowColor, &TomThumb, ML_DATUM );
if( wifisetup ) {
int8_t rssiminmaxed = min( (int8_t)-70, max( (int8_t)-50, (int8_t)WiFi.RSSI() ) );
int16_t rssimapped = map( rssiminmaxed, -50, -70, 1, 5 );
drawRSSIBar( 290, 4, rssimapped, theme->MenuColor, 2.0 );
} else {
SDUpdaterIcon.draw( gfx, 298, 6 );
}
if( ntpsetup ) {
// TODO: draw some ntp icon
}
}
void drawRegistryMenu()
{
String _mc = ""; // macro separator
String modalMessage = _mc + CHANNEL_TOOL_TEXT + "\n"
+ "\nCurrent: " + Registry.defaultChannel.name
+ "\nAlternate: " + (Registry.defaultChannel.name == REGISTRY_MASTER ? Registry.unstableChannel.name : Registry.masterChannel.name)
;
int resp = modalConfirm( CHANNEL_TOOL, ROOTACTION_SWITCH, modalMessage.c_str(), DOWNLOADER_MODAL_CHANGE, MENU_BTN_UPDATE, MENU_BTN_CANCEL );
// choose between updating the JSON or changing the default channel
switch( resp ) {
case HID_BTN_A: // pick a channel
resp = modalConfirm( CHANNEL_CHOOSER, CHANNEL_CHOOSER_PROMPT, CHANNEL_CHOOSER_TEXT, DOWNLOADER_MODAL_CHANGE, MENU_BTN_CANCEL, MENU_BTN_BACK );
if( resp == HID_BTN_A ) {
if( Registry.pref_default_channel == REGISTRY_MASTER ) {
Registry.pref_default_channel = REGISTRY_UNSTABLE;
} else {
Registry.pref_default_channel = REGISTRY_MASTER;
}
registrySave( Registry, appRegistryFolder + PATH_SEPARATOR + appRegistryDefaultName );
// TODO: download registry JSON
//Serial.println("Will reload in 5 sec");
delay(5000);
ESP.restart();
}
break;
case HID_BTN_B: // update channel
resp = modalConfirm( CHANNEL_DOWNLOADER, CHANNEL_DOWNLOADER_PROMPT, CHANNEL_DOWNLOADER_TEXT, MENU_BTN_UPDATE, MENU_BTN_CANCEL, MENU_BTN_BACK );
if( resp == HID_BTN_A ) {
registryFetch( Registry, appRegistryFolder + PATH_SEPARATOR + appRegistryDefaultName );
}
break;
default: // run wifi manager ?
break;
}
drawList( true );
}
};
namespace AppRenderer
{
using namespace UILists;
using namespace FSUtils;
//LGFX* gfx;
void AppInfo::clear()
{
appNameStr = "";
authorNameStr = NOT_IN_REGISTRY;
projectURLStr = "";
descriptionStr = "";
creditsStr = "";
type = REG_UNKNOWN;
assets_folder = nullptr;
assets.clear();
binSize = packageSize = assetsCount = rawtime = 0;
log_v("Cleared");
}
void AppInfo::parseAssets( JsonObject root )
{
if( assetsCount > 0 ) {
String appImageShaSum = "";
String authorImageShaSum = "";
assets.clear();
for (JsonVariant value : root["json_meta"]["assets"].as<JsonArray>() ) {
String assetName = value["name"].as<String>();//: "9axis_data_publisher.jpg",
String assetPath = value["path"].as<String>();//: "/jpg/",
String assetSha256Sum = value["sha256_sum"].as<String>();
size_t assetSize = value["size"].as<size_t>();//: 7215,
rawtime = value["created_at"].as<time_t>();
log_d("Collected rawtime: %d for file %s%s", rawtime, assetPath.c_str(), assetName.c_str() );
String assetFullPath = assetPath+assetName;
packageSize += assetSize;
if( isBinFile( assetFullPath.c_str() ) ) {
if( M5_FS.exists( assetFullPath ) ) {
type = REG_LOCAL;
}
binSize = assetSize;
rawtime = rawtime;//: 1573146468,
} else {
if( assetName == appNameStr + EXT_jpg ) {
// is app image
appImageShaSum = assetSha256Sum;
} else if ( assetName == appNameStr + "_gh" + EXT_jpg ) {
// is author image
authorImageShaSum = assetSha256Sum;
}
}
assets.push_back({ assetFullPath, assetName, assetPath, assetSha256Sum, assetSize, rawtime });
}
has_app_image = false;
if( appImageShaSum != "" && authorImageShaSum != "" ) {
if( appImageShaSum != authorImageShaSum ) {
log_w("Author image and app image differ!");
has_app_image = true;
} else {
log_w("Author image and app image are similar!");
}
} else {
log_w("Author image and app image shasums are missing!");
}
}
}
void AppInfo::draw()
{
log_v("rendering");
UI->windowClr();
UITheme* theme = UI->getTheme();
AppInfoPosY = 64;
if( appInfo.appNameStr ) {
drawTextShadow( _gfx, appInfo.appNameStr.c_str(), _gfx->width()/2, AppInfoPosY-20, theme->TextColor, theme->TextShadowColor, &FreeMono9pt7b, MC_DATUM );
}
if( authorNameStr ) {
String authorString = authorNameStr;
if( strcmp( NOT_IN_REGISTRY, authorString.c_str() ) !=0 ) {
showAppImage( assets_folder, "_gh");
authorString = "By: "+authorString;
} else {
UnknownAppIcon.draw( _gfx, theme->assetPosX, theme->assetPosY );
}
drawTextShadow( _gfx, authorString.c_str(), AppInfoPosX, AppInfoPosY, theme->TextColor, theme->TextShadowColor, &FreeMono9pt7b, ML_DATUM );
AppInfoPosY += 21;
} else { log_v("NO AUTHOR"); }
if( assetsCount > 0 ) {
String assetsString = "Assets: " + String( assetsCount );
drawTextShadow( _gfx, assetsString.c_str(), AppInfoPosX, AppInfoPosY, theme->TextColor, theme->TextShadowColor, &FreeMono9pt7b, ML_DATUM );
AppInfoPosY += 21;
} else { log_v("NO ASSETS"); }
if( binSize > 0 ) {
String sizeString = "Bin.: " + String(formatBytes( binSize, formatBuffer ));
drawTextShadow( _gfx, sizeString.c_str(), AppInfoPosX, AppInfoPosY, theme->TextColor, theme->TextShadowColor, &FreeMono9pt7b, ML_DATUM );
AppInfoPosY += 21;
} else { log_v("NO BIN SIZE"); }
if( packageSize > 0 ) {
String totalSize = "Tot.: " + String(formatBytes( packageSize, formatBuffer ));
drawTextShadow( _gfx, totalSize.c_str(), AppInfoPosX, AppInfoPosY, theme->TextColor, theme->TextShadowColor, &FreeMono9pt7b, ML_DATUM );
AppInfoPosY += 21;
} else { log_v("NO PACK SIZE"); }
if( rawtime > 0 ) {
struct tm * timeinfo;
//time (&rawtime);
timeinfo = localtime (&rawtime);
drawTextShadow( _gfx, "Creation date:", AppInfoPosX, AppInfoPosY, theme->TextColor, theme->TextShadowColor, &FreeMono9pt7b, ML_DATUM );
AppInfoPosY += 21;
strftime( formatBuffer, 64, "%F", timeinfo ); // to ISO date format
String binDateStr = String( formatBuffer );
drawTextShadow( _gfx, binDateStr.c_str(), AppInfoPosX+12, AppInfoPosY, theme->TextColor, theme->TextShadowColor, &FreeMono9pt7b, ML_DATUM );
AppInfoPosY += 21;
strftime( formatBuffer, 64, "%T", timeinfo ); // to ISO time format
String binTimeStr = String( formatBuffer );
drawTextShadow( _gfx, binTimeStr.c_str(), AppInfoPosX+12, AppInfoPosY, theme->TextColor, theme->TextShadowColor, &FreeMono9pt7b, ML_DATUM );
} else { log_v("NO DATETIME"); }
lastrender = millis();
}
};
namespace UIShow
{
using namespace FSUtils;
using namespace UILists;
using namespace UIDo;
using namespace MenuItems;
using namespace AppRenderer;
std::vector<onrender_cb_t> callbacks;
void showAppQrCode()
{
UITheme* th=UI->getTheme();
qrRender(UI->getGfx(), appInfo.projectURLStr, th->assetPosX, th->assetPosY, th->assetWidth, th->assetHeight );
}
void showAppAssetImage()
{
showAppImage( appInfo.assets_folder, "" );
}
void showAppAssetAuthor()
{
showAppImage( appInfo.assets_folder, "_gh" );
}
void showNTPImage()
{
const char* serverNameCstr = UI->getListItemTitle();
String serverNameStr = String( serverNameCstr );
String _ms = ""; // macro separator
String iconPath = CATALOG_DIR + _ms + DIR_png + "NTP-" + serverNameStr + EXT_png;
UITheme* theme = UI->getTheme();
RemoteAsset appIcon = { iconPath.c_str(), theme->assetWidth, theme->assetHeight, serverNameCstr };
drawAssetReveal( &appIcon, UI->getGfx(), theme->assetPosX, theme->assetPosY );
}
void updateCheckShowAppImage()
{
AppInfo tmpInfo;
getAppInfo( &tmpInfo, JSON_LOCAL );
getAppInfo( &appInfo, JSON_REMOTE );
// "IN REGISTRY" => compare binary size with meta, propose sha256_sum check or update + delete
showAppImage( "", "");
UITheme* theme = UI->getTheme();
LGFX* gfx = UI->getGfx();
// add icon status as an overlay to the image
if( appInfo.binSize != tmpInfo.binSize ) {
UpdateIcon.draw( gfx, theme->assetPosX, theme->assetPosY );
log_v("Bin sizes differ (local=%d, remote=%d)", tmpInfo.binSize, appInfo.binSize);
} else {
log_v("Bin sizes match (%d)", appInfo.binSize);
CheckIcon.draw( gfx, theme->assetPosX, theme->assetPosY );
}
}
void updateMetaShowAppImage()
{
// "BINARY INSTALLED" && "IN REGISTRY" => missing local meta ? => suggest copy meta + delete
log_d("TODO: check if missing local meta");
getAppInfo( &appInfo, JSON_REMOTE );
showAppImage( CATALOG_DIR, "");
UITheme* theme = UI->getTheme();
UpdateIcon.draw( UI->getGfx(), theme->assetPosX, theme->assetPosY );
}
void showDeleteAppImage()
{
String _ms = ""; // for macro separation
String imageLocal = DIR_jpg + String(UI->getListItemTitle()) + EXT_jpg;
String imageRemote = CATALOG_DIR + _ms + DIR_jpg + String(UI->getListItemTitle()) + EXT_jpg;
getAppInfo( &appInfo, JSON_LOCAL );
UITheme* theme = UI->getTheme();
RemoteAsset appIcon = { nullptr, theme->assetWidth, theme->assetHeight, UI->getListItemTitle() };
String imageRender = "";
if( M5_FS.exists( imageLocal ) ) {
appIcon.path = imageLocal.c_str();
log_d("local image");
} else if( M5_FS.exists( imageRemote ) ) {
appIcon.path = imageRemote.c_str();
log_d("remote image");
} else {
appIcon = UnknownAppIcon;
log_d("default image");
}
drawAssetReveal( &appIcon, UI->getGfx(), theme->assetPosX, theme->assetPosY );
}
void showAppImage()
{
showAppImage( UI->getList()->assets_folder, "");
if( strcmp( UI->getListItemTitle(), appInfo.appNameStr.c_str() ) != 0 ) {
log_d("AppInfo expired (expecting:%s, got:%s)", UI->getListItemTitle(), appInfo.appNameStr.c_str() );
getAppInfo( &appInfo, UI->getList()->assets_folder[0] == '0' ? JSON_LOCAL : JSON_REMOTE );
} else {
log_d("re-using existing appinfo");
}
appInfo.lastrender = millis();
}
void showAppImage( const char* prefix, const char* suffix )
{
const char* appNameStr = UI->getListItemTitle();
const char* menuNameStr = UI->getListTitle();
char appImageFile[255] = {0};
memset( appImageFile, 0, 255 );
snprintf( appImageFile, 255, "%s" DIR_jpg "%s%s" EXT_jpg, prefix, appNameStr, suffix );
log_v("Extrapolated Icon Path: '%s' (%d bytes) for '%s' menu item", appImageFile, strlen(appImageFile), menuNameStr );
UITheme* theme = UI->getTheme();
RemoteAsset appIcon = { appImageFile, theme->assetWidth, theme->assetHeight, appNameStr };
drawAssetReveal( &appIcon, UI->getGfx(), theme->assetPosX, theme->assetPosY );
appInfo.lastrender = millis();
}
void getAppInfo( AppInfo *appInfo, AppJSONType jsonType )
{
appInfo->clear();
appInfo->appNameStr = String( UI->getListItemTitle() );
appInfo->type = isHiddenApp( appInfo->appNameStr ) ? REG_HIDDEN : M5_FS.exists( ROOT_DIR+appInfo->appNameStr+EXT_bin ) ? REG_LOCAL : REG_REMOTE;
appInfo->assets_folder = UI->getList()->assets_folder;
String assetsFolderStr = jsonType == JSON_LOCAL ? "" : CATALOG_DIR;
String jsonFile = assetsFolderStr + DIR_json + appInfo->appNameStr + EXT_json;
String appImageFile = assetsFolderStr + DIR_jpg + appInfo->appNameStr + EXT_jpg;
String binFile = ROOT_DIR + appInfo->appNameStr + EXT_bin;
if( M5_FS.exists( binFile ) ) getFileAttrs( binFile.c_str(), &appInfo->binSize, &appInfo->rawtime );
JsonObject root;
DynamicJsonDocument jsonBuffer( 8192 );
if( !getJson( jsonFile.c_str(), root, jsonBuffer ) ) {
appInfo->type = NOREG;
log_d("NOREG: '%s' has no JSON", jsonFile.c_str() );
return;
}
if ( root.isNull() ) {
appInfo->type = NOREG;
log_e("No parsable JSON in %s file", jsonFile.c_str() );
return;
}
size_t jsonMetaPropsCount;
fs::File file;
switch( jsonType ) {
case JSON_LOCAL:
if( M5_FS.exists( appImageFile ) ) appInfo->assetsCount++;
if( M5_FS.exists( jsonFile ) ) appInfo->assetsCount++;
appInfo->descriptionStr = root["description"].isNull() ? "" : root["description"].as<String>();
appInfo->authorNameStr = root["authorName"].isNull() ? "" : root["authorName"].as<String>();
appInfo->projectURLStr = root["projectURL"].isNull() ? "" : root["projectURL"].as<String>();
appInfo->creditsStr = root["credits"].isNull() ? "" : root["credits"].as<String>();
log_d("JSON_LOCAL: %s (%d bytes)", appInfo->appNameStr.c_str(), appInfo->binSize );
break;
case JSON_REMOTE:
if( !root["name"].as<String>().equals( appInfo->appNameStr ) ) {
log_e("AppName mismatch (expecting:'%s', got:'%s'", appInfo->appNameStr.c_str(), root["name"].as<String>().c_str() );
return;
}
jsonMetaPropsCount = root["json_meta"].size(); // only present on remote appinfo
appInfo->binSize = root["size"].as<size_t>();
appInfo->descriptionStr = jsonMetaPropsCount > 0 ? root["json_meta"]["description"].isNull() ? "" : root["json_meta"]["description"].as<String>() : "";
appInfo->assetsCount = jsonMetaPropsCount > 0 ? root["json_meta"]["assets"].size() : 0;
appInfo->authorNameStr = jsonMetaPropsCount > 0 ? root["json_meta"]["authorName"].as<String>() : "";
appInfo->projectURLStr = jsonMetaPropsCount > 0 ? root["json_meta"]["projectURL"].as<String>() : "";
appInfo->creditsStr = jsonMetaPropsCount > 0 ? root["json_meta"]["credits"].as<String>() : "";
if( appInfo->appNameStr == "" || appInfo->binSize <= 0 || jsonMetaPropsCount == 0 ) {
log_e("Invalid AppInfo in JSON (path: %s)", jsonFile.c_str() );
return;
}
appInfo->parseAssets( root );
log_d("JSON_REMOTE: %s (%d bytes)", appInfo->appNameStr.c_str(), appInfo->binSize );
break;
default:
log_e("INVALID TYPE");
break;
}
cycleid = 0;
cycleanimation = false;
callbacks.clear();
if( appInfo->projectURLStr !="" ) {
callbacks.push_back( &showAppQrCode );
cycleanimation = true;
}
callbacks.push_back( &showAppAssetAuthor );
if( appInfo->has_app_image ) {
callbacks.push_back( &showAppAssetImage );
cycleanimation = true;
}
}
void handleModalAction( AppInfo * appInfo )
{
onselect_cb_t aftermodal = [](){};
MenuActionLabels *confirmLabels = nullptr;
int hidState = HID_BTN_C;
switch( appInfo->type ) {
case REG_HIDDEN:
log_v("REG_HIDDEN");
confirmLabels = &UnhideAppsActionButtons;
aftermodal = &buildHiddenAppList;
break;
case NOREG:
log_v("NOREG");
confirmLabels = &DeleteAppsActionButtons;
aftermodal = &buildMyAppsMenu;
break;
case REG_LOCAL:
log_v("REG_LOCAL");
confirmLabels = &DeleteAppsActionButtons;
aftermodal = &buildMyAppsMenu;
break;
case REG_REMOTE:
log_v("REG_REMOTE");
confirmLabels = &InstallHideAppsActionButtons;
aftermodal = &buildStoreMenu;
break;
case REG_UNKNOWN:
default:
log_e("REG_UNKNOWN, No valid AppInfo->type (#%d) provided for menu element '%s'", appInfo->type, UI->getListTitle() );
break;
}
if( confirmLabels ) {
confirmLabels->title = appInfo->appNameStr.c_str();
hidState = modalConfirm( confirmLabels, true );
}
switch( hidState ) {
case HID_BTN_A: confirmLabels->Buttons[0]->onClick(); aftermodal(); break;
case HID_BTN_B: confirmLabels->Buttons[1]->onClick(); aftermodal(); break;
case HID_BTN_C: UI->drawMenu( true ); UI->showList(); break;
}
}
void showAppInfo()
{
appInfo.draw();
handleModalAction( &appInfo );
}
void scrollAppInfo()
{
LGFX* gfx = UI->getGfx();
String scrollStr = appInfo.creditsStr;
if( appInfo.projectURLStr != "" ) scrollStr = scrollStr+ SCROLL_SEPARATOR + appInfo.projectURLStr;
if( appInfo.descriptionStr != "" ) scrollStr = scrollStr+ SCROLL_SEPARATOR + appInfo.descriptionStr;
scrollStr = scrollStr+SCROLL_SEPARATOR;
HeaderScroll.render( gfx, scrollStr, 10, 2, nullptr, 0, 0, gfx->width(), TITLEBAR_HEIGHT );
}
void cycleAppAssets()
{
if( !cycleanimation ) return;
uint32_t elapsed = millis()-appInfo.lastrender;
if( elapsed > cbdelay ) {
uint32_t cbid = cycleid%callbacks.size();
callbacks[cbid]();
appInfo.lastrender = millis();
cycleid++;
} else {
if( elapsed > 0 ) {
UITheme* th=UI->getTheme();
float progress = float(float(elapsed)/float(cbdelay))*th->assetWidth;
UI->getGfx()->fillRect( th->assetPosX, th->assetPosY+th->assetHeight-2, progress, 2, TFT_RED );
}
delay(10); // kill that buzz sound coming out of the speaker :D
}
}
};
namespace UILists
{
using namespace MenuItems;
using namespace FSUtils;
using namespace UIDo;
using namespace UIShow;
using namespace Downloader;
void buildNtpMenu()
{
size_t before = ESP.getFreeHeap();
NtpMenuGroup.clear();
NtpMenuGroup.push( &BackToRootMenu );
size_t servers_count = sizeof( NTP::Servers ) / sizeof( NTP::Server );
for( int i=0; i<servers_count; i++ ) {
log_v("Adding action menu %d : %s", i, NTP::Servers[i].name );
NtpMenuGroup.push( NTP::Servers[i].name, &NtpItemCallbacks );
}
NtpMenuGroup.selectedindex = NTP::currentServer+1;
UI->setList( &NtpMenuGroup );
UIDraw::drawList( true ); // render the menu
log_v("Servers count: %d (bytes free: before=%d, after=%d)", UI->getListSize(), before, ESP.getFreeHeap() );
}
void buildRootMenu()
{
size_t before = ESP.getFreeHeap();
countApps();
String jsonFile = CATALOG_DIR + Registry.defaultChannel.catalog_endpoint /*"/catalog.json"*/;
bool has_catalog = M5_FS.exists( jsonFile );
bool has_certs = M5_FS.exists( SD_CERT_PATH );
log_v("Root menu: has_catalog:%s, has_certs:%s", has_catalog?"true":"false", has_certs?"true":"false" );
RootMenuGroup.clear();
if( has_catalog ) {
RootActionRefresh.setTitle( ROOTACTION_REFRESH );
} else {
RootActionRefresh.setTitle( ROOTACTION_DOWNLOAD );
}
RootActionBrowse.setTitle( MENUTITLE_MANAGEAPPS );
if( has_catalog || appsCount > 0 ) {
RootMenuGroup.push( &RootActionBrowse );
}
RootMenuGroup.push( &RootActionRefresh );
RootMenuGroup.push( &RootActionSwitch );
RootMenuGroup.push( &RootActionNtp );
if( has_certs ) {
RootMenuGroup.push( &RootActionClearTls );
}
if( has_catalog ) {
RootMenuGroup.push( &RootActionClearAll );
}
RootMenuGroup.push( &RootActionSleep );
UI->setList( &RootMenuGroup );
UI->setMenuActionLabels( &RootListActionButtons );
UIDraw::drawList( true ); // render the menu
log_v("Rootmenu items count: %d (bytes free: before=%d, after=%d)", UI->getListSize(), before, ESP.getFreeHeap() );
}
void buildBrowseAppsMenu()
{
countApps();
ManageAppsGroup.clear();
ManageAppsGroup.push( &BackToRootMenu );
String jsonFile = CATALOG_DIR + Registry.defaultChannel.catalog_endpoint /*"/catalog.json"*/;
if( M5_FS.exists( jsonFile ) ) {
ManageAppsGroup.push( &BrowseAppStore ); // [Browse/Install/Hide] Available Apps
} else { // no catalog available
ManageAppsGroup.push( &RootActionRefresh ); // Download Catalog
}
if( appsCount > 0 ) {
ManageAppsGroup.push( &ManageMyApps ); // [Browse/Delete] Installed Apps
}
if( M5_FS.exists( HIDDEN_APPS_FILE ) ) {
ManageAppsGroup.push( &ManageAppStore ); // [Unhide] Available Apps
}
UI->setList( &ManageAppsGroup );
UI->setMenuActionLabels( &MyAppsActionButtons );
UIDraw::drawList( true ); // render the menu
}
void buildHiddenAppList()
{
getHiddenApps();
if( HiddenFiles.size() == 0 ) {
return;
}
HiddenAppsMenuGroup.clear();
HiddenAppsMenuGroup.push( &BackToManageApps );
for( int i=0; i<HiddenFiles.size(); i++ ) {
HiddenAppsMenuGroup.push( HiddenFiles[i].c_str(), &HiddenAppsCallbacks, nullptr, isLauncher(HiddenFiles[i].c_str())?LAUNCHER_COLOR:TEXT_COLOR );
}
UI->setList( &HiddenAppsMenuGroup );
UIDraw::drawList( true ); // render the menu
}
void buildMyAppsMenu()
{
countApps();
if( appsCount == 0 ) {
log_v("No apps on SD Card, falling back to root menu");
buildRootMenu();
}
std::vector<String> files;
getInstalledApps( files );
if( files.size() <= 0 ) {
log_v("No apps found, falling back to root menu");
buildRootMenu();
return;
}
std::sort( files.begin(), files.end() );
MyAppsMenuGroup.clear();
for( int i=0; i<files.size(); i++ ) {
if( i%7==0 ) MyAppsMenuGroup.push( &BackToManageApps );
String _ms = ""; // for macro separation
String jsonLocalFile = DIR_json + String( files[i].c_str() ) + EXT_json;
String jsonRemoteFile = CATALOG_DIR + _ms + DIR_json + String( files[i].c_str() ) + EXT_json;
if( M5_FS.exists( jsonLocalFile ) ) { // "IN REGISTRY" => compare size with meta, propose sha256_sum check or update + delete
log_v("[#%d] mb=updateCheckShowAppImage(%s)", i, files[i].c_str() );
MyAppsMenuGroup.push( files[i].c_str(), &UpdateCheckCallbacks, nullptr, isLauncher(files[i].c_str())?LAUNCHER_COLOR:TEXT_COLOR );
} else if( M5_FS.exists( jsonRemoteFile ) ) { // "BINARY INSTALLED" && "IN REGISTRY" => missing local meta ? => suggest copy meta + delete
log_v("[#%d] mb=updateMetaShowAppImage(%s)", i, files[i].c_str() );
MyAppsMenuGroup.push( files[i].c_str(), &UpdateMetaCallbacks, nullptr, isLauncher(files[i].c_str())?LAUNCHER_COLOR:TEXT_COLOR );
} else { // "BINARY INSTALLED" && "NOT IN REGISTRY" => only suggest delete
log_v("[#%d] mb=showDeleteAppImage(%s)", i, files[i].c_str() );
MyAppsMenuGroup.push( files[i].c_str(), &DeleteAppCallbacks, nullptr, isLauncher(files[i].c_str())?LAUNCHER_COLOR:TEXT_COLOR );
}
}
UI->setList( &MyAppsMenuGroup );
// TODO: focus selected item
UIDraw::drawList( true ); // render the menu
log_v("Added %d items to menu", MyAppsMenuGroup.actions_count );
}
void buildStoreMenu()
{
size_t before = ESP.getFreeHeap();
getHiddenApps(); // Fill 'HiddenFiles' vector
String jsonFile = CATALOG_DIR + Registry.defaultChannel.catalog_endpoint; // "/catalog.json"
JsonObject root;
DynamicJsonDocument jsonBuffer( 8192 );
if( !getJson( jsonFile.c_str(), root, jsonBuffer ) ) {
log_e("Failed to get json from %s", jsonFile.c_str() );
return;
}
if ( root.isNull() ) {
log_e("No parsable JSON in %s file", jsonFile.c_str() );
return;
}
if( root["apps"].size() <= 0 ) {
log_e("No apps in catalog");
return;
}
if( root["apps_count"].as<size_t>() == 0 ) { // TODO: root["generated_at"]
log_e("Empty catalog");
return;
}
if( root["base_url"].as<String>() == "" ) {
log_e("No base url");
return;
}
if( root["gz_url"].as<String>() == "" ) {
log_e("No gz url");
return;
}
baseCatalogURL = root["base_url"].as<String>();
gzCatalogURL = root["gz_url"].as<String>();
AppStoreMenuGroup.clear();
std::vector<String> files;
for( JsonVariant appEntry : root["apps"].as<JsonArray>() ) {
String fName = appEntry["name"].as<String>();
fName.trim();
if( fName != "" && !isHiddenApp(fName) && !M5_FS.exists( ROOT_DIR + fName + EXT_bin ) )
files.push_back( fName ); // don't list installed or hidden apps
}
std::sort( files.begin(), files.end() );
for( int i=0; i<files.size(); i++ ) {
if( i%7==0 ) AppStoreMenuGroup.push( &BackToManageApps );
log_v("Adding action menu %d : %s", i, files[i].c_str() );
AppStoreMenuGroup.push( files[i].c_str(), &AppStoreCallbacks, nullptr, isLauncher(files[i].c_str())?LAUNCHER_COLOR:TEXT_COLOR );
}
files.clear();
UI->setList( &AppStoreMenuGroup );
UIDraw::drawList( true ); // render the menu
log_v("Added %d apps (bytes free: before=%d, after=%d)", AppStoreMenuGroup.actions_count-1, before, ESP.getFreeHeap() );
}
};
================================================
FILE: examples/AppStore/modules/AppStoreActions/AppStoreActions.hpp
================================================
#pragma once
#include "../misc/core.h"
#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson/
namespace UIDo
{
unsigned long MsBeforeSleep = MS_BEFORE_SLEEP;
unsigned long lastcheck = millis(); // timer check
unsigned long lastpush = millis(); // keypad/keyboard activity
uint8_t brightness = MAX_BRIGHTNESS; // used for fadeout before sleep
void gotoSleep();
void clearTLS();
void clearApps();
void setRootMenu();
void setMyAppsMenu();
void setStoreMenu();
void setNtpServer();
void setTimezone( float tz );
void setDst( bool set );
void downloadCatalog();
#if !defined FS_CAN_CREATE_PATH
void doFSChecks();
#endif
void checkSleepTimer();
void deleteApp();
void deleteApp( const char* appName );
void removeHiddenApp();
void addHiddenApp();
void installApp();
void installApp( const char* appName );
void downloadCatalog();
void BtnA();
void BtnB();
void BtnC();
void idle();
void modal();
};
namespace UIDraw
{
void drawBrowseAppsMenu();
void drawStatusBar();
void drawDownloaderMenu( const char* title = nullptr, const char* body = nullptr );
void drawList( bool renderButtons = false );
void drawRegistryMenu();
};
namespace UILists
{
void buildBrowseAppsMenu();
void buildMyAppsMenu();
void buildStoreMenu();
void buildRootMenu();
void buildNtpMenu();
void buildHiddenAppList();
};
enum AppJSONType
{
JSON_LOCAL, // local json type, short version with authorName/width/height/projectURL/credits
JSON_REMOTE // remote json type, long version with meta assets
};
enum AppType
{
REG_UNKNOWN, // default when unscanned
REG_LOCAL, // in registry and installed
REG_REMOTE, // in registry but not installed
REG_HIDDEN, // in registry and hidden
NOREG // not in resistry but present
};
struct AppAsset
{
String assetFullPath; //: "/jpg/9axis_data_publisher.jpg"
String name; //: "9axis_data_publisher.jpg"
String path; //: "/jpg/"
String assetSha256Sum;
size_t size;
time_t created_at;
};
namespace AppRenderer
{
uint16_t AppInfoPosY = 46;
uint16_t AppInfoPosX = LISTITEM_OFFSETX;
uint32_t cycleid = 0;
uint32_t cbdelay = 5000; // msec cycle per callback
bool cycleanimation = false;
struct AppInfo
{
LGFX* _gfx;
String appNameStr;
String authorNameStr = NOT_IN_REGISTRY;
String projectURLStr;
String creditsStr;
String descriptionStr;
AppType type;
std::vector<AppAsset> assets;
bool has_app_image;
const char* assets_folder;
size_t binSize;
size_t packageSize;
size_t assetsCount;
time_t rawtime;
void clear();
void parseAssets( JsonObject root );
void draw();
uint32_t lastrender;
};
AppInfo appInfo;
};
namespace UIShow
{
using namespace AppRenderer;
void updateCheckShowAppImage();
void updateMetaShowAppImage();
void showDeleteAppImage();
void handleModalAction( AppInfo * appInfo );
void showAppInfo();
void scrollAppInfo();
void getAppInfo( AppInfo *appInfo, AppJSONType jsonType );
void showNTPImage();
void showAppImage();
void showAppImage( const char* prefix, const char* suffix );
void cycleAppAssets();
};
================================================
FILE: examples/AppStore/modules/AppStoreMain/AppStoreMain.cpp
================================================
/*
*
* M5Stack Application Store
* Project Page: https://github.com/tobozo/M5Stack-SD-Updater
*
* Copyright 2021 tobozo http://github.com/tobozo
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files ("M5Stack SD Updater"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#pragma once
#include "AppStoreMain.hpp"
#include "../Console/Console.cpp"
#include "../Assets/Assets.cpp"
#include "../MenuItems/MenuItems.cpp"
#include "../MenuUtils/MenuUtils.cpp"
#include "../AppStoreUI/AppStoreUI.cpp"
#include "../AppStoreActions/AppStoreActions.cpp"
#include "../FSUtils/FSUtils.cpp"
#include "../Downloader/Downloader.cpp"
#include "../Registry/Registry.cpp"
#include "../CertsManager/CertsManager.cpp"
AppStoreUI *UI = nullptr;
LogWindow *Console = nullptr;
AppRegistry Registry;
namespace AppStore
{
void setup()
{
M5.begin( true, true, true, false, ScreenShotEnable ); // bool LCDEnable, bool SDEnable, bool SerialEnable, bool I2CEnable, bool ScreenShotEnable
#if !defined FS_CAN_CREATE_PATH
UIDo::doFSChecks();
#endif
UI = new AppStoreUI( &tft );
#if !defined HAS_RTC
FSUtils::setTimeFromLastFSAccess();
#endif
checkSDUpdater( M5_FS, MENU_BIN, 5000, TFCARD_CS_PIN );
Registry = RegistryUtils::init(); // load registry profile
UI->channel_name = Registry.defaultChannel.name.c_str();
DummyAsset::setup( &UIUtils::drawCaption, &MenuItems::BrokenImage, UI->getTheme()->TextColor, UI->getTheme()->BgColor );
TLS::wget = &Downloader::wget; // attach downloader to TLS
TLS::modalConfirm = &UIUtils::modalConfirm;
TLS::certProvider = Registry.defaultChannel.api_cert_provider_url_https; // set TLS cert provider
NTP::loadPrefServer();
BackToRootMenu.textcolor = DIMMED_COLOR;
BackToManageApps.textcolor = DIMMED_COLOR;
Serial.println( WELCOME_MESSAGE );
Serial.println( INIT_MESSAGE );
Serial.printf( MENU_SETTINGS, LINES_PER_PAGE, LIST_MAX_COUNT);
Serial.printf("Has PSRam: %s\n", psramInit() ? "true" : "false");
Serial.println("Build DateTime: "+ Downloader::ISODateTime );
log_i("\nRAM SIZE:\t%s\nFREE RAM:\t%s\nMAX ALLOC:\t%s",
String( formatBytes(ESP.getHeapSize(), formatBuffer) ).c_str(),
String( formatBytes(ESP.getFreeHeap(), formatBuffer) ).c_str(),
String( formatBytes(ESP.getMaxAllocHeap(), formatBuffer) ).c_str()
);
tft.setBrightness(100);
lastcheck = millis();
FSUtils::countApps();
UILists::buildRootMenu();
}
void loop()
{
HIDSignal hidState = getControls();
if( hidState!=HID_INERT && UIDo::brightness != MAX_BRIGHTNESS ) {
// some activity occured, restore brightness
Serial.println(".. !!! Waking up !!");
UIDo::brightness = MAX_BRIGHTNESS;
tft.setBrightness( UIDo::brightness );
}
UIDo::lastcheck = millis();
switch( hidState ) {
case HID_BTN_C:
UIDo::BtnC();
break;
case HID_BTN_A:
UI->getList()->selectedindex = UI->getListID();
UIDo::BtnA();
break;
case HID_BTN_B:
UI->getList()->selectedindex = UI->getListID();
UIDo::BtnB();
break;
default:
case HID_INERT:
UIDo::lastcheck = UIDo::lastpush;
break;
case HID_SCREENSHOT:
#if defined USE_SCREENSHOT
M5.ScreenShot->snap( "screenshot" );
#endif
break;
}
UIDo::lastpush = UIDo::lastcheck;
UI->idle();
}
};
================================================
FILE: examples/AppStore/modules/AppStoreMain/AppStoreMain.hpp
================================================
/*
*
* M5Stack Application Store
* Project Page: https://github.com/tobozo/M5Stack-SD-Updater
*
* Copyright 2021 tobozo http://github.com/tobozo
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files ("M5Stack SD Updater"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#pragma once
#include "../misc/core.h" // base software stack (ESP32-Chimera-Core + SD, etc )
#include "../misc/config.h" // config settings
#include "../misc/controls.h" // keypad / joypad / keyboard controls
namespace AppStore
{
void setup();
void loop();
};
================================================
FILE: examples/AppStore/modules/AppStoreUI/AppStoreUI.cpp
================================================
#pragma once
#include "AppStoreUI.hpp"
#include "lgfx/utility/lgfx_qrcode.h"
using namespace MenuItems;
using namespace UIDraw;
using namespace UIDo;
using namespace UIUtils;
// destructor
AppStoreUI::~AppStoreUI()
{
clearList();
}
// constructor
AppStoreUI::AppStoreUI( LGFX* _gfx, UITheme * theme )
{
gfx = _gfx;
Theme = theme;
Theme->setupCanvas( gfx );
initSprites( gfx );
}
// some public getters
size_t AppStoreUI::getListPages() { return ListTotalPages; }
size_t AppStoreUI::getListSize() { return ListCount; }
uint16_t AppStoreUI::getListID() { return MenuID; }
uint16_t AppStoreUI::getPageID() { return PageID; }
const char* AppStoreUI::getListTitle() { return Menu->ActionLabels->title; }
const char* AppStoreUI::getListItemTitle() { return Menu->Actions[MenuID]->title; }
MenuActionLabels* AppStoreUI::getMenuActionLabels() { return Menu->ActionLabels; }
UITheme* AppStoreUI::getTheme() { return Theme; }
LGFX* AppStoreUI::getGfx() { return gfx; }
bool AppStoreUI::empty( const char* str )
{
return str ? (str[0] == '\0') : true;
}
void AppStoreUI::clearList()
{
PageID = 0;
MenuID = 0;
ListCount = 0;
ListTotalPages = 0;
ListLinesInLastPage = 0;
}
void AppStoreUI::setPageID( uint16_t idx )
{
if( idx < ListCount/LinesPerPage ) {
PageID = idx;
}
}
void AppStoreUI::setListID( uint16_t idx )
{
uint16_t was = PageID;
if( idx < ListCount ) {
MenuID = idx;
} else {
MenuID = ListCount -1;
}
PageID = MenuID / LinesPerPage;
log_v("Setting list id as %d (was %d)", idx, was );
}
void AppStoreUI::nextList( bool renderAfter )
{
if( MenuID < ( PageID * LinesPerPage + LinesInCurrentPage - 1 ) ) {
MenuID++;
} else {
if( PageID<ListTotalPages - 1 ) {
PageID++;
} else {
PageID = 0;
}
MenuID = PageID * LinesPerPage;
}
log_v("Next to %d/%d", PageID, MenuID);
if( renderAfter ) showList();
}
void AppStoreUI::pageDown( bool renderAfter )
{
if( PageID < ListTotalPages -1 ) {
PageID++;
MenuID = (PageID * LinesPerPage) -1;
nextList( false );
} else {
PageID = 0;
MenuID = 0;
}
log_v("Paging down to %d/%d", PageID, MenuID);
if( renderAfter ) showList();
}
void AppStoreUI::pageUp( bool renderAfter )
{
if( PageID > 0 ) {
PageID--;
MenuID -= LinesPerPage;
log_v("Paging up to %d/%d", PageID, MenuID);
if( renderAfter ) showList();
}
}
void AppStoreUI::menuDown( bool renderAfter )
{
int16_t lastId = MenuID;
int16_t oldPageID = PageID;
if( MenuID == ListCount-1 ) {
setListID( 0 );
} else {
setListID( MenuID+1 );
}
if( PageID != oldPageID ) {
if( renderAfter ) showList();
} else {
if( renderAfter ) updateList( lastId );
}
}
void AppStoreUI::menuUp( bool renderAfter )
{
int16_t oldPageID = PageID;
int16_t lastId = MenuID;
if( MenuID == 0 ) {
// jump to end
setListID( ListCount-1 );
} else {
setListID( MenuID-1 );
}
if( PageID != oldPageID ) {
if( renderAfter ) showList();
} else {
if( renderAfter ) updateList( lastId );
}
log_v("Menu up to %d/%d", PageID, MenuID);
}
void AppStoreUI::setList( MenuGroup* menu )
{
if( menu->actions_count == 0 ) {
log_e("Cowardly refusing to insert an empty menu list for collection %s, aborting", menu->Title );
return;
}
clearList();
ListCount = menu->actions_count;
if( ListCount>0 ) {
if( ListCount > LinesPerPage ) {
ListLinesInLastPage = ListCount % LinesPerPage;
if( ListLinesInLastPage>0 ) {
ListTotalPages = ( ListCount - ListLinesInLastPage ) / LinesPerPage;
ListTotalPages++;
} else {
ListTotalPages = ListCount / LinesPerPage;
}
} else {
ListTotalPages = 1;
}
}
// if( Menu && menu->Parent != nullptr ) menu->Parent = Menu;
Menu = menu;
if( Menu->selectedindex > 0 ) {
setListID( Menu->selectedindex );
}
log_v("Added list '%s' with %d elements", menu->Title, menu->actions_count );
}
void AppStoreUI::execBtn( uint8_t bnum )
{
if( Menu->ActionLabels && Menu->ActionLabels->Buttons && Menu->ActionLabels->Buttons[bnum] && Menu->ActionLabels->Buttons[bnum]->onClick ) {
log_v("Button #%d Inherited Callback for Item '%s' in menu '%s' (#%d / %d)", bnum, getListItemTitle(), getListTitle(), MenuID, Menu->actions_count );
Menu->ActionLabels->Buttons[bnum]->onClick();
} else {
log_e("Button #%d MISSED Callback for Item #%d / %d", bnum, MenuID, Menu->actions_count );
}
}
void AppStoreUI::execBtnA()
{
if( Menu && MenuID < Menu->actions_count ) {
if( Menu->Actions[MenuID]->callbacks && Menu->Actions[MenuID]->callbacks->onSelect ) {
log_v("ButtonA Callback for Item '%s' (#%d / %d)", getListItemTitle(), MenuID, Menu->actions_count );
Menu->Actions[MenuID]->callbacks->onSelect();
} else {
execBtn( 0 );
}
} else {
log_e("No menu or bad menu range( %d ) to exec from", MenuID );
}
}
void AppStoreUI::execBtnB()
{
execBtn( 1 );
}
void AppStoreUI::execBtnC()
{
execBtn( 2 );
}
void AppStoreUI::idle()
{
checkSleepTimer();
if( Menu->Actions[MenuID]->callbacks && Menu->Actions[MenuID]->callbacks->onIdle ) {
Menu->Actions[MenuID]->callbacks->onIdle();
}
}
void AppStoreUI::modal()
{
checkSleepTimer();
if( Menu->Actions[MenuID]->callbacks && Menu->Actions[MenuID]->callbacks->onModal ) {
Menu->Actions[MenuID]->callbacks->onModal();
}
}
void AppStoreUI::buttonsClr()
{
gfx->fillRect( Theme->WinPosX, Theme->ButtonsPosY, Theme->WinWidth, TITLEBAR_HEIGHT, Theme->BgColor );
}
void AppStoreUI::windowClr( uint32_t fillcolor )
{
gfx->fillRect( Theme->WinPosX, Theme->WinPosY, Theme->WinWidth, Theme->WinHeight, fillcolor );
}
void AppStoreUI::windowClr()
{
uint32_t begin = millis();
if( !getGradientLine() ) {
log_d("Failed to create sprite (%d x %d ), will fill with single color", Theme->WinWidth, 1 );
gfx->fillScreen( Theme->MenuColor );
return;
}
gfx->startWrite();
gfx->pushImageRotateZoom( gfx->width()/2, gfx->height()/2, Theme->WinWidth/2, 0, 0.0, 1, Theme->WinHeight, GradienSprite->width(), GradienSprite->height(), (uint16_t*)GradienSprite->getBuffer() );
GradienSprite->deleteSprite();
cropRoundRect( gfx, Theme->WinPosX, Theme->WinPosY, Theme->WinWidth, Theme->WinHeight, 5, Theme->BgColor );
gfx->endWrite();
log_v("clear took %d ms", millis()-begin ); // avg 179ms
}
void AppStoreUI::drawListItem( uint16_t inIDX, uint16_t posY )
{
if( inIDX==MenuID ) {
drawTextShadow( gfx, Menu->Actions[inIDX]->title, Theme->LIOffsetX, Theme->LIOffsetY+(posY*Theme->LIHeight), Menu->Actions[inIDX]->textcolor, Theme->TextShadowColor, LIFont, TL_DATUM );
drawCheckMark( gfx, 4, Theme->LIOffsetY+(posY*Theme->LIHeight)+3, Theme->arrowWidth, LIFontHeight/2, Menu->Actions[inIDX]->textcolor, Theme->TextShadowColor );
} else {
drawTextShadow( gfx, Menu->Actions[inIDX]->title, Theme->LIOffsetX, Theme->LIOffsetY+(posY*Theme->LIHeight), Menu->Actions[inIDX]->textcolor, Theme->TextShadowColor, LIFont, TL_DATUM );
}
}
void AppStoreUI::updateList( uint16_t clearid )
{
if( !getGradientLine() ) { // can't blit
showList();
return;
}
uint16_t prevPosY = Theme->LIOffsetY+((clearid%LinesPerPage)*Theme->LIHeight)+3; // last arrow position
uint16_t currPosY = Theme->LIOffsetY+((MenuID%LinesPerPage)*Theme->LIHeight)+3; // current arrow position
uint16_t clipPosX = 4;
uint16_t clipWidth = 1+Theme->arrowWidth;
uint16_t clipHeight = 1+LIFontHeight/2;
uint16_t *sprPtr = (uint16_t*)GradienSprite->getBuffer();
uint16_t *clipPtr = &sprPtr[clipPosX];
bool clear_asset = false;
// delete old checkmark
gfx->pushImageRotateZoom( clipPosX+clipWidth/2, prevPosY+clipHeight/2, clipWidth/2, 0, 0.0, 1, clipHeight+1, clipWidth+1, GradienSprite->height()+1, clipPtr );
// draw new checkmark
drawCheckMark( gfx, 4, currPosY, Theme->arrowWidth, LIFontHeight/2, Menu->Actions[MenuID]->textcolor, Theme->TextShadowColor );
GradienSprite->deleteSprite();
callShowListHooks();
}
void AppStoreUI::showList()
{
windowClr();
uint16_t i, items = 0;
gfx->startWrite();
if( ListTotalPages > 1 ) {
snprintf(paginationStr, 16, paginationTpl, PageID+1, ListTotalPages );
drawTextShadow( gfx, paginationStr, Theme->LICaptionPosX, Theme->LICaptionPosY, Theme->TextColor, Theme->TextShadowColor, &Font2, TR_DATUM );
snprintf(paginationStr, 16, totalCountTpl, ListCount );
drawTextShadow( gfx, paginationStr, Theme->LICaptionPosX, gfx->height()-(Theme->LICaptionPosY-3), Theme->TextColor, Theme->TextShadowColor, &Font2, BR_DATUM );
}
if( (PageID + 1) == ListTotalPages ) { // in last page
if( ListLinesInLastPage == 0 and ListCount >= LinesPerPage ) {
LinesInCurrentPage = LinesPerPage;
items = LinesPerPage;
} else {
if( ListTotalPages>1 ) {
LinesInCurrentPage = ListLinesInLastPage;
items = ListLinesInLastPage;
} else {
LinesInCurrentPage = ListCount;
items = ListCount;
}
}
} else { // in first page or paginaged
LinesInCurrentPage = LinesPerPage;
items = LinesPerPage;
}
for( i = 0; i<items; i++ ) {
drawListItem( i+(PageID*LinesPerPage), i );
}
gfx->endWrite();
callShowListHooks();
}
void AppStoreUI::callShowListHooks()
{
if( Menu->Actions[MenuID]->callbacks && Menu->Actions[MenuID]->callbacks->onRender ) {
log_v("Side Callback for MenuItem '%s' (item #%d)", Menu->Actions[MenuID]->title, MenuID );
Menu->Actions[MenuID]->callbacks->onRender();
}
if( Menu->Actions[MenuID]->icon ) {
log_v("Icon attached: %s", Menu->Actions[MenuID]->icon->path );
RemoteAsset* asset = (RemoteAsset*)Menu->Actions[MenuID]->icon;
drawAssetReveal( asset, gfx, Theme->assetPosX, Theme->assetPosY );
}
}
void AppStoreUI::setMenuActionLabels( MenuActionLabels* _ActionLabels )
{
log_v("Overwriting Menu ActionLabels '%s' with '%s'", Menu->ActionLabels->title, _ActionLabels->title );
Menu->ActionLabels = _ActionLabels;
}
void AppStoreUI::drawButton( uint8_t bnum )
{
if( Menu && Menu->ActionLabels && Menu->ActionLabels->Buttons && Menu->ActionLabels->Buttons[bnum] ) {
ButtonAction* btn = Menu->ActionLabels->Buttons[bnum];
ButtonSprite->pushSprite( buttonsXOffset[bnum], Theme->ButtonsPosY );
if( btn->asset && M5_FS.exists( btn->asset->path ) ) {
RemoteAsset *btnIcon = btn->asset;
btnIcon->draw( gfx, buttonsXOffset[bnum]+BUTTON_HWIDTH - btnIcon->width/2, Theme->ButtonsPosY+BUTTON_HEIGHT/2 - btnIcon->height/2 );
} else if( btn->title && !empty( btn->title ) ) {
drawTextShadow( gfx, btn->title, buttonsXOffset[bnum]+BUTTON_HWIDTH, Theme->ButtonsPosY+BUTTON_HEIGHT/2, Theme->TextColor, Theme->TextShadowColor, ButtonFont, MC_DATUM );
} else {
// empty button wut ?
}
} else {
gfx->fillRect( buttonsXOffset[bnum], Theme->ButtonsPosY, BUTTON_WIDTH, BUTTON_HEIGHT, Theme->BgColor );
}
}
void AppStoreUI::drawButtons()
{
createButtonMask();
for( int i=0;i<BUTTONS_COUNT;i++ ) {
drawButton(i);
}
clearButtonMask();
}
void AppStoreUI::drawMenu( bool clearWindow )
{
// header background fill
gfx->startWrite();
if( !getGradientLine( true ) ) {
gfx->fillRect( 0, 0, Theme->WinWidth, TITLEBAR_HEIGHT-2, Theme->MenuColor );
} else {
for( int i=0;i<TITLEBAR_HEIGHT-2; i++ ) {
GradienSprite->pushSprite( 0, i );
}
GradienSprite->deleteSprite();
}
// header text
drawTextShadow( gfx, Menu->Title, Theme->WinWidth/2, (TITLEBAR_HEIGHT-2)/2, Theme->TextColor, Theme->TextShadowColor, HeaderFont, MC_DATUM );
cropRoundRect( gfx, 0, 0, Theme->WinWidth, TITLEBAR_HEIGHT-2, 3, Theme->BgColor );
gfx->endWrite();
// window
if( clearWindow ) windowClr();
// buttons
drawButtons();
}
void UITheme::setupCanvas( LGFX* gfx )
{
fontHeightFM9p7 = gfx->fontHeight(InfoWindowFont);
LIFontHeight = gfx->fontHeight(LIFont);
fontHeight0 = gfx->fontHeight(&Font0);
arrowWidth = gfx->textWidth(">");
ButtonsPosY = gfx->height()-BUTTON_HEIGHT;
WinHeight = gfx->height()-TITLEBAR_HEIGHT*2;
WinWidth = gfx->width();
WinPosY = TITLEBAR_HEIGHT+1;
WinPosX = 0;
WinMargin = WINDOW_MARGINX;
LIHeight = LISTITEM_HEIGHT;
LIOffsetY = LISTITEM_OFFSETY; // pixels offset from top for list items
LIOffsetX = LISTITEM_OFFSETX; // pixels offset from left for list items
LICaptionPosX = WinWidth-WinMargin; // text cursor position-x for list caption
LICaptionPosY = LISTCAPTION_POSY; // text cursor position-Y for list caption
assetPosY = ASSET_POSY; // assets position
assetPosX = WinWidth-(assetWidth+WinMargin); // assets positionx
pgW = gfx->width()-LISTITEM_OFFSETX*2; // progress bar width
pgX = gfx->width()/2 - pgW/2; // pogress bar posx, based on width
BgColor = BG_COLOR;
MenuColor = MENU_COLOR;
TextColor = TEXT_COLOR;
TextShadowColor = SHADOW_COLOR;
gzProgressBar.setup( gfx, pgX, 106, pgW, 10, GZ_PROGRESS_COLOR, MenuColor, true ); // gzip
tarProgressBar.setup( gfx, pgX, 152, pgW, 5, TAR_PROGRESS_COLOR, MenuColor, true ); // tar
dlProgressBar.setup( gfx, pgX+1, 188, pgW-2, 4, DL_PROGRESS_COLOR, MenuColor, false ); // download/sha sum
createGradients();
}
void UITheme::createGradients()
{
uint8_t r = (MenuColor >> 16) & 0xff; // red
uint8_t g = (MenuColor >> 8) & 0xff; // green
uint8_t b = MenuColor & 0xff; // blue
float sf = 0.2; // shade factor
float tf = 0.2; // tint factor
TintedMenuColor = ( uint8_t(r + (255 - r) * tf) << 16 ) + ( uint8_t(g + (255 - g) * tf) << 8 ) + ( uint8_t(b + (255 - b) * tf) ) ;
ShadedMenuColor = ( uint8_t(r * (1 - sf)) << 16 ) + ( uint8_t(g * (1 - sf)) << 8 ) + ( uint8_t(b * (1 - sf)) ) ;
}
void ProgressBarTheme::clear()
{
gfx->fillRect( x, y, w, caption?h+fontHeight0+2:h, bg );
}
void ProgressBarTheme::progress( uint8_t progress )
{
uint16_t th = fontHeight0+2;
uint16_t ty = y+h+2;
drawProgressBar( gfx, x, y, w, h, progress, fg, bg );
if( caption ) {
String progressStr = " " + String(progress)+"% ";
drawCaption( gfx, progressStr.c_str(), x, ty, w, th, &Font0, TC_DATUM, fg, bg );
}
}
void ProgressBarTheme::setup(LGFX* _gfx, uint16_t _x, uint16_t _y, uint16_t _w, uint16_t _h, uint32_t _fg, uint32_t _bg, bool _cap )
{
gfx = _gfx;
x = _x;
y = _y;
w = _w;
h = _h;
fg = _fg;
bg = _bg;
caption = _cap;
}
namespace UIUtils
{
using namespace MenuItems;
using namespace AppRenderer;
void initSprites( LGFX* gfx )
{
appInfo._gfx = gfx;
GradienSprite = new LGFX_Sprite( gfx );
GradienSprite->setColorDepth(16);
ButtonSprite = new LGFX_Sprite( gfx );
ButtonSprite->setColorDepth(16);
MaskSprite = new LGFX_Sprite( gfx );
MaskSprite->setColorDepth(16);
AssetSprite = new LGFX_Sprite( gfx );
AssetSprite->setColorDepth(16);
ScrollSprite = new LGFX_Sprite( gfx );
ScrollSprite->setColorDepth(1);
}
void drawProgressBar( LGFX* gfx, int x, int y, int w, int h, uint8_t val, uint32_t color, uint32_t bgcolor )
{
gfx->drawRect(x, y, w, h, color);
if( val>100) val = 100;
if( val==0 ) {
gfx->fillRect(x + 1, y + 1, w-2, h - 2, bgcolor);
} else {
int fillw = (w * (((float)val) / 100.0)) -2;
gfx->fillRect(x + 1, y + 1, fillw-2, h - 2, color);
gfx->fillRect(x + fillw + 1, y + 1, w-fillw-2, h - 2, bgcolor);
}
}
void gzProgressCallback( uint8_t progress )
{
static int8_t gzLibLastProgress = -1;
if( gzLibLastProgress != progress ) {
gzLibLastProgress = progress;
UI->getTheme()->gzProgressBar.progress( progress );
}
}
void tarProgressCallback( uint8_t progress )
{
static int8_t tarLibLastProgress = -1;
if( tarLibLastProgress != progress ) {
tarLibLastProgress = progress;
UI->getTheme()->tarProgressBar.progress( progress );
}
}
void tarStatusCallback( const char* name, size_t size, size_t total_unpacked )
{
log_d("[TAR] %-64s %8d bytes - %8d Total bytes", name, size, total_unpacked );
size_t th_small = fontHeight0*1.3;
size_t th_big = fontHeightFM9p7*1.3;
uint16_t posy = 162; // bottom text position (file size, overall size)
static bool clean;
UITheme* theme = UI->getTheme();
LGFX* gfx = UI->getGfx();
if( clean != true ) {
clean = true;
UI->windowClr( theme->MenuColor );
drawInfoWindow( TAR_PROGRESS_TITLE );
}
// TODO: don't redraw OVERALL_PROGRESS_TITLE on every callback
drawCaption( gfx, OVERALL_PROGRESS_TITLE, theme->gzProgressBar.x, theme->gzProgressBar.y - (th_big+2), theme->gzProgressBar.w, th_big, InfoWindowFont, MC_DATUM );
drawCaption( gfx, name, WINDOW_MARGINX, theme->tarProgressBar.y - (th_small+2), gfx->width()-WINDOW_MARGINX*2, th_small, &Font0, MC_DATUM );
drawCaption( gfx, String( formatBytes( size, formatBuffer ) ).c_str(), WINDOW_MARGINX, posy+th_big, gfx->width()/3-WINDOW_MARGINX, th_big, InfoWindowFont, TL_DATUM );
const char* caption = String( "Total: " + String( formatBytes( total_unpacked, formatBuffer ) ) ).c_str();
drawCaption( gfx, caption, gfx->width()/3, posy+th_big, gfx->width()*2/3-WINDOW_MARGINX, th_big, InfoWindowFont, TR_DATUM );
}
bool getGradientLine( bool invert )
{
uint32_t width = UI->getGfx()->width();
if( !GradienSprite->createSprite( width, 1 ) ) {
log_d("Failed to create sprite (%d x %d ), will fill with single color", width, 1 );
return false;
}
UITheme* theme = UI->getTheme();
GradienSprite->drawGradientHLine( 0, 0, width, invert?theme->TintedMenuColor:theme->ShadedMenuColor, invert?theme->ShadedMenuColor:theme->TintedMenuColor );
return true;
}
void createButtonMask()
{
if( !ButtonSprite->createSprite( BUTTON_WIDTH, BUTTON_HEIGHT ) ) {
ButtonSprite->setColorDepth( ButtonSprite->getColorDepth()/2 || 1 );
log_d("Failed to create Button Sprite (%d x %d )", BUTTON_WIDTH, 1 );
return;
}
UITheme* theme = UI->getTheme();
// fill with gradient
for( int i=0; i<BUTTON_HEIGHT; i++ ) {
ButtonSprite->drawGradientHLine( 0, i, BUTTON_WIDTH, theme->TintedMenuColor, theme->ShadedMenuColor );
}
// make a button skin
MaskSprite->createSprite( BUTTON_WIDTH, BUTTON_HEIGHT );
// colors will be cut diagonally, prepare coord for triangles
uint16_t x1 = 0 , y1 = 0;
uint16_t x2 = 0 , y2 = BUTTON_HEIGHT-1;
uint16_t x3 = BUTTON_WIDTH-1, y3 = 0;
uint16_t x4 = BUTTON_WIDTH-1, y4 = BUTTON_HEIGHT-1;
// apply both colors
for( int i=-1; i<1; i++ ) {
drawButtonMask( (LGFX*)MaskSprite, 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, 3, 3, theme->BgColor, i?theme->ShadedMenuColor:theme->TintedMenuColor );
MaskSprite->fillTriangle( x2, y2, i?x4:x1, i?y4:y1, x3, y3, theme->BgColor );
MaskSprite->pushSprite( ButtonSprite, 0, 0, theme->BgColor );
}
MaskSprite->deleteSprite();
// apply rounded borders
cropRoundRect( (LGFX*)ButtonSprite, 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, 5, theme->BgColor );
}
void drawButtonMask( LGFX* gfx, int32_t x, int32_t y, uint32_t w, uint32_t h, uint8_t radius, uint8_t thickness, uint32_t fillcolor, uint32_t bgcolor )
{
gfx->fillRect( x, y, w, h, fillcolor );
for( int i=0; i<thickness;i++ ) {
gfx->drawRoundRect( x+i, y+i, w-i*2, h-i*2, radius+i, bgcolor );
}
}
void clearButtonMask()
{
ButtonSprite->deleteSprite();
}
void drawCheckMark( LGFX* gfx, uint16_t gx, uint16_t gy, uint16_t gw, uint16_t gh, uint32_t textcolor, uint32_t shadowcolor, int8_t dirx, int8_t diry )
{
gfx->fillTriangle( dirx+gx, diry+gy, dirx+gx, diry+gy+gh, dirx+gx+gw, diry+gy+gh/2, shadowcolor );
gfx->fillTriangle( gx, gy, gx, gy+gh, gx+gw, gy+gh/2, textcolor );
}
void drawTextShadow( LGFX* gfx, const char*str, int32_t x, int32_t y, uint32_t textcolor, uint32_t shadowcolor, const lgfx::v1::IFont* font, textdatum_t datum, int8_t dirx, int8_t diry )
{
gfx->setTextDatum( datum );
gfx->setFont( font );
gfx->setTextColor( shadowcolor );
gfx->drawString( str, dirx+x, diry+y );
gitextract_couysrsq/
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── scripts/
│ │ └── semver.sh
│ ├── tools/
│ │ ├── SD-Apps/
│ │ │ ├── after_deploy.sh
│ │ │ ├── before_deploy.sh
│ │ │ ├── before_install.sh
│ │ │ ├── functions.sh
│ │ │ ├── gen-apps.sh
│ │ │ ├── gen-menu.sh
│ │ │ ├── get-deps.sh
│ │ │ ├── get-precompiled.sh
│ │ │ ├── install.sh
│ │ │ ├── install_master.sh
│ │ │ ├── install_unstable.sh
│ │ │ └── script.sh
│ │ ├── common.sh
│ │ ├── cron.sh
│ │ ├── deploy.sh
│ │ ├── git-describe.awk
│ │ ├── hooker.php
│ │ ├── ino2fw.sh
│ │ └── mkfw-build.sh
│ └── workflows/
│ ├── ArduinoBuild.yml
│ ├── PlatformioBuild.yml
│ ├── cron.yml
│ └── onrelease.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── LICENSE
├── README.md
├── component.mk
├── examples/
│ ├── AppStore/
│ │ ├── AppStore.ino
│ │ ├── main/
│ │ │ └── main.cpp
│ │ ├── modules/
│ │ │ ├── AppStoreActions/
│ │ │ │ ├── AppStoreActions.cpp
│ │ │ │ └── AppStoreActions.hpp
│ │ │ ├── AppStoreMain/
│ │ │ │ ├── AppStoreMain.cpp
│ │ │ │ └── AppStoreMain.hpp
│ │ │ ├── AppStoreUI/
│ │ │ │ ├── AppStoreUI.cpp
│ │ │ │ └── AppStoreUI.hpp
│ │ │ ├── Assets/
│ │ │ │ ├── Assets.cpp
│ │ │ │ ├── Assets.h
│ │ │ │ └── Assets.hpp
│ │ │ ├── CertsManager/
│ │ │ │ ├── CertsManager.cpp
│ │ │ │ └── CertsManager.hpp
│ │ │ ├── Console/
│ │ │ │ ├── Console.cpp
│ │ │ │ └── Console.hpp
│ │ │ ├── Downloader/
│ │ │ │ ├── Downloader.cpp
│ │ │ │ └── Downloader.hpp
│ │ │ ├── FSUtils/
│ │ │ │ ├── FSUtils.cpp
│ │ │ │ └── FSUtils.hpp
│ │ │ ├── MenuItems/
│ │ │ │ ├── MenuItems.cpp
│ │ │ │ └── MenuItems.hpp
│ │ │ ├── MenuUtils/
│ │ │ │ ├── MenuUtils.cpp
│ │ │ │ └── MenuUtils.hpp
│ │ │ ├── Registry/
│ │ │ │ ├── Registry.cpp
│ │ │ │ └── Registry.hpp
│ │ │ └── misc/
│ │ │ ├── compile_time.h
│ │ │ ├── config.h
│ │ │ ├── controls.h
│ │ │ ├── core.h
│ │ │ ├── i18n.h
│ │ │ └── lang/
│ │ │ ├── i18n.cn.h
│ │ │ ├── i18n.en.h
│ │ │ ├── i18n.jp.h
│ │ │ └── i18n.kr.h
│ │ └── platformio.ini
│ ├── CopySketchToFS/
│ │ └── CopySketchToFS.ino
│ ├── FactoryLauncher/
│ │ ├── FactoryLauncher.ino
│ │ └── partitions.csv
│ ├── GzLauncher/
│ │ └── GzLauncher.ino
│ ├── Headless/
│ │ └── Headless.ino
│ ├── LGFX-SDLoader-Snippet/
│ │ ├── LGFX-SDLoader-Snippet.ino
│ │ └── M5Stack_Buttons.h
│ ├── M5Core2-SDLoader-Snippet/
│ │ └── M5Core2-SDLoader-Snippet.ino
│ ├── M5Stack-FW-Menu/
│ │ ├── M5Stack-FW-Menu.ino
│ │ ├── ReadMe.md
│ │ ├── partitions/
│ │ │ ├── partitions-16MB-factory-4-apps.csv
│ │ │ └── partitions-16MB-factory-6-apps.csv
│ │ ├── platformio.ini
│ │ └── src/
│ │ └── main.cpp
│ ├── M5Stack-LittleFS-Snippet/
│ │ └── M5Stack-LittleFS-Snippet.ino
│ ├── M5Stack-SD-Menu/
│ │ ├── M5Stack-SD-Menu.ino
│ │ ├── SAM.h
│ │ ├── SD-Content/
│ │ │ └── json/
│ │ │ ├── Colours_Demo.json
│ │ │ ├── CrackScreen.json
│ │ │ ├── Downloader.json
│ │ │ ├── FlappyBird.json
│ │ │ ├── Lora_Frequency_Hopping.json
│ │ │ ├── LovyanLauncher.json
│ │ │ ├── MultiApps-Adv.json
│ │ │ ├── NyanCat.json
│ │ │ ├── NyanCat_Ext.json
│ │ │ ├── Oscilloscope.json
│ │ │ ├── PacketMonitor.json
│ │ │ ├── Pacman-JoyPSP.json
│ │ │ ├── Pixel-Fun.json
│ │ │ ├── Raytracer.json
│ │ │ ├── Rickroll.json
│ │ │ ├── RotateyCube.json
│ │ │ ├── SWRasterizer.json
│ │ │ ├── Sokoban.json
│ │ │ ├── SpaceDefense.json
│ │ │ ├── SpaceShooter.json
│ │ │ ├── Tetris.json
│ │ │ ├── Thermal-Camera.json
│ │ │ ├── TobozoLauncher.json
│ │ │ ├── Tube.json
│ │ │ ├── WiFiScanner.json
│ │ │ ├── arduinomegachess.json
│ │ │ ├── d_invader.json
│ │ │ ├── drawNumber.json
│ │ │ ├── menu.json
│ │ │ └── mp3-player.json
│ │ ├── assets/
│ │ │ └── workflow.dia
│ │ ├── assets.h
│ │ ├── certificates.h
│ │ ├── compile_time.h
│ │ ├── controls.h
│ │ ├── core.h
│ │ ├── downloader.h
│ │ ├── fsformat.h
│ │ ├── i18n.h
│ │ ├── main/
│ │ │ └── main.cpp
│ │ ├── menu.h
│ │ ├── partition_manager.h
│ │ ├── partitions_16MB_4_or_6_apps.csv
│ │ ├── partitions_16MB_8apps.csv
│ │ ├── platformio.ini
│ │ ├── registry.default.h
│ │ ├── registry.h
│ │ └── wifi_manager.h
│ ├── M5Stack-SDLoader-Snippet/
│ │ └── M5Stack-SDLoader-Snippet.ino
│ ├── M5StickC-SPIFFS-Loader-Snippet/
│ │ └── M5StickC-SPIFFS-Loader-Snippet.ino
│ ├── M5Unified/
│ │ └── M5Unified.ino
│ ├── MultiFS/
│ │ └── MultiFS.ino
│ ├── SdFatUpdater/
│ │ └── SdFatUpdater.ino
│ └── Test/
│ └── build_test/
│ ├── dev_lib_deps.ini
│ ├── main/
│ │ └── main.cpp
│ └── platformio.ini
├── library.json
├── library.properties
└── src/
├── ConfigManager/
│ ├── ConfigManager.cpp
│ └── ConfigManager.hpp
├── FS/
│ ├── ffat.hpp
│ ├── littlefs.hpp
│ ├── sd.hpp
│ ├── sd_mmc.hpp
│ ├── sdfat.hpp
│ └── spiffs.hpp
├── M5StackUpdater.h
├── M5StackUpdater.hpp
├── PartitionManager/
│ ├── NVS/
│ │ ├── NVSUtils.cpp
│ │ └── NVSUtils.hpp
│ ├── PartitionManager.cpp
│ ├── PartitionManager.hpp
│ └── Partitions/
│ ├── PartitionUtils.cpp
│ └── PartitionUtils.hpp
├── SDUpdater/
│ ├── SDUpdater_Class.cpp
│ ├── SDUpdater_Class.hpp
│ └── Update_Interface.hpp
├── UI/
│ ├── Touch.hpp
│ ├── UI.hpp
│ └── common.hpp
├── gitTagVersion.h
└── misc/
├── assets.h
├── config.h
└── types.h
SYMBOL INDEX (571 symbols across 57 files)
FILE: .github/tools/hooker.php
function usage (line 19) | function usage( $msg ) {
FILE: examples/AppStore/main/main.cpp
function setup (line 5) | void setup()
function loop (line 11) | void loop()
function loopTask (line 18) | void loopTask(void*)
function app_main (line 25) | void app_main()
FILE: examples/AppStore/modules/AppStoreActions/AppStoreActions.cpp
type UIDo (line 14) | namespace UIDo
function BtnA (line 22) | void BtnA()
function BtnB (line 28) | void BtnB()
function BtnC (line 35) | void BtnC()
function checkSleepTimer (line 42) | void checkSleepTimer()
function gotoSleep (line 65) | void gotoSleep()
function clearTLS (line 85) | void clearTLS()
function clearApps (line 96) | void clearApps()
function setNtpServer (line 115) | void setNtpServer()
function installApp (line 125) | void installApp( const char* appName )
function installApp (line 133) | void installApp()
function deleteApp (line 140) | void deleteApp( const char* appName )
function deleteApp (line 146) | void deleteApp()
function removeHiddenApp (line 151) | void removeHiddenApp()
function addHiddenApp (line 159) | void addHiddenApp()
function downloadCatalog (line 168) | void downloadCatalog()
function idle (line 181) | void idle()
function modal (line 186) | void modal()
function doFSChecks (line 193) | void doFSChecks()
type UIDraw (line 205) | namespace UIDraw
function drawDownloaderMenu (line 213) | void drawDownloaderMenu( const char* title, const char* body )
function drawList (line 225) | void drawList( bool renderButtons )
function drawStatusBar (line 235) | void drawStatusBar()
function drawRegistryMenu (line 255) | void drawRegistryMenu()
type AppRenderer (line 300) | namespace AppRenderer
type tm (line 405) | struct tm
type UIShow (line 427) | namespace UIShow
function showAppQrCode (line 438) | void showAppQrCode()
function showAppAssetImage (line 445) | void showAppAssetImage()
function showAppAssetAuthor (line 451) | void showAppAssetAuthor()
function showNTPImage (line 457) | void showNTPImage()
function updateCheckShowAppImage (line 470) | void updateCheckShowAppImage()
function updateMetaShowAppImage (line 489) | void updateMetaShowAppImage()
function showDeleteAppImage (line 499) | void showDeleteAppImage()
function showAppImage (line 522) | void showAppImage()
function showAppImage (line 535) | void showAppImage( const char* prefix, const char* suffix )
function getAppInfo (line 550) | void getAppInfo( AppInfo *appInfo, AppJSONType jsonType )
function handleModalAction (line 629) | void handleModalAction( AppInfo * appInfo )
function showAppInfo (line 675) | void showAppInfo()
function scrollAppInfo (line 682) | void scrollAppInfo()
function cycleAppAssets (line 693) | void cycleAppAssets()
type UILists (line 722) | namespace UILists
function buildNtpMenu (line 730) | void buildNtpMenu()
function buildRootMenu (line 748) | void buildRootMenu()
function buildBrowseAppsMenu (line 790) | void buildBrowseAppsMenu()
function buildHiddenAppList (line 814) | void buildHiddenAppList()
function buildMyAppsMenu (line 832) | void buildMyAppsMenu()
function buildStoreMenu (line 878) | void buildStoreMenu()
FILE: examples/AppStore/modules/AppStoreActions/AppStoreActions.hpp
type UIDo (line 6) | namespace UIDo
type UIDraw (line 41) | namespace UIDraw
type UILists (line 50) | namespace UILists
type AppJSONType (line 60) | enum AppJSONType
type AppType (line 66) | enum AppType
type AppAsset (line 75) | struct AppAsset
type AppRenderer (line 85) | namespace AppRenderer
type AppInfo (line 93) | struct AppInfo
type UIShow (line 118) | namespace UIShow
FILE: examples/AppStore/modules/AppStoreMain/AppStoreMain.cpp
type AppStore (line 49) | namespace AppStore
function setup (line 52) | void setup()
function loop (line 102) | void loop()
FILE: examples/AppStore/modules/AppStoreMain/AppStoreMain.hpp
type AppStore (line 37) | namespace AppStore
FILE: examples/AppStore/modules/AppStoreUI/AppStoreUI.cpp
function MenuActionLabels (line 33) | MenuActionLabels* AppStoreUI::getMenuActionLabels() { return Menu->Actio...
function UITheme (line 34) | UITheme* AppStoreUI::getTheme() { return Theme; }
function LGFX (line 35) | LGFX* AppStoreUI::getGfx() { return gfx; }
type UIUtils (line 502) | namespace UIUtils
function initSprites (line 508) | void initSprites( LGFX* gfx )
function drawProgressBar (line 529) | void drawProgressBar( LGFX* gfx, int x, int y, int w, int h, uint8_t v...
function gzProgressCallback (line 543) | void gzProgressCallback( uint8_t progress )
function tarProgressCallback (line 553) | void tarProgressCallback( uint8_t progress )
function tarStatusCallback (line 563) | void tarStatusCallback( const char* name, size_t size, size_t total_un...
function getGradientLine (line 587) | bool getGradientLine( bool invert )
function createButtonMask (line 600) | void createButtonMask()
function drawButtonMask (line 631) | void drawButtonMask( LGFX* gfx, int32_t x, int32_t y, uint32_t w, uint...
function clearButtonMask (line 640) | void clearButtonMask()
function drawCheckMark (line 646) | void drawCheckMark( LGFX* gfx, uint16_t gx, uint16_t gy, uint16_t gw, ...
function drawTextShadow (line 653) | void drawTextShadow( LGFX* gfx, const char*str, int32_t x, int32_t y, ...
function cropRoundRect (line 664) | void cropRoundRect( T* gfx, int32_t x, int32_t y, uint32_t w, uint32_t...
function drawDownloadIcon (line 687) | void drawDownloadIcon( uint32_t color, int16_t x, int16_t y, float size )
function drawRSSIBar (line 696) | void drawRSSIBar( int16_t x, int16_t y, int16_t rssi, uint32_t bgcolor...
function drawCaption (line 721) | void drawCaption( LGFX* gfx, const char* caption, int32_t x, int32_t y...
function drawInfoWindow (line 756) | void drawInfoWindow( const char* title, const char* body, unsigned lon...
function modalConfirm (line 773) | int modalConfirm( MenuActionLabels* labels, bool clearAfter )
function modalConfirm (line 800) | int modalConfirm( const char* question, const char* title, const char*...
function getHeatMapColor (line 815) | uint32_t getHeatMapColor( int value, int minimum, int maximum, RGBColo...
function getLowestQRVersionFromString (line 830) | uint8_t getLowestQRVersionFromString( String text, uint8_t ecc )
function qrRender (line 854) | void qrRender( LGFX* gfx, String text, int posX, int posY, uint32_t wi...
function fillAssetSprite (line 900) | void fillAssetSprite( LGFX_Sprite* dst, int32_t offsetX, int32_t offse...
function drawAssetReveal (line 915) | void drawAssetReveal( T *asset, LGFX*_gfx, int32_t posX, int32_t posY )
FILE: examples/AppStore/modules/AppStoreUI/AppStoreUI.hpp
type ProgressBarTheme (line 12) | struct ProgressBarTheme
type UITheme (line 29) | struct UITheme
type UIHScroll (line 76) | struct UIHScroll
type UIUtils (line 97) | namespace UIUtils
class AppStoreUI (line 138) | class AppStoreUI
method MenuGroup (line 170) | MenuGroup* getList() { return Menu; }
FILE: examples/AppStore/modules/Assets/Assets.cpp
type DummyAsset (line 38) | namespace DummyAsset
function drawAltText (line 40) | void drawAltText( LGFX* gfx, const char* caption, int32_t x, int32_t y...
function setup (line 48) | void setup( captionDrawer_t drawCb, LocalAsset * brokenImage, uint32_t...
FILE: examples/AppStore/modules/Assets/Assets.hpp
class RemoteAsset (line 7) | class RemoteAsset
method RemoteAsset (line 10) | RemoteAsset( const char* p, uint32_t w, uint32_t h, const char* a ) : ...
type imageType_t (line 19) | enum imageType_t
class LocalAsset (line 26) | class LocalAsset
method LocalAsset (line 29) | LocalAsset( const unsigned char* d, size_t l, imageType_t t, uint32_t ...
type DummyAsset (line 40) | namespace DummyAsset
FILE: examples/AppStore/modules/CertsManager/CertsManager.cpp
type TLS (line 5) | namespace TLS
function isInWallet (line 103) | bool isInWallet( String host )
function init (line 116) | bool init( String host )
FILE: examples/AppStore/modules/CertsManager/CertsManager.hpp
type TLS (line 5) | namespace TLS
type TLSCert (line 7) | struct TLSCert
FILE: examples/AppStore/modules/Console/Console.hpp
class LogWindow (line 6) | class LogWindow
FILE: examples/AppStore/modules/Downloader/Downloader.cpp
type NTP (line 33) | namespace NTP
function setTimezone (line 40) | void setTimezone( float tz )
function setDst (line 45) | void setDst( bool set )
function setServer (line 50) | void setServer( uint8_t id )
function loadPrefServer (line 75) | void loadPrefServer()
type Downloader (line 95) | namespace Downloader
function httpSetup (line 104) | void httpSetup()
function URLParts (line 112) | URLParts parseURL( String url ) // logic stolen from HTTPClient::begin...
function URLParts (line 144) | URLParts parseURL( const char* url )
function tinyBuffInit (line 150) | bool tinyBuffInit()
function sha_sum_to_str (line 166) | void sha_sum_to_str()
function sha256_sum (line 177) | void sha256_sum(const char* fileName)
function sha256_sum (line 213) | void sha256_sum( String fileName )
function wget (line 220) | bool wget( const char* url, const char* path )
function wget (line 338) | bool wget( String bin_url, String outputFile )
function wget (line 342) | bool wget( String bin_url, const char* outputFile )
function wget (line 346) | bool wget( const char* bin_url, String outputFile )
function WiFiClient (line 352) | WiFiClient *wgetptr( WiFiClientSecure *client, const char* url, const ...
function WiFiEvent (line 396) | void WiFiEvent(WiFiEvent_t event)
function disableWiFi (line 413) | void disableWiFi()
function enableWiFi (line 421) | void enableWiFi()
function enableNTP (line 453) | void enableNTP()
function wifiSetupWorked (line 482) | bool wifiSetupWorked()
function registryFetch (line 505) | void registryFetch( AppRegistry registry, String appRegistryLocalFile )
function downloadGzCatalog (line 550) | bool downloadGzCatalog()
function downloadApp (line 665) | bool downloadApp( String appName )
FILE: examples/AppStore/modules/Downloader/Downloader.hpp
type NTP (line 40) | namespace NTP
type Server (line 42) | struct Server
type Downloader (line 75) | namespace Downloader
type URLParts (line 84) | struct URLParts
FILE: examples/AppStore/modules/FSUtils/FSUtils.cpp
type FSUtils (line 7) | namespace FSUtils
function setTimeFromLastFSAccess (line 12) | void setTimeFromLastFSAccess()
function getFileAttrs (line 83) | bool getFileAttrs( const char* name, size_t *_size, time_t *_time )
function countApps (line 99) | void countApps()
function getInstalledApps (line 107) | bool getInstalledApps( std::vector<String> &files ) {
function removeInstalledApp (line 154) | void removeInstalledApp( String appName )
function isBinFile (line 166) | bool isBinFile( const char* fileName )
function isLauncher (line 174) | bool isLauncher( const char* binFileName )
function isJsonFile (line 182) | bool isJsonFile( const char* fileName )
function isValidAppName (line 190) | bool isValidAppName( const char* fileName )
function iFile_exists (line 200) | bool iFile_exists( fs::FS *fs, String &fname )
function cleanDir (line 221) | void cleanDir( const char* dir )
function copyFile (line 274) | bool copyFile( const char* src, const char* dst )
function trashFile (line 299) | bool trashFile( String path )
function getJson (line 321) | bool getJson( const char* path, JsonObject &root, DynamicJsonDocument ...
function getHiddenApps (line 349) | void getHiddenApps()
function toggleHiddenApp (line 375) | void toggleHiddenApp( String appName, bool add )
function isHiddenApp (line 436) | bool isHiddenApp( String appName )
function scanDataFolder (line 445) | void scanDataFolder()
FILE: examples/AppStore/modules/FSUtils/FSUtils.hpp
type FSUtils (line 41) | namespace FSUtils
function String (line 85) | static String gnu_basename( String path )
FILE: examples/AppStore/modules/MenuItems/MenuItems.cpp
type MenuItems (line 5) | namespace MenuItems
FILE: examples/AppStore/modules/MenuItems/MenuItems.hpp
type MenuItems (line 10) | namespace MenuItems
FILE: examples/AppStore/modules/MenuUtils/MenuUtils.hpp
type MenuItemCallBacks (line 12) | struct MenuItemCallBacks
type ButtonAction (line 21) | struct ButtonAction
type MenuActionLabels (line 29) | struct MenuActionLabels
class MenuGroup (line 36) | class MenuGroup
method setTitle (line 62) | void setTitle( const char* title ) { Title = title; }
class MenuAction (line 39) | class MenuAction
class MenuGroup (line 55) | class MenuGroup
method setTitle (line 62) | void setTitle( const char* title ) { Title = title; }
FILE: examples/AppStore/modules/Registry/Registry.cpp
type RegistryUtils (line 62) | namespace RegistryUtils
function setJsonChannelItem (line 67) | void setJsonChannelItem( JsonObject json, AppRegistryItem reg )
function registrySave (line 80) | void registrySave( AppRegistry registry, String appRegistryLocalFile )
function isValidRegistryChannel (line 133) | bool isValidRegistryChannel( JsonVariant json )
function AppRegistryItem (line 147) | AppRegistryItem getJsonChannel( JsonVariant jsonChannels, const char* ...
function AppRegistry (line 162) | AppRegistry init( String appRegistryLocalFile )
FILE: examples/AppStore/modules/Registry/Registry.hpp
class AppRegistryItem (line 39) | class AppRegistryItem
class AppRegistry (line 58) | class AppRegistry
type RegistryUtils (line 73) | namespace RegistryUtils
FILE: examples/AppStore/modules/misc/controls.h
type HIDSignal (line 6) | enum HIDSignal
function vibrateTask (line 32) | static void vibrateTask( void * param )
function HIDFeedback (line 45) | static void HIDFeedback( int ms )
function HIDFeedback (line 52) | static void HIDFeedback( int ms ) { ; }
function HIDSignal (line 67) | HIDSignal getControls()
FILE: examples/LGFX-SDLoader-Snippet/M5Stack_Buttons.h
function class (line 9) | class SDUButton {
function read (line 57) | uint8_t SDUButton::read(void) {
function setState (line 64) | uint8_t SDUButton::setState(uint8_t pinVal)
function isPressed (line 92) | uint8_t SDUButton::isPressed(void) {
function isReleased (line 96) | uint8_t SDUButton::isReleased(void) {
function wasPressed (line 100) | uint8_t SDUButton::wasPressed(void) {
function wasReleased (line 104) | uint8_t SDUButton::wasReleased(void) {
function wasReleasefor (line 108) | uint8_t SDUButton::wasReleasefor(uint32_t ms) {
function pressedFor (line 114) | uint8_t SDUButton::pressedFor(uint32_t ms) {
function releasedFor (line 118) | uint8_t SDUButton::releasedFor(uint32_t ms) {
function lastChange (line 122) | uint32_t SDUButton::lastChange(void) {
FILE: examples/M5Stack-FW-Menu/src/main.cpp
type M5Btns_t (line 22) | enum M5Btns_t
type sdu_filesystem_t (line 31) | struct sdu_filesystem_t
type Item_t (line 39) | struct Item_t
type scrollClip_t (line 46) | struct scrollClip_t
type AppTheme (line 73) | namespace AppTheme
function HIDUpdate (line 110) | void HIDUpdate()
function M5Btns_t (line 147) | M5Btns_t getPressedButton(cb_t cb=nullptr)
function scrollTitle (line 165) | void scrollTitle()
function renderList (line 183) | void renderList( std::vector<Item_t> items, uint8_t selected_idx )
function GetSlotItems (line 342) | std::vector<Item_t> GetSlotItems( bool show_hidden )
function slotPicker (line 378) | int8_t slotPicker( const char* label, bool show_hidden=true )
function menuItemLoadFW (line 400) | void menuItemLoadFW()
function menuItemLoadFS (line 413) | void menuItemLoadFS()
function menuItemPartitionsInfo (line 426) | void menuItemPartitionsInfo()
function handleResult (line 507) | void handleResult( bool res, const char* msg )
function menuItemPartitionFlash (line 521) | void menuItemPartitionFlash()
function menuItemPartitionBackup (line 528) | void menuItemPartitionBackup()
function menuItemPartitionErase (line 535) | void menuItemPartitionErase()
function menuItemPartitionVerify (line 542) | void menuItemPartitionVerify()
function snoozeUI (line 559) | void snoozeUI()
function brightnessUI (line 580) | void brightnessUI()
function toolsPicker (line 603) | void toolsPicker()
function preferencesPicker (line 636) | void preferencesPicker()
function launcherPicker (line 670) | void launcherPicker()
function printFlashPartition (line 711) | void printFlashPartition( Flash::Partition_t* sdu_partition )
function lsFlashPartitions (line 741) | void lsFlashPartitions()
function lsNVSpartitions (line 752) | void lsNVSpartitions()
function checkFactoryStickyPartition (line 796) | bool checkFactoryStickyPartition()
function checkOTAStickyPartition (line 839) | bool checkOTAStickyPartition()
function setup (line 874) | void setup()
function loop (line 916) | void loop()
FILE: examples/M5Stack-SD-Menu/SAM.h
function class (line 30) | class M5SAM
function setListCaption (line 125) | void M5SAM::setListCaption(String inCaption)
function clearList (line 131) | void M5SAM::clearList()
function addList (line 146) | void M5SAM::addList(String inStr)
function byte (line 169) | byte M5SAM::getListID()
function setListID (line 175) | void M5SAM::setListID(byte idx)
function nextList (line 193) | void M5SAM::nextList( bool renderAfter )
function drawListItem (line 210) | void M5SAM::drawListItem(byte inIDX, byte postIDX)
function showList (line 225) | void M5SAM::showList()
function up (line 266) | void M5SAM::up()
function down (line 276) | void M5SAM::down()
function GoToLevel (line 286) | void M5SAM::GoToLevel(byte inlevel)
function execute (line 295) | void M5SAM::execute()
function addMenuItem (line 306) | void M5SAM::addMenuItem(byte levelID, const char *menu_title,const char ...
function show (line 321) | void M5SAM::show()
function windowClr (line 328) | void M5SAM::windowClr()
function getrgb (line 335) | uint16_t M5SAM::getrgb(byte inred, byte ingrn, byte inblue)
function drawAppMenu (line 345) | void M5SAM::drawAppMenu(String inmenuttl, String inbtnAttl, String inbtn...
function setColorSchema (line 353) | void M5SAM::setColorSchema(uint16_t inmenucolor, uint16_t inwindowcolor,...
function String (line 362) | String M5SAM::keyboardGetString()
function drawMenu (line 421) | void M5SAM::drawMenu(String inmenuttl, String inbtnAttl, String intSpeak...
function drawAppMenu (line 428) | void M5SAM::drawAppMenu(String inmenuttl, String inbtnAttl, String intSp...
function btnRestore (line 446) | void M5SAM::btnRestore()
function drawMenu (line 463) | void M5SAM::drawMenu(String inmenuttl, String inbtnAttl, String inbtnBtt...
FILE: examples/M5Stack-SD-Menu/controls.h
type HIDSignal (line 7) | enum HIDSignal
function vibrateTask (line 37) | static void vibrateTask( void * param )
function HIDFeedback (line 50) | static void HIDFeedback( int ms )
function HIDFeedback (line 57) | static void HIDFeedback( int ms ) { ; }
function M5FacesIsr (line 87) | void IRAM_ATTR M5FacesIsr()
function HIDSignal (line 92) | HIDSignal M5FacesOnKeyPushed( HIDSignal signal )
function HIDSignal (line 99) | HIDSignal extKey()
function HIDSignal (line 138) | HIDSignal extKey() { return HID_INERT; }
function handler (line 158) | void handler(Button2 &btn)
function HIDInit (line 192) | void HIDInit()
function HIDSignal (line 215) | HIDSignal getControls()
FILE: examples/M5Stack-SD-Menu/core.h
function class (line 33) | class LGFX_8BIT_CVBS : public lgfx::LGFX_Device
FILE: examples/M5Stack-SD-Menu/downloader.h
type TLSCert (line 118) | typedef struct {
type URLParts (line 142) | struct URLParts {
function tinyBuffInit (line 154) | bool tinyBuffInit()
function URLParts (line 171) | URLParts parseURL( String url ) { // logic stolen from HTTPClient::begin...
function URLParts (line 201) | URLParts parseURL( const char* url ) {
function modalConfirm (line 427) | int modalConfirm( const char* modalName, const char* question, const cha...
function printProgress (line 454) | void printProgress(uint16_t progress) {
function drawSDUpdaterChannel (line 512) | void drawSDUpdaterChannel() {
function drawAppMenu (line 521) | void drawAppMenu() {
function cleanDir (line 538) | void cleanDir( const char* dir) {
function sha_sum_to_str (line 611) | void sha_sum_to_str() {
function sha256_sum (line 621) | static void sha256_sum(const char* fileName) {
function sha256_sum (line 655) | static void sha256_sum( String fileName ) {
function isInWallet (line 708) | bool isInWallet( String host ) {
function wget (line 825) | bool wget( const char* bin_url, const char* outputFile ) {
function wget (line 914) | bool wget( String bin_url, String outputFile ) {
function wget (line 917) | bool wget( String bin_url, const char* outputFile ) {
function wget (line 920) | bool wget( const char* bin_url, String outputFile ) {
function countDownReboot (line 925) | static void countDownReboot( void * param ) {
function syncStart (line 953) | void syncStart() {
function printVerifyProgress (line 966) | void printVerifyProgress( const char* msg, uint16_t textcolor, uint16_t ...
function getApp (line 974) | bool getApp( String appURL ) {
function init_tls_or_die (line 1089) | bool init_tls_or_die( String host ) {
function syncAppRegistry (line 1111) | bool syncAppRegistry( String BASE_URL ) {
function WiFiEvent (line 1181) | void WiFiEvent(WiFiEvent_t event) {
function enableWiFi (line 1267) | void enableWiFi() {
function enableNTP (line 1304) | void enableNTP() {
function wifiSetupWorked (line 1324) | bool wifiSetupWorked() {
function updateOne (line 1344) | void updateOne(String appName) {
function updateAll (line 1409) | void updateAll() {
FILE: examples/M5Stack-SD-Menu/fsformat.h
function isBinFile (line 59) | bool isBinFile( const char* fileName )
function iFile_exists (line 82) | bool iFile_exists( fs::FS *fs, String &fname )
type FileSystem_src_t (line 104) | enum FileSystem_src_t
type FileSystem_fs_t (line 113) | struct FileSystem_fs_t
type JSONMeta (line 121) | struct JSONMeta
function shortName (line 132) | struct FileInfo
function hasIcon (line 156) | bool hasIcon()
function hasFace (line 167) | bool hasFace()
function hasMeta (line 182) | bool hasMeta()
function getMeta (line 201) | void getMeta( FileInfo *fileInfo )
type tm (line 251) | struct tm
function scanDataFolder (line 290) | void scanDataFolder()
function replaceItem (line 368) | bool replaceItem( fs::FS &fs, String SourceName, String DestName)
function replaceLauncher (line 403) | bool replaceLauncher( fs::FS &fs, FileInfo &info)
function initFileInfo (line 426) | void initFileInfo()
FILE: examples/M5Stack-SD-Menu/main/main.cpp
function setup (line 4) | void setup()
function loop (line 35) | void loop() {
function loopTask (line 45) | void loopTask(void*)
function app_main (line 52) | void app_main()
FILE: examples/M5Stack-SD-Menu/menu.h
function progressBar (line 186) | void progressBar(SDU_DISPLAY_TYPE tft, int x, int y, int w, int h, uint8...
function String (line 198) | static String heapState()
function gotoSleep (line 208) | void gotoSleep()
function renderIcon (line 302) | void renderIcon( FileInfo &fileInfo )
function renderIcon (line 318) | void renderIcon( uint16_t MenuID )
function renderFace (line 324) | void renderFace( String face )
function renderMeta (line 335) | void renderMeta( JSONMeta &jsonMeta )
function getLowestQRVersionFromString (line 375) | uint8_t getLowestQRVersionFromString( String text, uint8_t ecc )
function qrRender (line 399) | void qrRender( SDU_DISPLAY_TYPE gfx, String text, int posX, int posY, ui...
function listDir (line 440) | void listDir( fs::FS &fs, const char * dirName, uint8_t levels, bool pro...
function buildM5Menu (line 554) | void buildM5Menu()
function pageDown (line 601) | void pageDown()
function pageUp (line 618) | void pageUp()
function menuUp (line 630) | void menuUp()
function menuInfo (line 663) | void menuInfo()
function checkMenuTimeStamp (line 692) | void checkMenuTimeStamp()
function downloaderMenu (line 739) | void downloaderMenu()
function updateApp (line 784) | void updateApp( FileInfo &info )
function launchApp (line 798) | void launchApp( FileInfo &info )
function doFSChecks (line 954) | void doFSChecks()
function doFSInventory (line 997) | void doFSInventory()
function HIDMenuObserve (line 1027) | void HIDMenuObserve() {
function sleepTimer (line 1098) | void sleepTimer() {
FILE: examples/M5Stack-SD-Menu/partition_manager.h
function lsPart (line 112) | void lsPart()
FILE: examples/M5Stack-SD-Menu/registry.h
function print (line 90) | void print() {
FILE: examples/M5Stack-SD-Menu/wifi_manager.h
function boolean (line 26) | boolean restoreConfig() {
function boolean (line 47) | boolean checkConnection() {
function startWebServer (line 69) | void startWebServer() {
function setupMode (line 138) | void setupMode() {
function String (line 169) | String makePage(String title, String contents) {
function String (line 180) | String urlDecode(String input) {
function wifiManagerSetup (line 217) | void wifiManagerSetup() {
function wifiManagerLoop (line 234) | void wifiManagerLoop() {
FILE: src/ConfigManager/ConfigManager.cpp
type SDUpdaterNS (line 3) | namespace SDUpdaterNS
type ConfigManager (line 18) | namespace ConfigManager
FILE: src/ConfigManager/ConfigManager.hpp
type SDUpdaterNS (line 37) | namespace SDUpdaterNS
class SDUpdater (line 40) | class SDUpdater
type ConfigManager (line 42) | namespace ConfigManager
type config_sdu_t (line 59) | struct config_sdu_t
method setSDUBtnPoller (line 139) | [[deprecated("use setBtnPoller()")]] void setSDUBtnPoller( BtnPoll...
method setSDUBtnA (line 140) | [[deprecated("use setBtnA()")]] void setSDUBtnA( BtnXPressCb ...
method setSDUBtnB (line 141) | [[deprecated("use setBtnB()")]] void setSDUBtnB( BtnXPressCb ...
method setSDUBtnC (line 142) | [[deprecated("use setBtnC()")]] void setSDUBtnC( BtnXPressCb ...
FILE: src/FS/ffat.hpp
type SDUpdaterNS (line 9) | namespace SDUpdaterNS
type ConfigManager (line 12) | namespace ConfigManager
type FFat_FS_Config_t (line 14) | struct FFat_FS_Config_t
function SDU_FFat_Begin (line 43) | inline bool SDU_FFat_Begin(ConfigManager::FFat_FS_Config_t cfg=ConfigM...
FILE: src/FS/littlefs.hpp
type SDUpdaterNS (line 9) | namespace SDUpdaterNS
type ConfigManager (line 12) | namespace ConfigManager
type LittleFS_FS_Config_t (line 14) | struct LittleFS_FS_Config_t
function SDU_LittleFS_Begin (line 44) | inline bool SDU_LittleFS_Begin(ConfigManager::LittleFS_FS_Config_t cfg...
FILE: src/FS/sd.hpp
type SDUpdaterNS (line 9) | namespace SDUpdaterNS
type ConfigManager (line 12) | namespace ConfigManager
type SD_FS_Config_t (line 14) | struct SD_FS_Config_t
function SDU_SDBegin (line 44) | inline bool SDU_SDBegin( uint8_t csPin )
function SDU_SDBegin (line 49) | inline bool SDU_SDBegin( ConfigManager::SD_FS_Config_t cfg=ConfigManag...
FILE: src/FS/sd_mmc.hpp
type SDUpdaterNS (line 9) | namespace SDUpdaterNS
type ConfigManager (line 12) | namespace ConfigManager
type SD_MMC_Bus_Config_t (line 14) | struct SD_MMC_Bus_Config_t
type SD_MMC_FS_Config_t (line 24) | struct SD_MMC_FS_Config_t
function SDU_SD_MMC_Begin (line 55) | inline bool SDU_SD_MMC_Begin( ConfigManager::SD_MMC_FS_Config_t cfg=Co...
FILE: src/FS/sdfat.hpp
function oflag_t (line 26) | inline oflag_t _convert_access_mode_to_flag(const char* mode, const bool...
class SdFsFileImpl (line 46) | class SdFsFileImpl : public fs::FileImpl
method SdFsFileImpl (line 51) | SdFsFileImpl(FsFile file) : _file(file) {}
method write (line 54) | virtual size_t write(const uint8_t *buf, size_t size) { return _file.w...
method read (line 55) | virtual size_t read(uint8_t* buf, size_t size) { return _file.read(buf...
method flush (line 56) | virtual void flush() { return _file.flush(); }
method position (line 57) | virtual size_t position() const { return _file.curPosition(); }
method size (line 58) | virtual size_t size() const { return _file.size(); }
method close (line 59) | virtual void close() { _file.close(); }
method boolean (line 61) | virtual boolean isDirectory(void) { return _file.isDirectory(); }
method openNextFile (line 62) | virtual fs::FileImplPtr openNextFile(const char* mode) { return std::...
method boolean (line 63) | virtual boolean seekDir(long position) { return _file.seek(position); }
method seek (line 64) | virtual bool seek(uint32_t pos, fs::SeekMode mode)
method String (line 84) | virtual String getNextFileName(void) { /* not implemented and not need...
method String (line 85) | virtual String getNextFileName(bool*) { /* not implemented and not nee...
method time_t (line 86) | virtual time_t getLastWrite() { /* not implemented and not needed */ ...
method setBufferSize (line 88) | virtual bool setBufferSize(size_t size) { /* not implemented and not n...
method rewindDirectory (line 89) | virtual void rewindDirectory(void) { /* not implemented and not needed...
class SdFsFSImpl (line 93) | class SdFsFSImpl : public fs::FSImpl
method SdFsFSImpl (line 97) | SdFsFSImpl(SdFs& sd) : sd(sd) { }
method open (line 99) | virtual fs::FileImplPtr open(const char* path, const char* mode, const...
method exists (line 103) | virtual bool exists(const char* path) { return sd.exists(path); }
method rename (line 104) | virtual bool rename(const char* pathFrom, const char* pathTo) { return...
method remove (line 105) | virtual bool remove(const char* path) { return sd.remove(path); }
method mkdir (line 106) | virtual bool mkdir(const char *path) { return sd.mkdir(path); }
method rmdir (line 107) | virtual bool rmdir(const char *path) { return sd.rmdir(path); }
type SDUpdaterNS (line 112) | namespace SDUpdaterNS
type ConfigManager (line 115) | namespace ConfigManager
function SDU_SDFat_Begin (line 138) | inline bool SDU_SDFat_Begin( SdSpiConfig *SdFatCfg )
FILE: src/FS/spiffs.hpp
type SDUpdaterNS (line 9) | namespace SDUpdaterNS
type ConfigManager (line 12) | namespace ConfigManager
type SPIFFS_FS_Config_t (line 14) | struct SPIFFS_FS_Config_t
function SDU_SPIFFS_Begin (line 45) | inline bool SDU_SPIFFS_Begin(ConfigManager::SPIFFS_FS_Config_t cfg=Con...
FILE: src/M5StackUpdater.hpp
type SDUpdaterNS (line 304) | namespace SDUpdaterNS
type ConfigManager (line 306) | namespace ConfigManager
function hasFS (line 444) | inline bool hasFS( SDUpdater* sdu, fs::FS &fs, bool report_errors=tr...
function updateFromFS (line 475) | inline void updateFromFS( const String& fileName )
function updateFromFS (line 488) | inline void updateFromFS( fs::FS &fs, const String& fileName, const in...
function saveSketchToFS (line 499) | inline bool saveSketchToFS(fs::FS &fs, const char* binfilename, const ...
function updateRollBack (line 509) | inline void updateRollBack( String message )
function checkSDUpdater (line 520) | inline void checkSDUpdater( fs::FS *fsPtr, String fileName, unsigned l...
function checkFWUpdater (line 568) | inline void checkFWUpdater( unsigned long waitdelay=5000 )
function checkSDUpdater (line 576) | inline void checkSDUpdater( fs::FS &fs, String fileName, unsigned long...
function checkSDUpdater (line 599) | inline void checkSDUpdater( SdFs &sd, String fileName=MENU_BIN, unsign...
FILE: src/PartitionManager/NVS/NVSUtils.cpp
type SDUpdaterNS (line 17) | namespace SDUpdaterNS
type NVS (line 20) | namespace NVS
function PartitionDesc_t (line 27) | PartitionDesc_t* findPartition( uint8_t ota_num )
function PartitionDesc_t (line 44) | PartitionDesc_t* findPartition( const char* name )
function PartitionDesc_t (line 61) | PartitionDesc_t* findPartition( Flash::Partition_t* flash_partition )
function Erase (line 76) | int Erase()
function getPartitions (line 86) | bool getPartitions()
function parsePartitions (line 126) | bool parsePartitions( const char* blob, size_t size )
function savePartitions (line 146) | bool savePartitions()
function deletePartitions (line 187) | void deletePartitions()
function saveMenuPrefs (line 206) | bool saveMenuPrefs()
function getMenuPrefs (line 244) | bool getMenuPrefs( uint32_t *menuSize, uint8_t *image_digest )
FILE: src/PartitionManager/NVS/NVSUtils.hpp
type SDUpdaterNS (line 19) | namespace SDUpdaterNS
type NVS (line 22) | namespace NVS
type PartitionDesc_t (line 35) | struct __attribute__((__packed__)) PartitionDesc_t
type blob_partition_t (line 44) | struct blob_partition_t
method blob_partition_t (line 48) | blob_partition_t() : blob(nullptr), needs_free(false) { }
method blob_partition_t (line 49) | blob_partition_t( size_t blob_size ) : blob(nullptr), needs_free(f...
FILE: src/PartitionManager/PartitionManager.cpp
type SDUpdaterNS (line 6) | namespace SDUpdaterNS
type PartitionManager (line 8) | namespace PartitionManager
function createPartitions (line 12) | void createPartitions()
function updatePartitions (line 40) | void updatePartitions()
function processPartitions (line 73) | void processPartitions()
function flash (line 101) | bool flash( const esp_partition_t *dstpart, fs::FS *dstfs, const cha...
function flash (line 138) | bool flash( uint8_t ota_num, sdu_fs_picker_t fsPicker, sdu_file_pick...
function backupFlash (line 152) | bool backupFlash( fs::FS* dstFs, const char* dstName )
function backup (line 162) | bool backup( uint8_t ota_num, sdu_fs_picker_t fsPicker )
function backup (line 170) | bool backup( NVS::PartitionDesc_t *src_nvs_part, sdu_fs_picker_t fsP...
function verify (line 189) | bool verify( uint8_t ota_num )
function erase (line 258) | bool erase( uint8_t ota_num )
function canMigrateToFactory (line 292) | bool canMigrateToFactory()
function flashFactory (line 300) | bool flashFactory()
function migrateSketch (line 312) | bool migrateSketch( const char* binFileName )
FILE: src/PartitionManager/PartitionManager.hpp
type SDUpdaterNS (line 9) | namespace SDUpdaterNS
type PartitionManager (line 11) | namespace PartitionManager
type sdu_fs_copy_t (line 38) | struct sdu_fs_copy_t
method commit (line 43) | bool commit()
FILE: src/PartitionManager/Partitions/PartitionUtils.cpp
type SDUpdaterNS (line 10) | namespace SDUpdaterNS
type Flash (line 13) | namespace Flash
function comparePartition (line 54) | bool comparePartition(const esp_partition_t* src1, const esp_partiti...
function comparePartition (line 76) | bool comparePartition(const esp_partition_t* src1, fs::File* src2, s...
function copyPartition (line 107) | bool copyPartition( fs::FS* fs, const char* binfilename )
function copyPartition (line 124) | bool copyPartition(fs::File* dst, const esp_partition_t* src, size_t...
function copyPartition (line 152) | bool copyPartition(fs::File* dstFile, const esp_partition_t* dst, co...
function copyPartition (line 185) | bool copyPartition(const esp_partition_t* dst, const esp_partition_t...
function copyPartition (line 217) | bool copyPartition(const esp_partition_t* dst, Stream* src, size_t l...
function copyPartition (line 249) | bool copyPartition(const esp_partition_t* dst, fs::FS *fs, const cha...
function dumpFW (line 264) | bool dumpFW( fs::FS *fs, const char* fw_name )
function loadFactory (line 316) | void loadFactory()
function esp_image_metadata_t (line 336) | esp_image_metadata_t getSketchMeta( const esp_partition_t* src )
function hasFactory (line 374) | bool hasFactory()
function hasFactoryApp (line 382) | bool hasFactoryApp()
function isRunningFactory (line 392) | bool isRunningFactory()
function saveSketchToPartition (line 401) | bool saveSketchToPartition( const esp_partition_t* dst_partition )
function saveSketchToFactory (line 427) | bool saveSketchToFactory()
function esp_partition_t (line 462) | const esp_partition_t* getFactoryPartition()
function esp_partition_t (line 472) | const esp_partition_t* getPartition( uint8_t ota_num )
function Partition_t (line 485) | Partition_t findPartition( uint8_t ota_num )
function Partition_t (line 496) | Partition_t* findDupePartition( esp_image_metadata_t *meta, esp_part...
function bootPartition (line 513) | bool bootPartition( uint8_t ota_num )
function partitionIsApp (line 531) | bool partitionIsApp( const esp_partition_t *part )
function partitionIsFactory (line 537) | bool partitionIsFactory( const esp_partition_t *part )
function partitionIsOTA (line 543) | bool partitionIsOTA( const esp_partition_t *part )
function metadataHasDigest (line 549) | bool metadataHasDigest( const esp_image_metadata_t *meta )
function isEmpty (line 556) | bool isEmpty( Partition_t* sdu_partition )
function esp_partition_t (line 562) | const esp_partition_t* getNextAvailPartition(esp_partition_type_t ty...
function memoize (line 578) | void memoize( const esp_partition_t *part )
function scan (line 593) | void scan()
function erase (line 608) | bool erase( const esp_partition_t *part )
function erase (line 620) | bool erase( uint8_t ota_num )
FILE: src/PartitionManager/Partitions/PartitionUtils.hpp
type SDUpdaterNS (line 17) | namespace SDUpdaterNS
type Flash (line 20) | namespace Flash
type digest_t (line 23) | struct digest_t
type Partition_t (line 31) | struct Partition_t
FILE: src/SDUpdater/SDUpdater_Class.cpp
type SDUpdaterNS (line 3) | namespace SDUpdaterNS
FILE: src/SDUpdater/SDUpdater_Class.hpp
type SDUpdaterNS (line 36) | namespace SDUpdaterNS
class SDUpdater (line 56) | class SDUpdater
method saveSketchToFS (line 75) | inline bool saveSketchToFS( fs::FS &fs, const char* binfilename={MEN...
FILE: src/SDUpdater/Update_Interface.hpp
type SDUpdaterNS (line 14) | namespace SDUpdaterNS
type ConfigManager (line 16) | namespace ConfigManager
function UpdateManagerInterface_t (line 19) | inline UpdateManagerInterface_t *GetUpdateInterface()
function UpdateManagerInterface_t (line 47) | inline UpdateManagerInterface_t *GetUpdateInterface()
type ConfigManager (line 44) | namespace ConfigManager
function UpdateManagerInterface_t (line 19) | inline UpdateManagerInterface_t *GetUpdateInterface()
function UpdateManagerInterface_t (line 47) | inline UpdateManagerInterface_t *GetUpdateInterface()
type SDUpdaterNS (line 42) | namespace SDUpdaterNS
type ConfigManager (line 16) | namespace ConfigManager
function UpdateManagerInterface_t (line 19) | inline UpdateManagerInterface_t *GetUpdateInterface()
function UpdateManagerInterface_t (line 47) | inline UpdateManagerInterface_t *GetUpdateInterface()
type ConfigManager (line 44) | namespace ConfigManager
function UpdateManagerInterface_t (line 19) | inline UpdateManagerInterface_t *GetUpdateInterface()
function UpdateManagerInterface_t (line 47) | inline UpdateManagerInterface_t *GetUpdateInterface()
FILE: src/UI/Touch.hpp
type SDUpdaterNS (line 7) | namespace SDUpdaterNS
type SDU_UI (line 10) | namespace SDU_UI
type TouchButtonWrapper (line 13) | struct TouchButtonWrapper
type TriggerSource (line 114) | namespace TriggerSource
type touchTriggerElements_t (line 118) | struct touchTriggerElements_t
method touchTriggerElements_t (line 120) | touchTriggerElements_t() { }
method touchTriggerElements_t (line 121) | touchTriggerElements_t( SDU_TouchButton *_LoadBtn, SDU_TouchButton...
function triggerInitTouch (line 135) | static void triggerInitTouch(triggerMap_t* trigger)
function triggerActionTouch (line 201) | static bool triggerActionTouch(triggerMap_t* trigger, uint32_t msec )
function triggerFinalizeTouch (line 272) | static void triggerFinalizeTouch( triggerMap_t* trigger, int ret )
FILE: src/UI/UI.hpp
type SDUpdaterNS (line 7) | namespace SDUpdaterNS
type SDU_UI (line 11) | namespace SDU_UI
type fontInfo_t (line 40) | struct fontInfo_t
type fontInfo_t (line 50) | struct fontInfo_t
function fillStyledRect (line 137) | static void fillStyledRect( SplashPageElementStyle_t *style, int32_t...
function adjustFontSize (line 148) | static void adjustFontSize( uint8_t *lineHeightBig, uint8_t *lineHei...
function fillProgressBox (line 157) | static void fillProgressBox( int32_t posX, int32_t posY, uint16_t of...
function resetScroll (line 172) | static void resetScroll()
function resetScroll (line 205) | static void resetScroll() { }
function fillStyledRect (line 206) | static void fillStyledRect( SplashPageElementStyle_t *style, int32_t...
function adjustFontSize (line 207) | static void adjustFontSize( uint8_t *lineHeightBig, uint8_t *lineHei...
function fillProgressBox (line 208) | static void fillProgressBox( int32_t posX, int32_t posY, uint16_t of...
function freezeTextStyle (line 215) | static void freezeTextStyle() // onBeforeCb
function thawTextStyle (line 231) | static void thawTextStyle()
function drawTextShadow (line 243) | static void drawTextShadow( const char* text, int32_t x, int32_t y, ...
function drawSDUSplashElement (line 253) | static void drawSDUSplashElement( const char* msg, int32_t x, int32_...
function drawSDUSplashPage (line 262) | static void drawSDUSplashPage( const char* msg )
function drawSDUPushButton (line 292) | static void drawSDUPushButton( const char* label, uint8_t position, ...
function SDMenuProgressUI (line 313) | static void SDMenuProgressUI( int state, int size )
function DisplayUpdateUI (line 350) | static void DisplayUpdateUI( const String& label )
function DisplayErrorUI (line 365) | static void DisplayErrorUI( const String& msg, unsigned long wait )
FILE: src/UI/common.hpp
type SDUpdaterNS (line 9) | namespace SDUpdaterNS
type ConfigManager (line 12) | namespace ConfigManager
type SDU_UI (line 18) | namespace SDU_UI
function SDMenuProgressHeadless (line 41) | static void inline SDMenuProgressHeadless( int state, int size )
type TriggerSource (line 64) | namespace TriggerSource
function triggerInitSerial (line 70) | static void triggerInitSerial(triggerMap_t* trigger) { }
function triggerActionSerial (line 71) | static bool triggerActionSerial(triggerMap_t* trigger, uint32_t msec )
function triggerFinalizeSerial (line 83) | static void triggerFinalizeSerial( triggerMap_t* trigger, int ret ) ...
function triggerInitButton (line 86) | static void triggerInitButton(triggerMap_t* trigger)
function triggerActionButton (line 119) | static bool triggerActionButton(triggerMap_t* trigger, uint32_t msec )
function triggerFinalizeButton (line 152) | static void triggerFinalizeButton( triggerMap_t* trigger, int ret )
function actionTriggered (line 177) | static int actionTriggered( char* labelLoad, char* labelSkip, char*...
function serial (line 220) | static inline int serial( char* labelLoad, char* labelSkip, char* l...
function pushButton (line 236) | static int pushButton( char* labelLoad, char* labelSkip, char* label...
function triggerInitTouch (line 307) | static void triggerInitTouch(triggerMap_t* trigger) { log_d("[NULLCB...
function triggerActionTouch (line 308) | static bool triggerActionTouch(triggerMap_t* trigger, uint32_t msec ...
function triggerFinalizeTouch (line 309) | static void triggerFinalizeTouch( triggerMap_t* trigger, int ret ) {...
function SDMenuProgressUI (line 316) | static inline void SDMenuProgressUI( int state, int size ) { log_d("[N...
function DisplayUpdateUI (line 317) | static inline void DisplayUpdateUI( const String& label ) { log_d("[NU...
function DisplayErrorUI (line 318) | static inline void DisplayErrorUI( const String& msg, unsigned long wa...
function freezeTextStyle (line 319) | static inline void freezeTextStyle() { log_d("[NULLCB] Freezing"); }
function thawTextStyle (line 320) | static inline void thawTextStyle() { log_d("[NULLCB] Thawing");}
function drawSDUSplashPage (line 321) | static inline void drawSDUSplashPage( const char* msg ) { log_d("[NULL...
function drawSDUPushButton (line 322) | static inline void drawSDUPushButton( const char* label, uint8_t posit...
FILE: src/misc/types.h
function namespace (line 13) | namespace SDUpdaterNS
type DigitalPinButton_t (line 135) | struct DigitalPinButton_t
type SplashPageElementStyle_t (line 147) | struct SplashPageElementStyle_t
type ProgressBarStyle_t (line 157) | struct ProgressBarStyle_t
type BtnStyle_t (line 176) | struct BtnStyle_t
type SDUTextStyle_t (line 186) | struct SDUTextStyle_t // somehow redundant with LGFX's textstyle_t
type BtnStyles_t (line 195) | struct BtnStyles_t
type loaderAnimator_t (line 225) | struct loaderAnimator_t
type TouchStyles (line 235) | struct TouchStyles
function BtnStyle_t (line 259) | BtnStyle_t *Save{nullptr};
type Theme_t (line 263) | struct Theme_t
function SplashPageElementStyle_t (line 285) | SplashPageElementStyle_t* SplashAuthorNameStyle{nullptr};
Condensed preview — 168 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (913K chars).
[
{
"path": ".gitattributes",
"chars": 191,
"preview": ".github/tools/SD-Apps/* linguist-vendored\n.github/tools/* linguist-vendored \n.github/tools/scripts/* linguist-vendored\n#"
},
{
"path": ".github/FUNDING.yml",
"chars": 547,
"preview": "# These are supported funding model platforms\n\ngithub: [lovyan03, tobozo] # Replace with up to 4 GitHub Sponsors-enabled"
},
{
"path": ".github/scripts/semver.sh",
"chars": 3651,
"preview": "#!/bin/bash\n\n\nfunction patchpropertyfiles {\n\n old_tag=$1\n new_tag=$2\n\n echo \"Patching property/json/tag files $old_ta"
},
{
"path": ".github/tools/SD-Apps/after_deploy.sh",
"chars": 673,
"preview": "#!/bin/bash\n\ncurl -v -H \"Authorization: token $GH_TOKEN\" --retry 5 \"https://api.github.com/repos/tobozo/M5Stack-SD-Updat"
},
{
"path": ".github/tools/SD-Apps/before_deploy.sh",
"chars": 1757,
"preview": "#!/bin/bash\n\n\ncd $PWD\n\n#if ! [[ $TRAVIS_TAG ]]; then\n# git config --global user.email \"travis@travis-ci.org\"\n# git con"
},
{
"path": ".github/tools/SD-Apps/before_install.sh",
"chars": 1020,
"preview": "#!/bin/bash\n\n# REQUIRES: $IDE_VERSION, $M5_SD_BUILD_DIR\n\n#date -u\n#uname -a\n#git fetch -t\n#env | sort\n#git log `git desc"
},
{
"path": ".github/tools/SD-Apps/functions.sh",
"chars": 3939,
"preview": "#!/bin/bash\n\nfunction movebin {\n find /tmp -name \\*.partitions.bin -exec rm {} \\; #<-- you need that backslash before an"
},
{
"path": ".github/tools/SD-Apps/gen-apps.sh",
"chars": 7684,
"preview": "#!/bin/bash\n\nmy_dir=\"$(dirname \"$0\")\"\nsource $my_dir/functions.sh\n\nreadonly ARDUINO_CI_SCRIPT_ARDUINO_OUTPUT_FILTER_REGE"
},
{
"path": ".github/tools/SD-Apps/gen-menu.sh",
"chars": 2144,
"preview": "#!/bin/bash\n\n# Generate TobozoLauncher.bin and BetaLauncher.bin\n\n\ncd $TRAVIS_BUILD_DIR;\narduino --pref \"compiler.warning"
},
{
"path": ".github/tools/SD-Apps/get-deps.sh",
"chars": 2497,
"preview": "#!/bin/bash\n\n# TODO: foreach this from JSON\n\nwget --quiet https://github.com/adafruit/Adafruit_NeoPixel/archive/master.z"
},
{
"path": ".github/tools/SD-Apps/get-precompiled.sh",
"chars": 2385,
"preview": "#!/bin/bash\n\n\n#- curl -v --retry 5 \"https://api.github.com/repos/lovyan03/M5Stack_LovyanLauncher/releases/latest?access_"
},
{
"path": ".github/tools/SD-Apps/install.sh",
"chars": 405,
"preview": "#!/bin/bash\n\ngem install git.io\n\nif [ -f $SDAPP_FOLDER/install_$TRAVIS_BRANCH.sh ]; then\n source $SDAPP_FOLDER/install_"
},
{
"path": ".github/tools/SD-Apps/install_master.sh",
"chars": 162,
"preview": "#!/bin/bash\n\ngit submodule update --init --recursive\ncd $SDAPP_FOLDER\n# pull latest code from submodules\ngit submodule f"
},
{
"path": ".github/tools/SD-Apps/install_unstable.sh",
"chars": 166,
"preview": "#!/bin/bash\n\n#git submodule update --init --recursive\n#cd $SDAPP_FOLDER\n## pull latest code from submodules\n#git submodu"
},
{
"path": ".github/tools/SD-Apps/script.sh",
"chars": 1722,
"preview": "#!/bin/bash\n\nsource $SDAPP_FOLDER/gen-menu.sh\n\nif [ \"$TRAVIS_BRANCH\" != \"master\" ]; then\n # UNSTABLE\n export git_versi"
},
{
"path": ".github/tools/common.sh",
"chars": 31823,
"preview": "#!/bin/bash\n\n/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec"
},
{
"path": ".github/tools/cron.sh",
"chars": 2335,
"preview": "#!/bin/bash\n\n# set -o xtrace\n\ngit remote rm origin\ngit remote add origin https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/"
},
{
"path": ".github/tools/deploy.sh",
"chars": 8728,
"preview": "#!/bin/bash\n\njson_escape () {\n printf '%s' \"$1\" | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))'\n "
},
{
"path": ".github/tools/git-describe.awk",
"chars": 404,
"preview": "#!/usr/bin/awk -f\nBEGIN {\n if (ARGC != 2) {\n print \"git-describe-remote.awk $GITHUB_REPOSITORY\"\n exit\n }\n FS = "
},
{
"path": ".github/tools/hooker.php",
"chars": 3136,
"preview": "<?php\n\n/*\n\nBash usage example:\n\n php hooker.php [Hook URL] [Event Name] [JSON Code] [Hook Secret]\n\n php hooker.php \"${"
},
{
"path": ".github/tools/ino2fw.sh",
"chars": 9658,
"preview": "#!/bin/bash\n\n# Credits: https://github.com/lstux/OdroidGO/\n\nVERBOSE=0\nARDUINO_BOARD=\"esp32:esp32:odroid_esp32\"\nSKETCHBOO"
},
{
"path": ".github/tools/mkfw-build.sh",
"chars": 715,
"preview": "#!/bin/sh\n\n# Credits: https://github.com/lstux/OdroidGO/\n\nBUILDDIR=\"/tmp/odroid-go-mkfw\"\n\n\nif ! [ -d \"${BUILDDIR}\" ]; th"
},
{
"path": ".github/workflows/ArduinoBuild.yml",
"chars": 8512,
"preview": "name: ArduinoBuild\non:\n push:\n paths:\n - '**.ino'\n - '**.cpp'\n - '**.hpp'\n - '**.h'\n - '**.c'\n - '"
},
{
"path": ".github/workflows/PlatformioBuild.yml",
"chars": 4051,
"preview": "name: PlatformIOBuild\n\nenv:\n PROJECT_DIR: examples/Test/build_test\n BRANCH_NAME: ${{ github.head_ref || github.ref_nam"
},
{
"path": ".github/workflows/cron.yml",
"chars": 1914,
"preview": "name: \"Cron ➕ workflows\"\non:\n schedule:\n - cron: '*/10 * * * *'\n workflow_dispatch:\n inputs:\n dispatch-fqwn"
},
{
"path": ".github/workflows/onrelease.yml",
"chars": 1209,
"preview": "on:\n release:\n types:\n - published\n workflow_dispatch:\n inputs:\n versiontype:\n description: 'Ve"
},
{
"path": ".gitignore",
"chars": 23,
"preview": "build\n.pio/\n.directory\n"
},
{
"path": ".gitmodules",
"chars": 375,
"preview": "[submodule \"examples/M5Stack-SD-Menu/SD-Apps/M5Stack_lifegame\"]\n\tpath = examples/M5Stack-SD-Menu/SD-Apps/M5Stack_lifegam"
},
{
"path": ".travis.yml",
"chars": 6901,
"preview": "sudo: required\n\nlanguage: python\npython: 3.6\n\nenv:\n global:\n # The Arduino IDE will be installed at APPLICATION_FOLD"
},
{
"path": "LICENSE",
"chars": 1084,
"preview": "MIT License\n\nCopyright 2018 tobozo http://github.com/tobozo\n\nPermission is hereby granted, free of charge, to any person"
},
{
"path": "README.md",
"chars": 23996,
"preview": "[](https://github.com/tobozo/M5Stack-SD-Upda"
},
{
"path": "component.mk",
"chars": 238,
"preview": "#\n# Main Makefile. This is basically the same as a component makefile.\n#\n# (Uses default behaviour of compiling all sour"
},
{
"path": "examples/AppStore/AppStore.ino",
"chars": 26,
"preview": "\n#include \"main/main.cpp\"\n"
},
{
"path": "examples/AppStore/main/main.cpp",
"chars": 356,
"preview": "\n#include \"../modules/AppStoreMain/AppStoreMain.cpp\"\n\n\nvoid setup()\n{\n AppStore::setup();\n}\n\n\nvoid loop()\n{\n AppStore:"
},
{
"path": "examples/AppStore/modules/AppStoreActions/AppStoreActions.cpp",
"chars": 30523,
"preview": "#pragma once\n\n#include <ArduinoJson.h>\n#include \"AppStoreActions.hpp\"\n#include \"../AppStoreUI/AppStoreUI.hpp\"\n#include \""
},
{
"path": "examples/AppStore/modules/AppStoreActions/AppStoreActions.hpp",
"chars": 3212,
"preview": "#pragma once\n\n#include \"../misc/core.h\"\n#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson/\n\nnamespace"
},
{
"path": "examples/AppStore/modules/AppStoreMain/AppStoreMain.cpp",
"chars": 4415,
"preview": "/*\n *\n * M5Stack Application Store\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2021 to"
},
{
"path": "examples/AppStore/modules/AppStoreMain/AppStoreMain.hpp",
"chars": 1527,
"preview": "/*\n *\n * M5Stack Application Store\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2021 to"
},
{
"path": "examples/AppStore/modules/AppStoreUI/AppStoreUI.cpp",
"chars": 35036,
"preview": "#pragma once\n\n#include \"AppStoreUI.hpp\"\n#include \"lgfx/utility/lgfx_qrcode.h\"\n\nusing namespace MenuItems;\nusing namespac"
},
{
"path": "examples/AppStore/modules/AppStoreUI/AppStoreUI.hpp",
"chars": 7386,
"preview": "#pragma once\n\n#include \"../MenuItems/MenuItems.hpp\"\n#include \"../MenuUtils/MenuUtils.hpp\"\n#include \"../AppStoreActions/A"
},
{
"path": "examples/AppStore/modules/Assets/Assets.cpp",
"chars": 1669,
"preview": "#pragma once\n\n#include \"Assets.hpp\"\n\nvoid LocalAsset::draw( LGFX* gfx, int32_t x, int32_t y, int32_t w, int32_t h )\n{\n "
},
{
"path": "examples/AppStore/modules/Assets/Assets.h",
"chars": 14295,
"preview": "/*\n * Image Source: https://i.imgur.com/llemDHF.gif\n *\n * - ImageMagick:\n * #> convert -strip disk.gif disk%02d.jpg\n "
},
{
"path": "examples/AppStore/modules/Assets/Assets.hpp",
"chars": 1435,
"preview": "#pragma once\n\n#include \"../misc/core.h\"\n#include \"Assets.h\"\n\n\nclass RemoteAsset\n{\npublic:\n RemoteAsset( const char* p, "
},
{
"path": "examples/AppStore/modules/CertsManager/CertsManager.cpp",
"chars": 4589,
"preview": "#pragma once\n\n#include \"CertsManager.hpp\"\n\nnamespace TLS\n{\n\n const char* updateWallet( String host, const char* ca)\n {"
},
{
"path": "examples/AppStore/modules/CertsManager/CertsManager.hpp",
"chars": 1009,
"preview": "#pragma once\n\n#include \"../misc/config.h\"\n\nnamespace TLS\n{\n struct TLSCert\n {\n const char* host;\n const char* ca"
},
{
"path": "examples/AppStore/modules/Console/Console.cpp",
"chars": 1609,
"preview": "#pragma once\n\n#include \"Console.hpp\"\n#include \"../misc/config.h\"\n#include \"../misc/i18n.h\"\n\n// Scrollable text window\nLo"
},
{
"path": "examples/AppStore/modules/Console/Console.hpp",
"chars": 587,
"preview": "#pragma once\n\n#include \"Console.hpp\"\n\n// Scrollable text window\nclass LogWindow\n{\n public:\n LogWindow( LGFX* gfx=nul"
},
{
"path": "examples/AppStore/modules/Downloader/Downloader.cpp",
"chars": 24222,
"preview": "#pragma once\n\n#include \"Downloader.hpp\"\n#include \"../AppStoreUI/AppStoreUI.hpp\"\n#include \"../FSUtils/FSUtils.hpp\" // f"
},
{
"path": "examples/AppStore/modules/Downloader/Downloader.hpp",
"chars": 5013,
"preview": "/*\n *\n * M5Stack SD Menu\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2019 tobozo http:"
},
{
"path": "examples/AppStore/modules/FSUtils/FSUtils.cpp",
"chars": 12116,
"preview": "#pragma once\n\n#include \"FSUtils.hpp\"\n\nextern LogWindow *Console;\n\nnamespace FSUtils\n{\n\n using namespace RegistryUtils;\n"
},
{
"path": "examples/AppStore/modules/FSUtils/FSUtils.hpp",
"chars": 3051,
"preview": "/*\n *\n * M5Stack SD Menu\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2019 tobozo http:"
},
{
"path": "examples/AppStore/modules/MenuItems/MenuItems.cpp",
"chars": 67,
"preview": "#pragma once\n\n#include \"MenuItems.hpp\"\n\nnamespace MenuItems\n{\n\n\n};\n"
},
{
"path": "examples/AppStore/modules/MenuItems/MenuItems.hpp",
"chars": 11965,
"preview": "#pragma once\n\n#include \"../misc/i18n.h\"\n#include \"../Assets/Assets.hpp\"\n#include \"../AppStoreActions/AppStoreActions.hpp"
},
{
"path": "examples/AppStore/modules/MenuUtils/MenuUtils.cpp",
"chars": 2719,
"preview": "#pragma once\n\n#include \"MenuUtils.hpp\"\n\n\nMenuAction::MenuAction( const char* _title, MenuItemCallBacks* _callbacks, cons"
},
{
"path": "examples/AppStore/modules/MenuUtils/MenuUtils.hpp",
"chars": 1757,
"preview": "#pragma once\n\n#include \"../misc/core.h\"\n#include \"../misc/config.h\"\n#include \"../Assets/Assets.hpp\"\n\ntypedef void(*onsel"
},
{
"path": "examples/AppStore/modules/Registry/Registry.cpp",
"chars": 7742,
"preview": "#pragma once\n\n#include \"Registry.hpp\"\n#include \"../Downloader/Downloader.hpp\"\n\n\nvoid AppRegistry::init()\n{\n masterChann"
},
{
"path": "examples/AppStore/modules/Registry/Registry.hpp",
"chars": 3353,
"preview": "/*\n *\n * M5Stack SD Menu\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2019 tobozo http:"
},
{
"path": "examples/AppStore/modules/misc/compile_time.h",
"chars": 4327,
"preview": "/*\n * compile_time.h\n *\n * Created: 30.05.2017 20:57:58\n * Author: Dennis (instructable.com/member/nqtronix)\n *\n * This"
},
{
"path": "examples/AppStore/modules/misc/config.h",
"chars": 4749,
"preview": "#pragma once\n\n#include \"core.h\"\n#include <vector>\n#include <M5StackUpdater.h>\n\n#define SD_CERT_PATH \"/cert\" // Filesyste"
},
{
"path": "examples/AppStore/modules/misc/controls.h",
"chars": 3030,
"preview": "#pragma once\n\n/*\n * Mandatory and optional controls for the menu to be usable\n */\nenum HIDSignal\n{\n HID_INERT = 0,"
},
{
"path": "examples/AppStore/modules/misc/core.h",
"chars": 1991,
"preview": "#pragma once\n\n#include <ESP32-Chimera-Core.h> // https://github.com/tobozo/ESP32-Chimera-Core\n\n#define SDU_APP_NAME \"M"
},
{
"path": "examples/AppStore/modules/misc/i18n.h",
"chars": 1194,
"preview": "#pragma once\n\n#define LANG_EN 0xff\n#define LANG_JP 0x12\n#define LANG_CN 0x07\n#define LANG_KR 0x14\n\n#define I18N_LANG LAN"
},
{
"path": "examples/AppStore/modules/misc/lang/i18n.cn.h",
"chars": 6859,
"preview": "#define WELCOME_MESSAGE \"Welcome to the \" PLATFORM_NAME \" App Store!\"\n#define INIT_MESSAGE PLATFORM_NAME \" App Store in"
},
{
"path": "examples/AppStore/modules/misc/lang/i18n.en.h",
"chars": 6858,
"preview": "#define WELCOME_MESSAGE \"Welcome to the \" PLATFORM_NAME \" App Store!\"\n#define INIT_MESSAGE PLATFORM_NAME \" App Store in"
},
{
"path": "examples/AppStore/modules/misc/lang/i18n.jp.h",
"chars": 6984,
"preview": "#define WELCOME_MESSAGE \"Welcome to the \" PLATFORM_NAME \" App Store!\"\n#define INIT_MESSAGE PLATFORM_NAME \" App Store in"
},
{
"path": "examples/AppStore/modules/misc/lang/i18n.kr.h",
"chars": 6858,
"preview": "#define WELCOME_MESSAGE \"Welcome to the \" PLATFORM_NAME \" App Store!\"\n#define INIT_MESSAGE PLATFORM_NAME \" App Store in"
},
{
"path": "examples/AppStore/platformio.ini",
"chars": 1530,
"preview": "[platformio]\nsrc_dir = main\n;default_envs = m5stack-fire\n;default_envs = m5stack-core-esp32\ndefault_envs = m5stack-core-"
},
{
"path": "examples/CopySketchToFS/CopySketchToFS.ino",
"chars": 1577,
"preview": "#include <ESP32-Chimera-Core.h>\n#define tft M5.Lcd\n\n#define SDU_APP_PATH \"/1_test_binary.bin\" // path to file on the SD\n"
},
{
"path": "examples/FactoryLauncher/FactoryLauncher.ino",
"chars": 2823,
"preview": "#include <SD.h>\n#include <SPIFFS.h>\n#include <LittleFS.h>\n#include <FFat.h>\n#include <ESP32-targz.h>\n\n#include <M5Unifie"
},
{
"path": "examples/FactoryLauncher/partitions.csv",
"chars": 1054,
"preview": "# 6 Apps + Factory\n# Name, Type, SubType, Offset, Size\nnvs, data, nvs, 0x9000, 0x5000,\notadata, "
},
{
"path": "examples/GzLauncher/GzLauncher.ino",
"chars": 2644,
"preview": "#include <ESP32-Chimera-Core.h> // https://github.com/tobozo/ESP32-Chimera-Core\n#define tft M5.Lcd\n\n#define DEST_FS_USES"
},
{
"path": "examples/Headless/Headless.ino",
"chars": 1822,
"preview": "#define SDU_APP_NAME \"Headless Example\"\n#define SDU_APP_PATH \"/Headless_Example.bin\"\n#define SDU_NO_AUTODETECT // don't "
},
{
"path": "examples/LGFX-SDLoader-Snippet/LGFX-SDLoader-Snippet.ino",
"chars": 4368,
"preview": "// M5Stack classic buttons/SD pinout\n#define SDU_BUTTON_A_PIN 39\n#define SDU_BUTTON_B_PIN 38\n#define SDU_BUTTON_C_PIN 37"
},
{
"path": "examples/LGFX-SDLoader-Snippet/M5Stack_Buttons.h",
"chars": 3092,
"preview": "#ifndef SDUButton_h\n#define SDUButton_h\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-variable\"\n"
},
{
"path": "examples/M5Core2-SDLoader-Snippet/M5Core2-SDLoader-Snippet.ino",
"chars": 619,
"preview": "#pragma GCC diagnostic ignored \"-Wmissing-field-initializers\"\n\n#include <M5Core2.h>\n#define SDU_APP_NAME \"M5Core2 SDLoad"
},
{
"path": "examples/M5Stack-FW-Menu/M5Stack-FW-Menu.ino",
"chars": 103,
"preview": "\n// dummy file to satisfy both Arduino IDE and platformio requirements\n// code is in src/main.cpp file\n"
},
{
"path": "examples/M5Stack-FW-Menu/ReadMe.md",
"chars": 2024,
"preview": "## Factory Partition Firmware Launcher\n\n/!\\ This app launcher depends on special partition schemes that include the fact"
},
{
"path": "examples/M5Stack-FW-Menu/partitions/partitions-16MB-factory-4-apps.csv",
"chars": 471,
"preview": "## 4 Apps + Factory\n## Name, Type, SubType, Offset, Size\nnvs, data, nvs, 0x9000, 0x5000\notadata, "
},
{
"path": "examples/M5Stack-FW-Menu/partitions/partitions-16MB-factory-6-apps.csv",
"chars": 560,
"preview": "# 6 Apps + Factory\n# Name, Type, SubType, Offset, Size\nnvs, data, nvs, 0x9000, 0x5000\notadata, d"
},
{
"path": "examples/M5Stack-FW-Menu/platformio.ini",
"chars": 1175,
"preview": "[platformio]\ndefault_envs = m5stack-cores3\nsrc_dir = src\n\n\n[env]\nframework = arduino\nboard "
},
{
"path": "examples/M5Stack-FW-Menu/src/main.cpp",
"chars": 28481,
"preview": "// load all supported filesystems\n#define SDU_USE_SD\n#define SDU_USE_SPIFFS\n#define SDU_USE_FFAT\n#define SDU_USE_LITTLEF"
},
{
"path": "examples/M5Stack-LittleFS-Snippet/M5Stack-LittleFS-Snippet.ino",
"chars": 3102,
"preview": "/*\n *\n * M5Stack LittleFS Loader Snippet\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2"
},
{
"path": "examples/M5Stack-SD-Menu/M5Stack-SD-Menu.ino",
"chars": 3471,
"preview": "/*\n *\n * M5Stack SD Menu\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2019 tobozo http:"
},
{
"path": "examples/M5Stack-SD-Menu/SAM.h",
"chars": 13094,
"preview": "#pragma once\n//#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\n\n#include <vector>\n\n#if !defined(ARDUINO_M5STA"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Colours_Demo.json",
"chars": 149,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"@Kongduino\", \"projectURL\": \"https://git.io/vptwc\",\"credits\":\"https://github.com"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/CrackScreen.json",
"chars": 156,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"nomolk\",\"projectURL\":\"https://git.io/vptXq\",\"credits\":\"https://github.com/nomolk"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Downloader.json",
"chars": 143,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"tobozo\",\"projectURL\":\"https://git.io/fhQ3F\",\"credits\":\"https://github.com/tobozo"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/FlappyBird.json",
"chars": 165,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Domenico Ponticelli\", \"projectURL\": \"https://git.io/vptPB\",\"credits\":\"https://g"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Lora_Frequency_Hopping.json",
"chars": 159,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"@Kongduino\", \"projectURL\": \"https://git.io/vptVQ\",\"credits\":\"https://github.com"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/LovyanLauncher.json",
"chars": 127,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"lovyan03\",\"projectURL\":\"https://git.io/fhdJV\",\"credits\":\"https://github.com/lovy"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/MultiApps-Adv.json",
"chars": 161,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"botofancalin\",\"projectURL\":\"https://git.io/vpu1r\",\"credits\":\"https://github.com/"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/NyanCat.json",
"chars": 139,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"tobozo\",\"projectURL\":\"https://git.io/vptXa\",\"credits\":\"https://github.com/tobozo"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/NyanCat_Ext.json",
"chars": 188,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"xisai\",\"projectURL\":\"https://git.io/vpQqA\",\"credits\":\"https://github.com/xisai/M"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Oscilloscope.json",
"chars": 166,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"@botofancalin\", \"projectURL\": \"https://git.io/vptVE\",\"credits\":\"https://github."
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/PacketMonitor.json",
"chars": 147,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Spacehuhn\", \"projectURL\":\"https://git.io/vptPd\",\"credits\":\"https://github.com/s"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Pacman-JoyPSP.json",
"chars": 173,
"preview": "{\"width\":110, \"height\":110, \"authorName\":\"Masahage\", \"projectURL\": \"https://git.io/vptX1\",\"credits\":\"Macsbug https://mac"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Pixel-Fun.json",
"chars": 136,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Harsh Talpada\", \"projectURL\": \"https://git.io/vptiz\",\"credits\":\"https://twitter"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Raytracer.json",
"chars": 141,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"tobozo\",\"projectURL\":\"https://git.io/fhQ35\",\"credits\":\"https://github.com/tobozo"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Rickroll.json",
"chars": 204,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"tobozo\",\"projectURL\":\"https://git.io/vptXS\",\"credits\":\"https://github.com/tobozo"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/RotateyCube.json",
"chars": 178,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"tobozo\",\"projectURL\":\"https://git.io/vptXN\",\"credits\":\"https://github.com/tobozo"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/SWRasterizer.json",
"chars": 151,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"gyabo\",\"projectURL\":\"https://git.io/fjL8E\",\"credits\":\"https://github.com/kumaash"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Sokoban.json",
"chars": 160,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Robo8080\", \"projectURL\": \"https://git.io/vpti5\",\"credits\":\"Masahage https://git"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/SpaceDefense.json",
"chars": 156,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Dmitry Dimi\", \"projectURL\": \"https://git.io/vptXp\",\"credits\":\"https://github.co"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/SpaceShooter.json",
"chars": 155,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"HailTheBDFL\", \"projectURL\": \"https://git.io/vpt1e\",\"credits\":\"https://github.co"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Tetris.json",
"chars": 282,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Masahage\", \"projectURL\": \"https://git.io/vpt1f\",\"credits\":\"https://github.com/M"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Thermal-Camera.json",
"chars": 156,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Offer DIYER\", \"projectURL\": \"https://git.io/vpjkL\",\"credits\":\"https://github.co"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/TobozoLauncher.json",
"chars": 164,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"tobozo\",\"projectURL\":\"https://git.io/vptwX\",\"credits\":\" github.com/reaper7 * * "
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/Tube.json",
"chars": 130,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"tobozo\",\"projectURL\":\"https://git.io/fhQ37\",\"credits\":\"https://github.com/tobozo"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/WiFiScanner.json",
"chars": 186,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"Dimi\",\"projectURL\":\"https://github.com/PartsandCircuits/M5Stack-WiFiScanner\",\"cr"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/arduinomegachess.json",
"chars": 188,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"ususovsv@gmail.com\",\"projectURL\":\"https://bit.ly/2F1l9Cm\",\"credits\":\"https://ww"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/d_invader.json",
"chars": 148,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Ohishi Nobuaki\", \"projectURL\": \"https://git.io/vpt6X\",\"credits\":\"https://github"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/drawNumber.json",
"chars": 139,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Kazuhiro Sasao\", \"projectURL\": \"https://git.io/vptVI\",\"credits\":\"https://gist.g"
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/menu.json",
"chars": 164,
"preview": "{\"width\":110,\"height\":110,\"authorName\":\"tobozo\",\"projectURL\":\"https://git.io/vptwX\",\"credits\":\" github.com/reaper7 * * "
},
{
"path": "examples/M5Stack-SD-Menu/SD-Content/json/mp3-player.json",
"chars": 137,
"preview": "{\"width\":110,\"height\":110, \"authorName\":\"Dimi Siberian\", \"projectURL\": \"https://git.io/vptoK\",\"credits\":\"https://github."
},
{
"path": "examples/M5Stack-SD-Menu/assets.h",
"chars": 59476,
"preview": "/*\n * Image Source: https://i.imgur.com/llemDHF.gif\n *\n * - ImageMagick:\n * #> convert -strip disk.gif disk%02d.jpg\n "
},
{
"path": "examples/M5Stack-SD-Menu/certificates.h",
"chars": 9732,
"preview": "#pragma once\n\n#if defined USE_DOWNLOADER\n\n/*\n\n ssl_host=\"phpsecure.info\" && \\\n prefix=\"const char* ${ssl_host//[^a-z"
},
{
"path": "examples/M5Stack-SD-Menu/compile_time.h",
"chars": 4368,
"preview": "/*\n * compile_time.h\n *\n * Created: 30.05.2017 20:57:58\n * Author: Dennis (instructable.com/member/nqtronix)\n *\n * This"
},
{
"path": "examples/M5Stack-SD-Menu/controls.h",
"chars": 9564,
"preview": "#ifndef __CONTROLS_H\n#define __CONTROLS_H\n\n/*\n * Mandatory and optional controls for the menu to be usable\n */\nenum HIDS"
},
{
"path": "examples/M5Stack-SD-Menu/core.h",
"chars": 2266,
"preview": "#pragma once\n\n//#include <FFat.h>\n#include <SD.h>\n\n#define ECC_NO_PRAGMAS // turn ESP32-Chimera-Core's pragma messages o"
},
{
"path": "examples/M5Stack-SD-Menu/downloader.h",
"chars": 47609,
"preview": "/*\n *\n * M5Stack SD Menu\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2019 tobozo http:"
},
{
"path": "examples/M5Stack-SD-Menu/fsformat.h",
"chars": 12636,
"preview": "/*\n *\n * M5Stack SD Menu\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2019 tobozo http:"
},
{
"path": "examples/M5Stack-SD-Menu/i18n.h",
"chars": 5304,
"preview": "#ifndef __I18N_H\n#define __I18N_H\n\n\n#define WELCOME_MESSAGE F(\"Welcome to the \" PLATFORM_NAME \" SD Menu Loader!\")\n#defin"
},
{
"path": "examples/M5Stack-SD-Menu/main/main.cpp",
"chars": 1090,
"preview": "#include \"../menu.h\"\n\n\nvoid setup()\n{\n\n #if defined __M5UNIFIED_HPP__\n M5.Log.setEnableColor(m5::log_target_serial, "
},
{
"path": "examples/M5Stack-SD-Menu/menu.h",
"chars": 34806,
"preview": "/*\n *\n * M5Stack SD Menu\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2019 tobozo http:"
},
{
"path": "examples/M5Stack-SD-Menu/partition_manager.h",
"chars": 8310,
"preview": "/*\n *\n * M5Stack SD Menu\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2019 tobozo http:"
},
{
"path": "examples/M5Stack-SD-Menu/partitions_16MB_4_or_6_apps.csv",
"chars": 1042,
"preview": "## 4 Apps + Factory\n## Name, Type, SubType, Offset, Size\n#nvs, data, nvs, 0x9000, 0x5000\n#otadata"
},
{
"path": "examples/M5Stack-SD-Menu/partitions_16MB_8apps.csv",
"chars": 1371,
"preview": "# 4 Apps + Factory\n# Name, Type, SubType, Offset, Size\nnvs, data, nvs, 0x9000, 0x5000\notadata, d"
},
{
"path": "examples/M5Stack-SD-Menu/platformio.ini",
"chars": 1725,
"preview": "[platformio]\nsrc_dir = main\n;default_envs = m5stack-fire\ndefault_envs = m5stack-core-esp32\n;default_envs = m5stack-core-"
},
{
"path": "examples/M5Stack-SD-Menu/registry.default.h",
"chars": 1382,
"preview": "// left in a separate file for easy editing / overwriting\n\n//see #define DEFAULT_REGISTRY_BOARD in menu.h\n\n#define DEFAU"
},
{
"path": "examples/M5Stack-SD-Menu/registry.h",
"chars": 4296,
"preview": "/*\n *\n * M5Stack SD Menu\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2019 tobozo http:"
},
{
"path": "examples/M5Stack-SD-Menu/wifi_manager.h",
"chars": 6582,
"preview": "#include <ESPmDNS.h>\n#include <WiFiClient.h>\n#include \"WebServer.h\"\n\n\nconst IPAddress apIP(192, 168, 4, 1);\nconst char* "
},
{
"path": "examples/M5Stack-SDLoader-Snippet/M5Stack-SDLoader-Snippet.ino",
"chars": 3950,
"preview": "/*\n *\n * M5Stack SD Loader Snippet\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2018 to"
},
{
"path": "examples/M5StickC-SPIFFS-Loader-Snippet/M5StickC-SPIFFS-Loader-Snippet.ino",
"chars": 3713,
"preview": "/*\n *\n * M5StickC SPIFFS Loader Snippet\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 20"
},
{
"path": "examples/M5Unified/M5Unified.ino",
"chars": 1114,
"preview": "\n#include <SD.h>\n#include <M5Unified.h>\n//#define TFCARD_CS_PIN 4\n#include <ESP32-targz.h> // optional: https://github.c"
},
{
"path": "examples/MultiFS/MultiFS.ino",
"chars": 1514,
"preview": "\n/*\n#include <SPIFFS.h>\n#include <LittleFS.h>\n#include <SD.h>\n#include <SD_MMC.h>\n#include <FFat.h>\n#include <SdFat.h>\n#"
},
{
"path": "examples/SdFatUpdater/SdFatUpdater.ino",
"chars": 4253,
"preview": "#define TFCARD_CS_PIN 4 // customize this\n\n\n#include <M5Unified.h> // Note: don't mix M5Unified with LovyanGFX\n//#includ"
},
{
"path": "examples/Test/build_test/dev_lib_deps.ini",
"chars": 857,
"preview": "[lib_sdupdater]\nlib_deps =\n M5Stack-SD-Updater\n ESP32-targz\n\n[lib_lgfx]\nlib_deps =\n SD\n git+https:"
},
{
"path": "examples/Test/build_test/main/main.cpp",
"chars": 727,
"preview": "\n\n\n//#include <stddef.h>\n\n#if defined TEST_LGFX\n #include \"../../../LGFX-SDLoader-Snippet/LGFX-SDLoader-Snippet.ino\"\n#e"
},
{
"path": "examples/Test/build_test/platformio.ini",
"chars": 2913,
"preview": "[platformio]\ndefault_envs = m5stack-core-esp32\nsrc_dir = main\nextra_configs = dev_lib_deps.ini\n\n[env]"
},
{
"path": "library.json",
"chars": 612,
"preview": "{\n \"name\": \"M5Stack-SD-Updater\",\n \"description\": \"Make your apps loadable from the SD card\",\n \"keywords\": [\"SD-Update"
},
{
"path": "library.properties",
"chars": 385,
"preview": "name=M5Stack-SD-Updater\nversion=1.2.8\nauthor=tobozo <tobozo@noreply.github.com>\nmaintainer=tobozo <tobozo@noreply.github"
},
{
"path": "src/ConfigManager/ConfigManager.cpp",
"chars": 3305,
"preview": "#include \"ConfigManager.hpp\"\n\nnamespace SDUpdaterNS\n{\n\n bool DigitalPinButton_t::changed()\n {\n static int lastbtnst"
},
{
"path": "src/ConfigManager/ConfigManager.hpp",
"chars": 5478,
"preview": "/*\n *\n * M5Stack SD Updater\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2018 tobozo ht"
},
{
"path": "src/FS/ffat.hpp",
"chars": 1402,
"preview": "#pragma once\n\n#if defined SDU_HAS_FFAT\n\n #include \"../misc/config.h\"\n #include \"../misc/types.h\"\n #include <FFat.h>\n\n"
},
{
"path": "src/FS/littlefs.hpp",
"chars": 1515,
"preview": "#pragma once\n\n#if defined SDU_HAS_LITTLEFS\n\n #include \"../misc/config.h\"\n #include \"../misc/types.h\"\n #include <Littl"
},
{
"path": "src/FS/sd.hpp",
"chars": 1702,
"preview": "#pragma once\n\n#if defined SDU_HAS_SD\n\n #include \"../misc/config.h\"\n #include \"../misc/types.h\"\n #include <SD.h>\n\n na"
},
{
"path": "src/FS/sd_mmc.hpp",
"chars": 2093,
"preview": "#pragma once\n\n#if defined SDU_HAS_SD_MMC\n\n #include \"../misc/config.h\"\n #include \"../misc/types.h\"\n #include <SD_MMC."
},
{
"path": "src/FS/sdfat.hpp",
"chars": 5774,
"preview": "#pragma once\n\n// fs::FS layer for SdFat\n// Inspired by @ockernuts https://github.com/ockernuts\n// See https://github.com"
},
{
"path": "src/FS/spiffs.hpp",
"chars": 1452,
"preview": "#pragma once\n\n#if defined SDU_HAS_SPIFFS\n\n #include \"../misc/config.h\"\n #include \"../misc/types.h\"\n #include <SPIFFS."
},
{
"path": "src/M5StackUpdater.h",
"chars": 1441,
"preview": "/*\\\n *\n * M5Stack SD Updater\n * Project Page: https://github.com/tobozo/M5Stack-SD-Updater\n *\n * Copyright 2018 tobozo h"
},
{
"path": "src/M5StackUpdater.hpp",
"chars": 22978,
"preview": "#pragma once\n#define __M5STACKUPDATER_H\n/*\n *\n * M5Stack SD Updater\n * Project Page: https://github.com/tobozo/M5Stack-S"
},
{
"path": "src/PartitionManager/NVS/NVSUtils.cpp",
"chars": 7341,
"preview": "/*\\\n *\n * NVS Dumper\n *\n * Copyleft tobozo 2020\n *\n * Inspired from the following implementations:\n *\n * - https://git"
},
{
"path": "src/PartitionManager/NVS/NVSUtils.hpp",
"chars": 2330,
"preview": "#pragma once\n#define SDU_NVSUTILS_HPP\n\n//#include <Arduino.h>\n//#include <Preferences.h>\n#include <cstring>\n#include <ct"
},
{
"path": "src/PartitionManager/PartitionManager.cpp",
"chars": 17285,
"preview": "\n//#include \"./PartitionManager.hpp\"\n#include \"../SDUpdater/SDUpdater_Class.hpp\"\n\n\nnamespace SDUpdaterNS\n{\n namespace P"
},
{
"path": "src/PartitionManager/PartitionManager.hpp",
"chars": 1251,
"preview": "#pragma once\n\n\n#include \"./NVS/NVSUtils.hpp\"\n#include \"./Partitions/PartitionUtils.hpp\"\n#include <FS.h>\n\n\nnamespace SDUp"
},
{
"path": "src/PartitionManager/Partitions/PartitionUtils.cpp",
"chars": 19810,
"preview": "#include \"./PartitionUtils.hpp\"\n#include \"../NVS/NVSUtils.hpp\"\n#include \"../../ConfigManager/ConfigManager.hpp\"\n\n\n#if !d"
},
{
"path": "src/PartitionManager/Partitions/PartitionUtils.hpp",
"chars": 3372,
"preview": "#pragma once\n\n#include <memory>\n#include <vector>\n#include <esp_partition.h>\n#include <esp_flash.h>\nextern \"C\" {\n #incl"
},
{
"path": "src/SDUpdater/SDUpdater_Class.cpp",
"chars": 11880,
"preview": "#include \"./SDUpdater_Class.hpp\"\n\nnamespace SDUpdaterNS\n{\n\n\n\n void SDUpdater::_error( const char **errMsgs, uint8_t msg"
},
{
"path": "src/SDUpdater/SDUpdater_Class.hpp",
"chars": 6197,
"preview": "#pragma once\n\n#include \"../gitTagVersion.h\"\n#include <esp_partition.h> // required by getSketchMeta(), compareFsPartitio"
},
{
"path": "src/SDUpdater/Update_Interface.hpp",
"chars": 2878,
"preview": "#pragma once\n\n\n#if defined SDU_HAS_TARGZ // binary may or may not be gzipped\n\n // some macros with if(gz) logic\n #defi"
},
{
"path": "src/UI/Touch.hpp",
"chars": 15985,
"preview": "#pragma once\n\n#include \"../misc/assets.h\"\n#include \"../misc/types.h\"\n#include \"../UI/common.hpp\"\n\nnamespace SDUpdaterNS\n"
},
{
"path": "src/UI/UI.hpp",
"chars": 15243,
"preview": "#pragma once\n\n#include \"../misc/assets.h\"\n#include \"../misc/types.h\"\n#include \"../UI/common.hpp\"\n\nnamespace SDUpdaterNS\n"
},
{
"path": "src/UI/common.hpp",
"chars": 12137,
"preview": "#pragma once\n\n#include \"../misc/types.h\"\n\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n#pragma GCC diagnostic igno"
},
{
"path": "src/gitTagVersion.h",
"chars": 663,
"preview": "#define SDU_VERSION_MAJOR 1\n#define SDU_VERSION_MINOR 2\n#define SDU_VERSION_PATCH 8\n#define _SDU_STR(x) #x\n#define SDU_S"
},
{
"path": "src/misc/assets.h",
"chars": 25420,
"preview": "#pragma once\n\n#include <stdint.h>\n\nconst uint16_t sdUpdaterIcon15x16_raw[] = {\n 0x0000, 0x1000, 0x3000, 0x107c, 0xffff,"
},
{
"path": "src/misc/config.h",
"chars": 2566,
"preview": "#pragma once\n\n#define ROLLBACK_LABEL \"Rollback\" // reload app from the \"other\" OTA partition\n#define LAUNCHER_LABEL "
},
{
"path": "src/misc/types.h",
"chars": 10306,
"preview": "#pragma once\n\n#include <stdint.h>\n#include <functional>\n#include <vector>\n#include <Stream.h>\n#include <WString.h>\n\n#def"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the tobozo/M5Stack-SD-Updater GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 168 files (846.5 KB), approximately 309.6k tokens, and a symbol index with 571 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.