Skip to content
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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
312 changes: 312 additions & 0 deletions usermods/M5Stick2_Visualiser/M5Stick2_Visualiser.h
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++) {
Copy link
Member

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

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, ty

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);
Copy link
Collaborator

@softhack007 softhack007 Dec 22, 2024

Choose a reason for hiding this comment

The 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 fftChannels[i] to access the data. If audioreactive is disabled, you will automatically get "soundsim" like in any other effect.

}

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!
};
57 changes: 57 additions & 0 deletions usermods/M5Stick2_Visualiser/README.md
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

***
![Hardware](images/m5stick_c_plus2.webp)

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
Copy link
Collaborator

@softhack007 softhack007 Dec 22, 2024

Choose a reason for hiding this comment

The 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 M5.begin() to power on microphone and other build-in devices. Is this the case for your board, too?

https://github.com/atuline/WLED/blob/ce6b9d80273575419fecf2a752162f08e28361e0/wled00/wled.cpp#L360-L365

Copy link
Author

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator

@softhack007 softhack007 Jan 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so please use ${esp32.AR_build_flags}, instead of -D USERMOD_AUDIOREACTIVE -D UM_AUDIOREACTIVE_USE_NEW_FFT

Copy link
Author

Choose a reason for hiding this comment

The 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
Copy link
Collaborator

@softhack007 softhack007 Dec 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here - you need to use ${esp32.AR_lib_deps} , instead of referencing a commit tag in ArduinoFFT

   ${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:

![Microphone setup](images/mic.jpg)

![Display setup](images/display.jpg)
Binary file added usermods/M5Stick2_Visualiser/images/display.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file added usermods/M5Stick2_Visualiser/images/mic.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions wled00/usermods_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@
#include "../usermods/LD2410_v2/usermod_ld2410.h"
#endif

#ifdef USERMOD_M5STICK2_VISUALISER
#include "../usermods/M5Stick2_Visualiser/M5Stick2_Visualiser.h"
#endif

void registerUsermods()
{
/*
Expand Down Expand Up @@ -470,4 +474,8 @@ void registerUsermods()
#ifdef USERMOD_POV_DISPLAY
UsermodManager::add(new PovDisplayUsermod());
#endif

#ifdef USERMOD_M5STICK2_VISUALISER
usermods.add(new M5Stick2Visualiser());
#endif
}
Loading