This firmware's structure follows the KISS principle. The code is extremely simple and structured. All functions use my wrappers, allowing you to quickly add new "programs" while focusing on the functionality itself, without wasting time interacting with the interface.
You can easily add a new function using the short guide or by looking on already created funcs in functions/ folder.
You can download firmware from M5Burner, build firmware by yourself or download binary from github releases (see the instructions below).
You can use the M5Burner for simple firmware installation. Select m5stick in left side menu and start writing in the search line "Crystal".
- Add clock setting
- Add text size setting
- Add font style setting
- New languages
- Create new battery indicator
- Level tool based on gyro (why not?)
- Add NFC features
- Add IR features
- Structure functions, by the domain or by belonging to the category
- Create PID categories (like http status codes)
- Complete and structure the documentation
- Rerwrite to M5Unified
- Set up linting
- Rethract the web interface(beta)
- Support new devices
Libraries:
arduino-cli lib install --git-url https://github.com/T-vK/ESP32-BLE-Keyboard
Compile:
arduino-cli compile --fqbn m5stack:esp32:m5stack_stickc_plus2 --build-path ./build -e --build-property build.partitions=huge_app --build-property upload.maximum_size=3145728 ./m5stick_crystal_firmware.ino
Merge binaries:
esptool --chip esp32s3 merge_bin --output build/firmware.bin 0x1000 build/m5stick_crystal_firmware.ino.bootloader.bin 0x8000 build/m5stick_crystal_firmware.ino.partitions.bin 0x10000 build/m5stick_crystal_firmware.ino.bin
Flash firmware:
esptool write_flash 0 build/firmware.bin
You can also use the extension arduino community edition for build and upload firmware in VS code.
Here is example of configuration files for this extension which I'm using:
.vscode/arduino.json
{
"sketch": "m5stick_crystal_firmware.ino",
"port": "/dev/ttyACM0",
"board": "m5stack:esp32:m5stack_stickc_plus2",
"output": "./build",
"programmer": "esptool",
"configuration": "PSRAM=enabled,PartitionScheme=default_8MB,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=8M,UploadSpeed=1500000,LoopCore=1,EventsCore=1,DebugLevel=none,EraseFlash=none"
}
.vscode/settings.json
{
"C_Cpp.intelliSenseEngine": "Tag Parser",
"C_Cpp.default.includePath": [
"${workspaceRoot}",
"~/Arduino/libraries"
],
"arduino.enableUSBDetection": true,
"arduino.additionalUrls": ["https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json"],
"arduino.disableIntelliSenseAutoGen": true
}
Add a menu item wherever you want. You must specify the name and number of the process that will be launched. You can take the first one available. Check the process id list.
functions/mainMenuLoop.h
MENU mainMenu[] = {
// other items ...
{40, "Hello world!"},
};
After that, create a function for the process, let's create a new file for this in the functions directory
functions/helloWorldLoop.h
void helloWorldLoop() {
if (isSetup()) {
centeredPrint("Hello world!", SMALL_TEXT);
}
checkExit(0);
}
In this code, we used functions, one of the “utilities”, necessary to make the code easier and cleaner. They are all located inside files in the utils directory. Some utilities are already described in the utilities documentation.
Add an include of this function next to the others.
globals/functions.h
// other includes...
#include "../functions/helloWorldLoop.h"
Finally, add your process to the processEntries array.
globals/switcher.h
const ProcessEntry processEntries[] = {
// ...
{40, helloWorldLoop},
}
- mainMenu
- clock
- battery info
- settingsMenu
- Wi-Fi AP
- brightness
- rotation
- colors
- Wi-Fi scan
- Wi-Fi network menu
- settings statusBar
- Wi-Fi deauth
- Wi-Fi info
- Wi-Fi Menu
- Bluetooth Menu
- Bluetooth camera shutter
Only a small part of the utilities is described here
| Utility name and description | Code example |
|---|---|
isBtnBWasPressedisBtnAWasPressedChecks physical clicks and clicks from the web interface. |
if (isBtnAWasPressed()) {Serial.print("btn A pressed!")} |
pressBtnBpressBtnAEmulates a button press. You can try it with utility isBtn..WasPressed) |
pressBtnA(); |
isSetupEnsures that code within a condition is executed once when the program starts. |
if (isSetup()) {Serial.print("run only ones!")} |
checkExitIf the user exits the program (presses button B) the process will be switched to the previous one (or pass the ID manually). |
checkExit(3) |
changeProcessSwitches the process to the one passed in parameters |
changeProcess(0) |
setDataWrites data to internal memory. Pass the key and value. |
setData("brightness", 10); |
getDataGet data from internal memory. Pass a key, and a default value that will be returned if the data does not exist. |
int brightness = 1; |
isWebDataRequestedChecks whether a new line should be generated describing the current state of the interface. You will most likely need to pass the string "function" as the first argument. The second argument is a string that describes the contents of the interface. |
if (isWebDataRequested()) {webData = generateWebData("function", generateFunctionElement("text", SMALL_TEXT, "center"));} |
generateFunctionElementCreates and returns a string that describes the text parameters to be displayed inside the web interface. The first parameter is a line with text, the second is size, the third is centering. The resulting strings can be combined. |
generateFunctionElement("text", SMALL_TEXT, "center") |
I need your support and experience in contributing. Any form of assistance is welcome. Ask any questions through Issues or Disscusions.



