Merge remote-tracking branch 'origin/ac_main' into mdev

This commit is contained in:
Ewoud
2022-11-15 12:47:32 +01:00
30 changed files with 3841 additions and 3545 deletions

View File

@@ -44,6 +44,8 @@ private:
// configuration (available in API and stored in flash)
bool enabled = false;
Segment mainSegment;
bool hourMarksEnabled = true;
uint32_t hourMarkColor = 0xFF0000;
uint32_t hourColor = 0x0000FF;
uint32_t minuteColor = 0x00FF00;
bool secondsEnabled = true;
@@ -87,10 +89,9 @@ private:
}
bool hexStringToColor(String const& s, uint32_t& c, uint32_t def) {
errno = 0;
char* ep;
char *ep;
unsigned long long r = strtoull(s.c_str(), &ep, 16);
if (*ep == 0 && errno != ERANGE) {
if (*ep == 0) {
c = r;
return true;
} else {
@@ -162,11 +163,18 @@ public:
lastOverlayDraw = millis();
auto time = toki.getTime();
auto localSec = tz ? tz->toLocal(time.sec) : time.sec;
double secondP = second(localSec) / 60.0;
double minuteP = minute(localSec) / 60.0;
double hourP = (hour(localSec) % 12) / 12.0 + minuteP / 12.0;
double secondP = second(localTime) / 60.0;
double minuteP = minute(localTime) / 60.0;
double hourP = (hour(localTime) % 12) / 12.0 + minuteP / 12.0;
if (hourMarksEnabled) {
for (int Led = 0; Led <= 55; Led = Led + 5)
{
int16_t hourmarkled = adjustToSegment(Led / 60.0, mainSegment);
setPixelColor(hourmarkled, hourMarkColor);
}
}
if (secondsEnabled) {
int16_t secondLed = adjustToSegment(secondP, secondsSegment);
@@ -200,6 +208,8 @@ public:
top["First LED (Main Ring)"] = mainSegment.firstLed;
top["Last LED (Main Ring)"] = mainSegment.lastLed;
top["Center/12h LED (Main Ring)"] = mainSegment.centerLed;
top["Hour Marks Enabled"] = hourMarksEnabled;
top["Hour Mark Color (RRGGBB)"] = colorToHexString(hourMarkColor);
top["Hour Color (RRGGBB)"] = colorToHexString(hourColor);
top["Minute Color (RRGGBB)"] = colorToHexString(minuteColor);
top["Show Seconds"] = secondsEnabled;
@@ -221,6 +231,8 @@ public:
configComplete &= getJsonValue(top["First LED (Main Ring)"], mainSegment.firstLed, 0);
configComplete &= getJsonValue(top["Last LED (Main Ring)"], mainSegment.lastLed, 59);
configComplete &= getJsonValue(top["Center/12h LED (Main Ring)"], mainSegment.centerLed, 0);
configComplete &= getJsonValue(top["Hour marks Enabled"], hourMarksEnabled, false);
configComplete &= getJsonValue(top["Hour mark Color (RRGGBB)"], color, "FF0000") && hexStringToColor(color, hourMarkColor, 0x0000FF);
configComplete &= getJsonValue(top["Hour Color (RRGGBB)"], color, "0000FF") && hexStringToColor(color, hourColor, 0x0000FF);
configComplete &= getJsonValue(top["Minute Color (RRGGBB)"], color, "00FF00") && hexStringToColor(color, minuteColor, 0x00FF00);
configComplete &= getJsonValue(top["Show Seconds"], secondsEnabled, true);

View File

@@ -0,0 +1,34 @@
# SD-card mod
## Build
- modify `platformio.ini` and add to the `build_flags` of your configuration the following
- choose the way your SD is connected
1. via `-D WLED_USE_SD_MMC` when connected via MMC
2. via `-D WLED_USE_SD_SPI` when connected via SPI (use usermod page to setup SPI pins)
### Test
- enable `-D SD_PRINT_HOME_DIR` and `-D WLED_DEBUG`
- this will print all files in `/` on boot via serial
## Configuration
### MMC
- The MMC port / pins needs no configuration as they are specified by Espressif
### SPI
- The SPI port / pins can be modified via the WLED web-UI: `Config → Usermod → SD Card`
| option | effect | default |
| ----------------- | ------------------------------------------------------------------------------------------------ | ------- |
| `pinSourceSelect` | GPIO that is connected to SD's `SS`(source select) / `CS`(chip select) | 16 |
| `pinSourceClock` | GPIO that is connected to SD's `SCLK` (source clock) / `CLK`(clock) | 14 |
| `pinPoci` | GPIO that is connected to SD's `POCI`<sup>☨</sup> (Peripheral-Out-Ctrl-In) / `MISO` (deprecated) | 36 |
| `pinPico` | GPIO that is connected to SD's `PICO`<sup>☨</sup> (Peripheral-In-Ctrl-Out) / `MOSI` (deprecated) | 14 |
| `sdEnable` | Enable to read data from the SD-card | true |
<sup>☨</sup><sub>Following new naming convention of [OSHWA](https://www.oshwa.org/a-resolution-to-redefine-spi-signal-names/)</sub>
## Usage in other mods
- creates a macro `SD_ADAPTER` which is either mapped to `SD` or `SD_MMC` (see `SD_Test.ino` how to use SD / SD_MMC functions)
- checks if the specified file is available on the SD card
```cpp
bool file_onSD(const char *filepath) {...}
```

View File

@@ -0,0 +1,243 @@
#pragma once
#include "wled.h"
// SD connected via MMC / SPI
#if defined(WLED_USE_SD_MMC)
#define USED_STORAGE_FILESYSTEMS "SD MMC, LittleFS"
#define SD_ADAPTER SD_MMC
#include "SD_MMC.h"
// SD connected via SPI (adjustable via usermod config)
#elif defined(WLED_USE_SD_SPI)
#define SD_ADAPTER SD
#define USED_STORAGE_FILESYSTEMS "SD SPI, LittleFS"
#include "SD.h"
#include "SPI.h"
#endif
#ifdef WLED_USE_SD_MMC
#elif defined(WLED_USE_SD_SPI)
SPIClass spiPort = SPIClass(VSPI);
#endif
void listDir( const char * dirname, uint8_t levels);
class UsermodSdCard : public Usermod {
private:
bool sdInitDone = false;
#ifdef WLED_USE_SD_SPI
int8_t configPinSourceSelect = 16;
int8_t configPinSourceClock = 14;
int8_t configPinPoci = 36; // confusing names? Then have a look :)
int8_t configPinPico = 15; // https://www.oshwa.org/a-resolution-to-redefine-spi-signal-names/
//acquired and initialize the SPI port
void init_SD_SPI()
{
if(!configSdEnabled) return;
if(sdInitDone) return;
PinManagerPinType pins[5] = {
{ configPinSourceSelect, true },
{ configPinSourceClock, true },
{ configPinPoci, false },
{ configPinPico, true }
};
if (!pinManager.allocateMultiplePins(pins, 4, PinOwner::UM_SdCard)) {
DEBUG_PRINTF("[%s] SD (SPI) pin allocation failed!\n", _name);
sdInitDone = false;
return;
}
bool returnOfInitSD = false;
#if defined(WLED_USE_SD_SPI)
spiPort.begin(configPinSourceClock, configPinPoci, configPinPico, configPinSourceSelect);
returnOfInitSD = SD_ADAPTER.begin(configPinSourceSelect, spiPort);
#endif
if(!returnOfInitSD) {
DEBUG_PRINTF("[%s] SPI begin failed!\n", _name);
sdInitDone = false;
return;
}
sdInitDone = true;
}
//deinitialize the acquired SPI port
void deinit_SD_SPI()
{
if(!sdInitDone) return;
SD_ADAPTER.end();
DEBUG_PRINTF("[%s] deallocate pins!\n", _name);
pinManager.deallocatePin(configPinSourceSelect, PinOwner::UM_SdCard);
pinManager.deallocatePin(configPinSourceClock, PinOwner::UM_SdCard);
pinManager.deallocatePin(configPinPoci, PinOwner::UM_SdCard);
pinManager.deallocatePin(configPinPico, PinOwner::UM_SdCard);
sdInitDone = false;
}
// some SPI pin was changed, while SPI was initialized, reinit to new port
void reinit_SD_SPI()
{
deinit_SD_SPI();
init_SD_SPI();
}
#endif
#ifdef WLED_USE_SD_MMC
void init_SD_MMC() {
if(sdInitDone) return;
bool returnOfInitSD = false;
returnOfInitSD = SD_ADAPTER.begin();
DEBUG_PRINTF("[%s] MMC begin\n", _name);
if(!returnOfInitSD) {
DEBUG_PRINTF("[%s] MMC begin failed!\n", _name);
sdInitDone = false;
return;
}
sdInitDone = true;
}
#endif
public:
static bool configSdEnabled;
static const char _name[];
void setup() {
DEBUG_PRINTF("[%s] usermod loaded \n", _name);
#if defined(WLED_USE_SD_SPI)
init_SD_SPI();
#elif defined(WLED_USE_SD_MMC)
init_SD_MMC();
#endif
#if defined(SD_ADAPTER) && defined(SD_PRINT_HOME_DIR)
listDir("/", 0);
#endif
}
void loop(){
}
uint16_t getId()
{
return USERMOD_ID_SD_CARD;
}
void addToConfig(JsonObject& root)
{
#ifdef WLED_USE_SD_SPI
JsonObject top = root.createNestedObject(FPSTR(_name));
top["pinSourceSelect"] = configPinSourceSelect;
top["pinSourceClock"] = configPinSourceClock;
top["pinPoci"] = configPinPoci;
top["pinPico"] = configPinPico;
top["sdEnabled"] = configSdEnabled;
#endif
}
bool readFromConfig(JsonObject &root)
{
#ifdef WLED_USE_SD_SPI
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINTF("[%s] No config found. (Using defaults.)\n", _name);
return false;
}
uint8_t oldPinSourceSelect = configPinSourceSelect;
uint8_t oldPinSourceClock = configPinSourceClock;
uint8_t oldPinPoci = configPinPoci;
uint8_t oldPinPico = configPinPico;
bool oldSdEnabled = configSdEnabled;
getJsonValue(top["pinSourceSelect"], configPinSourceSelect);
getJsonValue(top["pinSourceClock"], configPinSourceClock);
getJsonValue(top["pinPoci"], configPinPoci);
getJsonValue(top["pinPico"], configPinPico);
getJsonValue(top["sdEnabled"], configSdEnabled);
if(configSdEnabled != oldSdEnabled) {
configSdEnabled ? init_SD_SPI() : deinit_SD_SPI();
DEBUG_PRINTF("[%s] SD card %s\n", _name, configSdEnabled ? "enabled" : "disabled");
}
if( configSdEnabled && (
oldPinSourceSelect != configPinSourceSelect ||
oldPinSourceClock != configPinSourceClock ||
oldPinPoci != configPinPoci ||
oldPinPico != configPinPico)
)
{
DEBUG_PRINTF("[%s] Init SD card based of config\n", _name);
DEBUG_PRINTF("[%s] Config changes \n - SS: %d -> %d\n - MI: %d -> %d\n - MO: %d -> %d\n - En: %d -> %d\n", _name, oldPinSourceSelect, configPinSourceSelect, oldPinSourceClock, configPinSourceClock, oldPinPoci, configPinPoci, oldPinPico, configPinPico);
reinit_SD_SPI();
}
#endif
return true;
}
};
const char UsermodSdCard::_name[] PROGMEM = "SD Card";
bool UsermodSdCard::configSdEnabled = true;
#ifdef SD_ADAPTER
//checks if the file is available on SD card
bool file_onSD(const char *filepath)
{
#ifdef WLED_USE_SD_SPI
if(!UsermodSdCard::configSdEnabled) return false;
#endif
uint8_t cardType = SD_ADAPTER.cardType();
if(cardType == CARD_NONE) {
DEBUG_PRINTF("[%s] not attached / cardType none\n", UsermodSdCard::_name);
return false; // no SD card attached
}
if(cardType == CARD_MMC || cardType == CARD_SD || cardType == CARD_SDHC)
{
return SD_ADAPTER.exists(filepath);
}
return false; // unknown card type
}
void listDir( const char * dirname, uint8_t levels){
DEBUG_PRINTF("Listing directory: %s\n", dirname);
File root = SD_ADAPTER.open(dirname);
if(!root){
DEBUG_PRINTF("Failed to open directory\n");
return;
}
if(!root.isDirectory()){
DEBUG_PRINTF("Not a directory\n");
return;
}
File file = root.openNextFile();
while(file){
if(file.isDirectory()){
DEBUG_PRINTF(" DIR : %s\n",file.name());
if(levels){
listDir(file.name(), levels -1);
}
} else {
DEBUG_PRINTF(" FILE: %s SIZE: %d\n",file.name(), file.size());
}
file = root.openNextFile();
}
}
#endif