-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added support for m5stick C plus 2 #4415
base: main
Are you sure you want to change the base?
Changes from 2 commits
a9a1df2
30689b8
2a0dd3e
64998f6
2b46fc0
4c2bfab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,312 @@ | ||
// Credits to @mrVanboy, @gwaland and my dearest friend @westward | ||
// Also for @spiff72 for usermod TTGO-T-Display | ||
// 210217 | ||
#pragma once | ||
|
||
#include "wled.h" | ||
#include <TFT_eSPI.h> | ||
#include <SPI.h> | ||
|
||
#ifndef USER_SETUP_LOADED | ||
#ifndef ST7789_DRIVER | ||
#error Please define ST7789_DRIVER | ||
#endif | ||
#ifndef TFT_WIDTH | ||
#error Please define TFT_WIDTH | ||
#endif | ||
#ifndef TFT_HEIGHT | ||
#error Please define TFT_HEIGHT | ||
#endif | ||
#ifndef TFT_DC | ||
#error Please define TFT_DC | ||
#endif | ||
#ifndef TFT_RST | ||
#error Please define TFT_RST | ||
#endif | ||
#ifndef LOAD_GLCD | ||
#error Please define LOAD_GLCD | ||
#endif | ||
#endif | ||
#ifndef TFT_BL | ||
#define TFT_BL -1 | ||
#endif | ||
|
||
#define USERMOD_ID_M5STICK_VISUALISER 541 | ||
|
||
TFT_eSPI tft = TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); // Invoke custom library | ||
|
||
// Extra char (+1) for null | ||
#define LINE_BUFFER_SIZE 20 | ||
|
||
// How often we are redrawing screen | ||
#define USER_LOOP_REFRESH_RATE_MS 50 | ||
|
||
extern int getSignalQuality(int rssi); | ||
|
||
|
||
//class name. Use something descriptive and leave the ": public Usermod" part :) | ||
class M5Stick2Visualiser : public Usermod { | ||
private: | ||
//Private class members. You can declare variables and functions only accessible to your usermod here | ||
unsigned long lastTime = 0; | ||
bool enabled = true; | ||
|
||
bool displayTurnedOff = false; | ||
long lastRedraw = 0; | ||
// needRedraw marks if redraw is required to prevent often redrawing. | ||
bool needRedraw = true; | ||
|
||
uint8_t knownBrightness = 0; | ||
uint8_t knownPalette = 0; | ||
uint8_t knownEffectSpeed = 0; | ||
uint8_t knownEffectIntensity = 0; | ||
uint8_t knownMode = 0; | ||
|
||
const uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2 | ||
long lastUpdate = 0; | ||
|
||
void center(String &line, uint8_t width) { | ||
int len = line.length(); | ||
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line; | ||
for (byte i=line.length(); i<width; i++) line += ' '; | ||
} | ||
|
||
public: | ||
//Functions called by WLED | ||
|
||
/* | ||
* setup() is called once at boot. WiFi is not yet connected at this point. | ||
* You can use it to initialize variables, sensors or similar. | ||
*/ | ||
void setup() | ||
{ | ||
DEBUG_PRINTLN("TFT pre init"); | ||
PinManagerPinType spiPins[] = { { spi_mosi, true }, { spi_miso, false}, { spi_sclk, true } }; | ||
DEBUG_PRINTLN("TFT try to allocate SPI"); | ||
if (!pinManager.allocateMultiplePins(spiPins, 3, PinOwner::HW_SPI)) { enabled = false; return; } | ||
PinManagerPinType displayPins[] = { { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } }; | ||
DEBUG_PRINTLN("TFT try to allocate display"); | ||
if (!pinManager.allocateMultiplePins(displayPins, sizeof(displayPins)/sizeof(PinManagerPinType), PinOwner::UM_FourLineDisplay)) { | ||
pinManager.deallocateMultiplePins(spiPins, 3, PinOwner::HW_SPI); | ||
enabled = false; | ||
return; | ||
} | ||
DEBUG_PRINTLN("TFT init"); | ||
|
||
tft.init(); | ||
tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip. | ||
tft.fillScreen(TFT_BLACK); | ||
|
||
if (TFT_BL >= 0) | ||
{ | ||
pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode | ||
digitalWrite(TFT_BL, HIGH); // Turn backlight on. | ||
} | ||
} | ||
|
||
/* | ||
* connected() is called every time the WiFi is (re)connected | ||
* Use it to initialize network interfaces | ||
*/ | ||
void connected() { | ||
//Serial.println("Connected to WiFi!"); | ||
} | ||
|
||
/* | ||
* loop() is called continuously. Here you can check for events, read sensors, etc. | ||
* | ||
* Tips: | ||
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection. | ||
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker. | ||
* | ||
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds. | ||
* Instead, use a timer check as shown here. | ||
*/ | ||
void loop() { | ||
char buff[LINE_BUFFER_SIZE]; | ||
|
||
tft.setTextSize(2); | ||
|
||
// Wifi name | ||
tft.setTextColor(TFT_GREEN); | ||
tft.setCursor(0, 60); | ||
|
||
// Check if we time interval for redrawing passes. | ||
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) | ||
{ | ||
return; | ||
} | ||
//tft.fillScreen(TFT_BLACK); | ||
lastUpdate = millis(); | ||
|
||
// Turn off display after 5 minutes with no change. | ||
if (!displayTurnedOff && millis() - lastRedraw > 5*60*1000) | ||
{ | ||
DEBUG_PRINTLN("Turn off display"); | ||
if (TFT_BL >= 0) digitalWrite(TFT_BL, LOW); // Turn backlight off. | ||
displayTurnedOff = true; | ||
} | ||
|
||
lastRedraw = millis(); | ||
|
||
knownBrightness = bri; | ||
knownMode = strip.getMainSegment().mode; | ||
knownPalette = strip.getMainSegment().palette; | ||
knownEffectSpeed = strip.getMainSegment().speed; | ||
knownEffectIntensity = strip.getMainSegment().intensity; | ||
tft.setTextSize(1); | ||
tft.setTextColor(TFT_GREEN); | ||
tft.setTextFont(1); | ||
tft.setCursor(100, 100); | ||
|
||
for(int i = 0; i < 16; i++) { | ||
tft.fillRect(fftResult[i] / 2, i*15 + 3, 135 - (fftResult[i] / 2), 10, TFT_BLACK); | ||
tft.fillRect(0, i*15 + 3, fftResult[i] / 2, 10, TFT_GOLD); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This way of referencing a variable inside the audioreactive usermod may not work in the future, when each usermod will have it's own namespace. The safer way is to use a local pointer to array um_data_t *um_data = getAudioData();
uint8_t *fftChannels = (uint8_t*)um_data->u_data[2]; Then you can simply use |
||
} | ||
|
||
tft.drawString("TEST", 0, 0); | ||
|
||
// Wifi name | ||
tft.setTextColor(TFT_GREEN); | ||
tft.setCursor(0, 60); | ||
// Print AP IP and password in AP mode or knownIP if AP not active. | ||
if (apActive) | ||
{ | ||
//DEBUG_PRINTLN("TFT PRINT AP INFO"); | ||
tft.setCursor(0, 84); | ||
tft.print("AP IP: "); | ||
tft.setCursor(0,108); | ||
tft.print("AP Pass:"); | ||
tft.print(apPass); | ||
|
||
// percent brightness | ||
tft.setCursor(0, 120); | ||
tft.setTextColor(TFT_WHITE); | ||
tft.print("Bri: "); | ||
tft.print((((int)bri*100)/255)); | ||
tft.print("%"); | ||
} | ||
|
||
// mode name | ||
tft.setTextColor(TFT_CYAN); | ||
tft.setCursor(0, 144); | ||
char lineBuffer[tftcharwidth+1]; | ||
extractModeName(knownMode, JSON_mode_names, lineBuffer, tftcharwidth); | ||
tft.drawString(lineBuffer, 0, 144); | ||
|
||
// palette name | ||
tft.setTextColor(TFT_YELLOW); | ||
tft.setCursor(0, 168); | ||
extractModeName(knownPalette, JSON_palette_names, lineBuffer, tftcharwidth); | ||
tft.drawString(lineBuffer, 0, 168); | ||
|
||
tft.setCursor(0, 192); | ||
tft.setTextColor(TFT_SILVER); | ||
sprintf_P(buff, PSTR("FX Spd:%3d Int:%3d"), effectSpeed, effectIntensity); | ||
tft.drawString(buff, 0, 192); | ||
|
||
// Fifth row with estimated mA usage | ||
tft.setTextColor(TFT_ORANGE); | ||
tft.setCursor(0, 216); | ||
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). | ||
sprintf_P(buff, PSTR("%3d mA"), strip.currentMilliamps); | ||
tft.drawString(buff, 0, 216); | ||
} | ||
|
||
/* | ||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. | ||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. | ||
* Below it is shown how this could be used for e.g. a light sensor | ||
*/ | ||
void addToJsonInfo(JsonObject& root) | ||
{ | ||
JsonObject user = root["u"]; | ||
if (user.isNull()) user = root.createNestedObject("u"); | ||
|
||
JsonArray lightArr = user.createNestedArray("ST7789"); //name | ||
lightArr.add(enabled?F("installed"):F("disabled")); //unit | ||
} | ||
|
||
|
||
/* | ||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). | ||
* Values in the state object may be modified by connected clients | ||
*/ | ||
void addToJsonState(JsonObject& root) | ||
{ | ||
//root["user0"] = userVar0; | ||
} | ||
|
||
|
||
/* | ||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). | ||
* Values in the state object may be modified by connected clients | ||
*/ | ||
void readFromJsonState(JsonObject& root) | ||
{ | ||
//userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value | ||
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!")); | ||
} | ||
|
||
|
||
/* | ||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. | ||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved) | ||
* If you want to force saving the current state, use serializeConfig() in your loop(). | ||
* | ||
* CAUTION: serializeConfig() will initiate a filesystem write operation. | ||
* It might cause the LEDs to stutter and will cause flash wear if called too often. | ||
* Use it sparingly and always in the loop, never in network callbacks! | ||
* | ||
* addToConfig() will also not yet add your setting to one of the settings pages automatically. | ||
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually. | ||
* | ||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! | ||
*/ | ||
void addToConfig(JsonObject& root) | ||
{ | ||
JsonObject top = root.createNestedObject("ST7789"); | ||
JsonArray pins = top.createNestedArray("pin"); | ||
pins.add(TFT_CS); | ||
pins.add(TFT_DC); | ||
pins.add(TFT_RST); | ||
pins.add(TFT_BL); | ||
//top["great"] = userVar0; //save this var persistently whenever settings are saved | ||
} | ||
|
||
|
||
void appendConfigData() { | ||
oappend(SET_F("addInfo('ST7789:pin[]',0,'','SPI CS');")); | ||
oappend(SET_F("addInfo('ST7789:pin[]',1,'','SPI DC');")); | ||
oappend(SET_F("addInfo('ST7789:pin[]',2,'','SPI RST');")); | ||
oappend(SET_F("addInfo('ST7789:pin[]',2,'','SPI BL');")); | ||
} | ||
|
||
/* | ||
* readFromConfig() can be used to read back the custom settings you added with addToConfig(). | ||
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot) | ||
* | ||
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes), | ||
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup. | ||
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :) | ||
*/ | ||
bool readFromConfig(JsonObject& root) | ||
{ | ||
//JsonObject top = root["top"]; | ||
//userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot) | ||
return true; | ||
} | ||
|
||
|
||
/* | ||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). | ||
* This could be used in the future for the system to determine whether your usermod is installed. | ||
*/ | ||
uint16_t getId() | ||
{ | ||
return USERMOD_ID_M5STICK_VISUALISER; | ||
} | ||
|
||
//More methods can be added in the future, this example will then be extended. | ||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class! | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Using the M5Stick C Plus2 | ||
Connect the stick to 5V via your favourite method (USB-C/5V). DO NOT POWER THE LEDS FROM THE STICK OR ITS BATTERY! | ||
|
||
## Hardware | ||
|
||
*** | ||
 | ||
|
||
Connect the data pin of your strips to any of the GPIO pins. Connect GND to any of the GND pins. | ||
|
||
## Library used | ||
|
||
Used the eSPI TFT library directly, as adding the M5Stack library would cause too much of an overhead. | ||
|
||
[Bodmer/TFT_eSPI](https://github.com/Bodmer/TFT_eSPI) | ||
|
||
## Setup | ||
|
||
In the `platformio.ini` file, you must change the environment setup to build for just the m5stick platform by adding the following target and commeting the other ones: | ||
|
||
``` | ||
[env:m5stick] | ||
board = esp32dev | ||
build_unflags = ${common.build_unflags} | ||
build_flags = ${common.build_flags_esp32} | ||
-D BTNPIN=39 | ||
-D LEDPIN=19 | ||
-D IRPIN=19 | ||
-D WLED_DEBUG=1 | ||
softhack007 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# Display config | ||
-D ST7789_DRIVER=1 | ||
-D USERMOD_M5STICK2_VISUALISER=1 | ||
-D TFT_WIDTH=135 | ||
-D TFT_HEIGHT=240 | ||
-D TFT_MOSI=15 | ||
-D TFT_BL=27 | ||
-D TFT_SCLK=13 | ||
-D TFT_DC=14 | ||
-D TFT_RST=12 | ||
-D TFT_CS=5 | ||
-D SPI_FREQUENCY=20000000 | ||
-D USER_SETUP_LOADED | ||
-D USERMOD_AUDIOREACTIVE -D UM_AUDIOREACTIVE_USE_NEW_FFT | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi, this is the "old way" for activating the audioreactive usermod. It's better if you use references to standard flags. Also you may want to to add flags for the onboard mic pins. ${esp32.AR_build_flags} ;; default flags required to properly configure ArduinoFFT
-D I2S_SDPIN=34 -D I2S_WSPIN=0 -D I2S_CKPIN=-1 -D MCLK_PIN=-1 ;; PDM microphone pins
-D DMTYPE=5 -D SR_SQUELCH=5 -D SR_GAIN=75 ;; SPM1423 specific sound settings
-D I2S_USE_RIGHT_CHANNEL ;; might be needed, if your microphone uses the right channel instead of left
-D UM_AUDIOREACTIVE_ENABLE ;; enable AR by default The example is actually from the M5StickC, so your pins might be different. For your info, the full buildenv is from here: https://github.com/atuline/WLED/blob/ce6b9d80273575419fecf2a752162f08e28361e0/platformio.ini#L545 I also remember that some M5 boards needed a special library and call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, both the tft and the mic worked without the M5 library. I wanted to avoid it as it would add too much bloat. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so please use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added, my bad. |
||
lib_deps = | ||
${esp32.lib_deps} | ||
TFT_eSPI @ ^2.3.70 | ||
https://github.com/kosme/arduinoFFT#419d7b0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here - you need to use ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE |
||
platform = ${esp32.platform} | ||
platform_packages = ${esp32.platform_packages} | ||
board_build.partitions = ${esp32.default_partitions} | ||
``` | ||
|
||
Compile and run. Turn it on, enable the audioreactive mod from the webUI. Make sure the values for the display and I2C mic as follows down below: | ||
|
||
 | ||
|
||
 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try to avoid magic numbers, e.g 16, use NUM_GEQ_CHANNELS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, ty