Compare commits
32 Commits
AR-MoonMod
...
bugfix_444
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
402ebb4b1e | ||
|
|
bb0c0af189 | ||
|
|
709aeff9ea | ||
|
|
34f18122f5 | ||
|
|
7208282431 | ||
|
|
204e72e9eb | ||
|
|
f626dfb7b7 | ||
|
|
08b263bf4e | ||
|
|
d0e99923fd | ||
|
|
1750512477 | ||
|
|
5e3b4c3a11 | ||
|
|
0c431d9746 | ||
|
|
438c5d9909 | ||
|
|
7d29edf6f4 | ||
|
|
50d505b896 | ||
|
|
2e06f5b1e8 | ||
|
|
3adcbb7904 | ||
|
|
dcf89e0dbd | ||
|
|
35d92f43c0 | ||
|
|
12db60885f | ||
|
|
d637260dc3 | ||
|
|
0937064e18 | ||
|
|
56e1d577fd | ||
|
|
1a82a3bf7b | ||
|
|
a2d84886c0 | ||
|
|
97e8382a41 | ||
|
|
8f8afd98a5 | ||
|
|
0db47a8586 | ||
|
|
cec8978886 | ||
|
|
0160e3fa87 | ||
|
|
32eee3365a | ||
|
|
95b4bde918 |
@@ -2,12 +2,7 @@
|
||||
|
||||
# [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6
|
||||
ARG VARIANT="3"
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
|
||||
|
||||
# [Option] Install Node.js
|
||||
ARG INSTALL_NODE="true"
|
||||
ARG NODE_VERSION="lts/*"
|
||||
RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
|
||||
FROM mcr.microsoft.com/devcontainers/python:0-${VARIANT}
|
||||
|
||||
# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
|
||||
# COPY requirements.txt /tmp/pip-tmp/
|
||||
|
||||
@@ -5,10 +5,7 @@
|
||||
"context": "..",
|
||||
"args": {
|
||||
// Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8, 3.9
|
||||
"VARIANT": "3",
|
||||
// Options
|
||||
"INSTALL_NODE": "true",
|
||||
"NODE_VERSION": "lts/*"
|
||||
"VARIANT": "3"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -54,7 +51,7 @@
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "npm install",
|
||||
"postCreateCommand": "bash -i -c 'nvm install && npm ci'",
|
||||
|
||||
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "vscode"
|
||||
|
||||
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -38,6 +38,7 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- name: Cache PlatformIO
|
||||
@@ -74,7 +75,7 @@ jobs:
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
|
||||
40
.github/workflows/nightly.yml
vendored
Normal file
40
.github/workflows/nightly.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
name: Deploy Nightly
|
||||
on:
|
||||
# This can be used to automatically publish nightlies at UTC nighttime
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
# This can be used to allow manually triggering nightlies from the web interface
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
wled_build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
nightly:
|
||||
name: Deploy nightly
|
||||
runs-on: ubuntu-latest
|
||||
needs: wled_build
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
- name: Show Files
|
||||
run: ls -la
|
||||
- name: "✏️ Generate release changelog"
|
||||
id: changelog
|
||||
uses: janheinrichmerker/action-github-changelog-generator@v2.3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sinceTag: v0.15.0
|
||||
- name: Update Nightly Release
|
||||
uses: andelf/nightly-release@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: nightly
|
||||
name: 'Nightly Release $$'
|
||||
prerelease: true
|
||||
body: ${{ steps.changelog.outputs.changelog }}
|
||||
files: |
|
||||
./*.bin
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
@title MoonModules WLED - audioreactive usermod
|
||||
@file audio_source.h
|
||||
@repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED
|
||||
@Authors https://github.com/MoonModules/WLED/commits/mdev/
|
||||
@Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details)
|
||||
@license Licensed under the EUPL-1.2 or later
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <Wire.h>
|
||||
#include "wled.h"
|
||||
#include <driver/i2s.h>
|
||||
#include <driver/adc.h>
|
||||
@@ -28,9 +15,6 @@
|
||||
#define SRate_t int
|
||||
#endif
|
||||
|
||||
constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not change! I2S_NUM_1 possible but this has
|
||||
// strong limitations -> no MCLK routing, no ADC support, no PDM support
|
||||
|
||||
//#include <driver/i2s_std.h>
|
||||
//#include <driver/i2s_pdm.h>
|
||||
//#include <driver/i2s_tdm.h>
|
||||
@@ -38,7 +22,7 @@ constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not c
|
||||
|
||||
// see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/chip-series-comparison.html#related-documents
|
||||
// and https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#overview-of-all-modes
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265)
|
||||
// there are two things in these MCUs that could lead to problems with audio processing:
|
||||
// * no floating point hardware (FPU) support - FFT uses float calculations. If done in software, a strong slow-down can be expected (between 8x and 20x)
|
||||
// * single core, so FFT task might slow down other things like LED updates
|
||||
@@ -65,11 +49,6 @@ constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not c
|
||||
// data type requested from the I2S driver - currently we always use 32bit
|
||||
//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible
|
||||
|
||||
#if defined(WLED_ENABLE_HUB75MATRIX) && defined(CONFIG_IDF_TARGET_ESP32)
|
||||
// this is bitter, but necessary to survive
|
||||
#define I2S_USE_16BIT_SAMPLES
|
||||
#endif
|
||||
|
||||
#ifdef I2S_USE_16BIT_SAMPLES
|
||||
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT
|
||||
#define I2S_datatype int16_t
|
||||
@@ -92,7 +71,7 @@ constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not c
|
||||
* if you want to receive two channels, one is the actual data from microphone and another channel is suppose to receive 0, it's different data in two channels, you need to choose I2S_CHANNEL_FMT_RIGHT_LEFT in this case.
|
||||
*/
|
||||
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 8)) // should be fixed in IDF 4.4.5, however arduino-esp32 2.0.14 - 2.0.17 did an "I2S rollback" to 4.4.4
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 6))
|
||||
// espressif bug: only_left has no sound, left and right are swapped
|
||||
// https://github.com/espressif/esp-idf/issues/9635 I2S mic not working since 4.4 (IDFGH-8138)
|
||||
// https://github.com/espressif/esp-idf/issues/8538 I2S channel selection issue? (IDFGH-6918)
|
||||
@@ -126,9 +105,6 @@ constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not c
|
||||
#endif
|
||||
|
||||
|
||||
// max number of samples for a single i2s_read --> size of global buffer.
|
||||
#define I2S_SAMPLES_MAX 512 // same as samplesFFT
|
||||
|
||||
/* Interface class
|
||||
AudioSource serves as base class for all microphone types
|
||||
This enables accessing all microphones with one single interface
|
||||
@@ -172,61 +148,38 @@ class AudioSource {
|
||||
virtual I2S_datatype postProcessSample(I2S_datatype sample_in) {return(sample_in);} // default method can be overriden by instances (ADC) that need sample postprocessing
|
||||
|
||||
// Private constructor, to make sure it is not callable except from derived classes
|
||||
AudioSource(SRate_t sampleRate, int blockSize, float sampleScale, bool i2sMaster) :
|
||||
AudioSource(SRate_t sampleRate, int blockSize, float sampleScale) :
|
||||
_sampleRate(sampleRate),
|
||||
_blockSize(blockSize),
|
||||
_initialized(false),
|
||||
_i2sMaster(i2sMaster),
|
||||
_sampleScale(sampleScale)
|
||||
{};
|
||||
|
||||
SRate_t _sampleRate; // Microphone sampling rate
|
||||
int _blockSize; // I2S block size
|
||||
bool _initialized; // Gets set to true if initialization is successful
|
||||
bool _i2sMaster; // when false, ESP32 will be in I2S SLAVE mode (for devices that only operate in MASTER mode). Only works in newer IDF >= 4.4.x
|
||||
float _sampleScale; // pre-scaling factor for I2S samples
|
||||
I2S_datatype newSampleBuffer[I2S_SAMPLES_MAX+4] = { 0 }; // global buffer for i2s_read
|
||||
};
|
||||
|
||||
/* Basic I2S microphone source
|
||||
All functions are marked virtual, so derived classes can replace them
|
||||
WARNING: i2sMaster = false is experimental, and most likely will not work
|
||||
*/
|
||||
class I2SSource : public AudioSource {
|
||||
public:
|
||||
I2SSource(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
|
||||
AudioSource(sampleRate, blockSize, sampleScale, i2sMaster) {
|
||||
I2SSource(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) :
|
||||
AudioSource(sampleRate, blockSize, sampleScale) {
|
||||
_config = {
|
||||
.mode = i2sMaster ? i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX) : i2s_mode_t(I2S_MODE_SLAVE | I2S_MODE_RX),
|
||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
|
||||
.sample_rate = _sampleRate,
|
||||
.bits_per_sample = I2S_SAMPLE_RESOLUTION, // slave mode: may help to set this to 96000, as the other side (master) controls sample rates
|
||||
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
|
||||
.channel_format = I2S_MIC_CHANNEL,
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
|
||||
//.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
#ifdef WLEDMM_FASTPATH
|
||||
#ifdef WLED_ENABLE_HUB75MATRIX
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_IRAM|ESP_INTR_FLAG_LEVEL1, // HUB75 seems to get into trouble if we allocate a higher priority interrupt
|
||||
.dma_buf_count = 18, // 100ms buffer (128 * dma_buf_count / sampleRate)
|
||||
#else
|
||||
#if CONFIG_IDF_TARGET_ESP32 && !defined(BOARD_HAS_PSRAM) // still need to test on boards with PSRAM
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_IRAM|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3, // IRAM flag reduces missed samples
|
||||
#else
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3, // seems to reduce noise
|
||||
#endif
|
||||
.dma_buf_count = 24, // 140ms buffer (128 * dma_buf_count / sampleRate)
|
||||
#endif
|
||||
#else
|
||||
#ifdef WLED_ENABLE_HUB75MATRIX
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // HUB75 seems to get into trouble if we allocate a higher priority interrupt
|
||||
#else
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
|
||||
#endif
|
||||
.dma_buf_count = 8,
|
||||
#endif
|
||||
.dma_buf_len = _blockSize,
|
||||
.use_apll = 0,
|
||||
//.fixed_mclk = 0,
|
||||
.bits_per_chan = I2S_data_size,
|
||||
#else
|
||||
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
|
||||
@@ -243,7 +196,7 @@ class I2SSource : public AudioSource {
|
||||
if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) {
|
||||
if (!PinManager::allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) ||
|
||||
!PinManager::allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206
|
||||
ERRORSR_PRINTF("\nAR: Failed to allocate I2S pins: ws=%d, sd=%d\n", i2swsPin, i2ssdPin);
|
||||
DEBUGSR_PRINTF("\nAR: Failed to allocate I2S pins: ws=%d, sd=%d\n", i2swsPin, i2ssdPin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -251,7 +204,7 @@ class I2SSource : public AudioSource {
|
||||
// i2ssckPin needs special treatment, since it might be unused on PDM mics
|
||||
if (i2sckPin != I2S_PIN_NO_CHANGE) {
|
||||
if (!PinManager::allocatePin(i2sckPin, true, PinOwner::UM_Audioreactive)) {
|
||||
ERRORSR_PRINTF("\nAR: Failed to allocate I2S pins: sck=%d\n", i2sckPin);
|
||||
DEBUGSR_PRINTF("\nAR: Failed to allocate I2S pins: sck=%d\n", i2sckPin);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -273,17 +226,10 @@ class I2SSource : public AudioSource {
|
||||
_config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm if clock pin not provided. PDM is not supported on ESP32-S2. PDM RX not supported on ESP32-C3
|
||||
_config.channel_format =I2S_PDM_MIC_CHANNEL; // seems that PDM mono mode always uses left channel.
|
||||
_config.use_apll = true; // experimental - use aPLL clock source to improve sampling quality
|
||||
//_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; // not needed
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
if ((_i2sMaster == false) && (_config.mode & I2S_MODE_SLAVE)) { // I2S slave mode (experimental).
|
||||
// Seems we need to drive clocks in slave mode
|
||||
_config.use_apll = true;
|
||||
_config.fixed_mclk = 512 * int(_config.sample_rate);
|
||||
}
|
||||
|
||||
if (mclkPin != I2S_PIN_NO_CHANGE) {
|
||||
_config.use_apll = true; // experimental - use aPLL clock source to improve sampling quality, and to avoid glitches.
|
||||
// //_config.fixed_mclk = 512 * _sampleRate;
|
||||
@@ -298,28 +244,13 @@ class I2SSource : public AudioSource {
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
if (ESP.getChipRevision() == 0) _config.use_apll = false; // APLL is broken on ESP32 revision 0
|
||||
#endif
|
||||
#if defined(WLED_ENABLE_HUB75MATRIX)
|
||||
_config.use_apll = false; // APLL needed for HUB75 DMA driver ?
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (_i2sMaster == false) {
|
||||
DEBUG_PRINTLN(F("AR: Warning - i2S SLAVE mode is experimental!"));
|
||||
if (_config.mode & I2S_MODE_PDM) {
|
||||
// APLL does not work in DAC or PDM "Slave Mode": https://github.com/espressif/esp-idf/issues/1244, https://github.com/espressif/esp-idf/issues/2634
|
||||
_config.use_apll = false;
|
||||
_config.fixed_mclk = 0;
|
||||
}
|
||||
if ((_config.mode & I2S_MODE_MASTER) != 0) {
|
||||
DEBUG_PRINTLN("AR: (oops) I2S SLAVE mode requested but not configured!");
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve the master clock pin if provided
|
||||
_mclkPin = mclkPin;
|
||||
if (mclkPin != I2S_PIN_NO_CHANGE) {
|
||||
if(!PinManager::allocatePin(mclkPin, true, PinOwner::UM_Audioreactive)) {
|
||||
ERRORSR_PRINTF("\nAR: Failed to allocate I2S pin: MCLK=%d\n", mclkPin);
|
||||
DEBUGSR_PRINTF("\nAR: Failed to allocate I2S pin: MCLK=%d\n", mclkPin);
|
||||
return;
|
||||
} else
|
||||
_routeMclk(mclkPin);
|
||||
@@ -337,36 +268,32 @@ class I2SSource : public AudioSource {
|
||||
|
||||
//DEBUGSR_PRINTF("[AR] I2S: SD=%d, WS=%d, SCK=%d, MCLK=%d\n", i2ssdPin, i2swsPin, i2sckPin, mclkPin);
|
||||
|
||||
esp_err_t err = i2s_driver_install(AR_I2S_PORT, &_config, 0, nullptr);
|
||||
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
|
||||
if (err != ESP_OK) {
|
||||
ERRORSR_PRINTF("AR: Failed to install i2s driver: %d\n", err);
|
||||
DEBUGSR_PRINTF("AR: Failed to install i2s driver: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUGSR_PRINTF("AR: I2S#0 driver %s aPLL; fixed_mclk=%d.\n", _config.use_apll? "uses":"without", _config.fixed_mclk);
|
||||
DEBUGSR_PRINTF("AR: %d bits, Sample scaling factor = %6.4f\n", _config.bits_per_sample, _sampleScale);
|
||||
if(_config.mode & I2S_MODE_MASTER) {
|
||||
if (_config.mode & I2S_MODE_PDM) {
|
||||
if (_config.mode & I2S_MODE_PDM) {
|
||||
DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in PDM MASTER mode."));
|
||||
} else {
|
||||
} else {
|
||||
DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in MASTER mode."));
|
||||
}
|
||||
} else {
|
||||
DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in SLAVE mode."));
|
||||
}
|
||||
|
||||
err = i2s_set_pin(AR_I2S_PORT, &_pinConfig);
|
||||
err = i2s_set_pin(I2S_NUM_0, &_pinConfig);
|
||||
if (err != ESP_OK) {
|
||||
ERRORSR_PRINTF("AR: Failed to set i2s pin config: %d\n", err);
|
||||
i2s_driver_uninstall(AR_I2S_PORT); // uninstall already-installed driver
|
||||
DEBUGSR_PRINTF("AR: Failed to set i2s pin config: %d\n", err);
|
||||
i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver
|
||||
return;
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
err = i2s_set_clk(AR_I2S_PORT, _sampleRate, I2S_SAMPLE_RESOLUTION, I2S_CHANNEL_MONO); // set bit clocks. Also takes care of MCLK routing if needed.
|
||||
err = i2s_set_clk(I2S_NUM_0, _sampleRate, I2S_SAMPLE_RESOLUTION, I2S_CHANNEL_MONO); // set bit clocks. Also takes care of MCLK routing if needed.
|
||||
if (err != ESP_OK) {
|
||||
ERRORSR_PRINTF("AR: Failed to configure i2s clocks: %d\n", err);
|
||||
i2s_driver_uninstall(AR_I2S_PORT); // uninstall already-installed driver
|
||||
DEBUGSR_PRINTF("AR: Failed to configure i2s clocks: %d\n", err);
|
||||
i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -375,7 +302,7 @@ class I2SSource : public AudioSource {
|
||||
|
||||
virtual void deinitialize() {
|
||||
_initialized = false;
|
||||
esp_err_t err = i2s_driver_uninstall(AR_I2S_PORT);
|
||||
esp_err_t err = i2s_driver_uninstall(I2S_NUM_0);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
|
||||
return;
|
||||
@@ -391,20 +318,17 @@ class I2SSource : public AudioSource {
|
||||
if (_initialized) {
|
||||
esp_err_t err;
|
||||
size_t bytes_read = 0; /* Counter variable to check if we actually got enough data */
|
||||
I2S_datatype newSamples[num_samples]; /* Intermediary sample storage */
|
||||
|
||||
memset(buffer, 0, sizeof(float) * num_samples); // clear output buffer
|
||||
I2S_datatype *newSamples = newSampleBuffer; // use global input buffer
|
||||
if (num_samples > I2S_SAMPLES_MAX) num_samples = I2S_SAMPLES_MAX; // protect the buffer from overflow
|
||||
|
||||
err = i2s_read(AR_I2S_PORT, (void *)newSamples, num_samples * sizeof(I2S_datatype), &bytes_read, portMAX_DELAY);
|
||||
err = i2s_read(I2S_NUM_0, (void *)newSamples, sizeof(newSamples), &bytes_read, portMAX_DELAY);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to get samples: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// For correct operation, we need to read exactly sizeof(samples) bytes from i2s
|
||||
if (bytes_read != (num_samples * sizeof(I2S_datatype))) {
|
||||
DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", num_samples * sizeof(I2S_datatype), bytes_read);
|
||||
if (bytes_read != sizeof(newSamples)) {
|
||||
DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", sizeof(newSamples), bytes_read);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -459,10 +383,6 @@ class I2SSource : public AudioSource {
|
||||
*/
|
||||
class ES7243 : public I2SSource {
|
||||
private:
|
||||
// I2C initialization functions for ES7243
|
||||
void _es7243I2cBegin() {
|
||||
Wire.setClock(100000);
|
||||
}
|
||||
|
||||
void _es7243I2cWrite(uint8_t reg, uint8_t val) {
|
||||
#ifndef ES7243_ADDR
|
||||
@@ -478,7 +398,6 @@ class ES7243 : public I2SSource {
|
||||
}
|
||||
|
||||
void _es7243InitAdc() {
|
||||
_es7243I2cBegin();
|
||||
_es7243I2cWrite(0x00, 0x01);
|
||||
_es7243I2cWrite(0x06, 0x00);
|
||||
_es7243I2cWrite(0x05, 0x1B);
|
||||
@@ -488,26 +407,17 @@ class ES7243 : public I2SSource {
|
||||
}
|
||||
|
||||
public:
|
||||
ES7243(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
|
||||
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
|
||||
ES7243(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) :
|
||||
I2SSource(sampleRate, blockSize, sampleScale) {
|
||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
|
||||
};
|
||||
|
||||
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
|
||||
DEBUGSR_PRINTLN("ES7243:: initialize();");
|
||||
|
||||
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
|
||||
// ERRORSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
|
||||
// return;
|
||||
// }
|
||||
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
|
||||
ERRORSR_PRINTF("\nAR: invalid ES7243 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
DEBUGSR_PRINTLN(F("ES7243:: initialize();"));
|
||||
if ((i2sckPin < 0) || (mclkPin < 0)) {
|
||||
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
|
||||
return;
|
||||
}
|
||||
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
|
||||
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// First route mclk, then configure ADC over I2C, then configure I2S
|
||||
_es7243InitAdc();
|
||||
@@ -525,16 +435,14 @@ public:
|
||||
*/
|
||||
class ES8388Source : public I2SSource {
|
||||
private:
|
||||
// I2C initialization functions for ES8388
|
||||
void _es8388I2cBegin() {
|
||||
Wire.setClock(100000);
|
||||
}
|
||||
|
||||
void _es8388I2cWrite(uint8_t reg, uint8_t val) {
|
||||
#ifndef ES8388_ADDR
|
||||
#define ES8388_ADDR 0x10 // default address
|
||||
#endif
|
||||
#ifndef ES8388_ADDR
|
||||
Wire.beginTransmission(0x10);
|
||||
#define ES8388_ADDR 0x10 // default address
|
||||
#else
|
||||
Wire.beginTransmission(ES8388_ADDR);
|
||||
#endif
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.write((uint8_t)val);
|
||||
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
|
||||
@@ -551,7 +459,6 @@ class ES8388Source : public I2SSource {
|
||||
// Registries are decimal, settings are binary as that's how everything is listed in the docs
|
||||
// ...which makes it easier to reference the docs.
|
||||
//
|
||||
_es8388I2cBegin();
|
||||
_es8388I2cWrite( 8,0b00000000); // I2S to slave
|
||||
_es8388I2cWrite( 2,0b11110011); // Power down DEM and STM
|
||||
_es8388I2cWrite(43,0b10000000); // Set same LRCK
|
||||
@@ -617,28 +524,16 @@ class ES8388Source : public I2SSource {
|
||||
|
||||
public:
|
||||
ES8388Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
|
||||
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
|
||||
I2SSource(sampleRate, blockSize, sampleScale) {
|
||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
|
||||
};
|
||||
|
||||
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
|
||||
DEBUGSR_PRINTLN("ES8388Source:: initialize();");
|
||||
|
||||
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
|
||||
// ERRORSR_PRINTF("\nAR: invalid I2S ES8388 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
|
||||
// return;
|
||||
// }
|
||||
// BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
|
||||
// Workaround: Set I2C pins here, which will also set them globally.
|
||||
// Bug also exists in ES7243.
|
||||
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
|
||||
ERRORSR_PRINTF("\nAR: invalid ES8388 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
DEBUGSR_PRINTLN(F("ES8388Source:: initialize();"));
|
||||
if ((i2sckPin < 0) || (mclkPin < 0)) {
|
||||
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
|
||||
return;
|
||||
}
|
||||
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
|
||||
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// First route mclk, then configure ADC over I2C, then configure I2S
|
||||
_es8388InitAdc();
|
||||
@@ -651,302 +546,6 @@ class ES8388Source : public I2SSource {
|
||||
|
||||
};
|
||||
|
||||
/* ES8311 Sound Module
|
||||
This is an I2S sound processing unit that requires initialization over
|
||||
I2C before I2S data can be received.
|
||||
*/
|
||||
class ES8311Source : public I2SSource {
|
||||
private:
|
||||
// I2C initialization functions for es8311
|
||||
void _es8311I2cBegin() {
|
||||
Wire.setClock(100000);
|
||||
}
|
||||
|
||||
void _es8311I2cWrite(uint8_t reg, uint8_t val) {
|
||||
#ifndef ES8311_ADDR
|
||||
#define ES8311_ADDR 0x18 // default address is... foggy
|
||||
#endif
|
||||
Wire.beginTransmission(ES8311_ADDR);
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.write((uint8_t)val);
|
||||
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
|
||||
if (i2cErr != 0) {
|
||||
DEBUGSR_PRINTF("AR: ES8311 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, ES8311_ADDR, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
void _es8311InitAdc() {
|
||||
//
|
||||
// Currently only tested with the ESP32-P4 boards with the onboard mic.
|
||||
// Datasheet with I2C commands: https://dl.xkwy2018.com/downloads/RK3588/01_Official%20Release/04_Product%20Line%20Branch_NVR/02_Key%20Device%20Specifications/ES8311%20DS.pdf
|
||||
//
|
||||
_es8311I2cBegin();
|
||||
_es8311I2cWrite(0x00, 0b00011111); // RESET, default value
|
||||
_es8311I2cWrite(0x45, 0b00000000); // GP, default value
|
||||
_es8311I2cWrite(0x01, 0b00111010); // CLOCK MANAGER was 0b00110000 trying 0b00111010 (MCLK enable?)
|
||||
|
||||
_es8311I2cWrite(0x02, 0b00000000); // 22050hz calculated
|
||||
_es8311I2cWrite(0x05, 0b00000000); // 22050hz calculated
|
||||
_es8311I2cWrite(0x03, 0b00010000); // 22050hz calculated
|
||||
_es8311I2cWrite(0x04, 0b00010000); // 22050hz calculated
|
||||
_es8311I2cWrite(0x07, 0b00000000); // 22050hz calculated
|
||||
_es8311I2cWrite(0x08, 0b11111111); // 22050hz calculated
|
||||
_es8311I2cWrite(0x06, 0b11100011); // 22050hz calculated
|
||||
|
||||
_es8311I2cWrite(0x16, 0b00100100); // ADC was 0b00000011 trying 0b00100100 was good
|
||||
_es8311I2cWrite(0x0B, 0b00000000); // SYSTEM at default
|
||||
_es8311I2cWrite(0x0C, 0b00100000); // SYSTEM was 0b00001111 trying 0b00100000
|
||||
_es8311I2cWrite(0x10, 0b00010011); // SYSTEM was 0b00011111 trying 0b00010011
|
||||
_es8311I2cWrite(0x11, 0b01111100); // SYSTEM was 0b01111111 trying 0b01111100
|
||||
_es8311I2cWrite(0x00, 0b11000000); // *** RESET (again - seems important?)
|
||||
_es8311I2cWrite(0x01, 0b00111010); // *** CLOCK MANAGER was 0b00111111 trying 0b00111010 (again?? seems important)
|
||||
_es8311I2cWrite(0x14, 0b00010000); // *** SYSTEM was 0b00011010 trying 0b00010000 (PGA gain)
|
||||
_es8311I2cWrite(0x0A, 0b00001000); // *** SDP OUT, was 0b00001100 trying 0b00001000 (I2S 32-bit)
|
||||
_es8311I2cWrite(0x0E, 0b00000010); // *** SYSTEM was 0b00000010 trying 0b00000010
|
||||
_es8311I2cWrite(0x0F, 0b01000100); // SYSTEM was 0b01000100
|
||||
_es8311I2cWrite(0x15, 0b00010000); // ADC soft ramp (disabled 0000xxxx)
|
||||
_es8311I2cWrite(0x1B, 0b00000101); // ADC soft-mute was 0b00000101
|
||||
_es8311I2cWrite(0x1C, 0b01100101); // ADC EQ and offset freeze at 0b01100101 (bad at 0b00101100)
|
||||
_es8311I2cWrite(0x17, 0b10111111); // ADC volume was 0b11111111 trying ADC volume 0b10111111 = 0db (maxgain)
|
||||
_es8311I2cWrite(0x18, 0b10000001); // ADC ALC enabled and AutoMute disabled.
|
||||
// _es8311I2cWrite(0x19, 0b11110100); // ADC ALC max and min - not sure how best to use this, default seems fine
|
||||
}
|
||||
|
||||
public:
|
||||
ES8311Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
|
||||
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
|
||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
|
||||
};
|
||||
|
||||
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
|
||||
DEBUGSR_PRINTLN("es8311Source:: initialize();");
|
||||
|
||||
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
|
||||
// ERRORSR_PRINTF("\nAR: invalid I2S es8311 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
|
||||
// return;
|
||||
// }
|
||||
// BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
|
||||
// Workaround: Set I2C pins here, which will also set them globally.
|
||||
// Bug also exists in ES7243.
|
||||
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
|
||||
ERRORSR_PRINTF("\nAR: invalid es8311 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
return;
|
||||
}
|
||||
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
|
||||
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// First route mclk, then configure ADC over I2C, then configure I2S
|
||||
_es8311InitAdc();
|
||||
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||
}
|
||||
|
||||
void deinitialize() {
|
||||
I2SSource::deinitialize();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class WM8978Source : public I2SSource {
|
||||
private:
|
||||
// I2C initialization functions for WM8978
|
||||
void _wm8978I2cBegin() {
|
||||
Wire.setClock(400000);
|
||||
}
|
||||
|
||||
void _wm8978I2cWrite(uint8_t reg, uint16_t val) {
|
||||
#ifndef WM8978_ADDR
|
||||
#define WM8978_ADDR 0x1A
|
||||
#endif
|
||||
char buf[2];
|
||||
buf[0] = (reg << 1) | ((val >> 8) & 0X01);
|
||||
buf[1] = val & 0XFF;
|
||||
Wire.beginTransmission(WM8978_ADDR);
|
||||
Wire.write((const uint8_t*)buf, 2);
|
||||
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
|
||||
if (i2cErr != 0) {
|
||||
DEBUGSR_PRINTF("AR: WM8978 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, WM8978_ADDR, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
void _wm8978InitAdc() {
|
||||
// https://www.mouser.com/datasheet/2/76/WM8978_v4.5-1141768.pdf
|
||||
// Sets ADC to around what AudioReactive expects, and loops line-in to line-out/headphone for monitoring.
|
||||
// Registries are decimal, settings are 9-bit binary as that's how everything is listed in the docs
|
||||
// ...which makes it easier to reference the docs.
|
||||
//
|
||||
_wm8978I2cBegin();
|
||||
|
||||
_wm8978I2cWrite( 0,0b000000000); // Reset all settings
|
||||
_wm8978I2cWrite( 1,0b000111110); // Power Management 1 - power off most things, but enable mic bias and I/O tie-off to help mitigate mic leakage.
|
||||
_wm8978I2cWrite( 2,0b110111111); // Power Management 2 - enable output and amp stages (amps may lift signal but it works better on the ADCs)
|
||||
_wm8978I2cWrite( 3,0b000001100); // Power Management 3 - enable L&R output mixers
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
_wm8978I2cWrite( 4,0b001010000); // Audio Interface - standard I2S, 24-bit
|
||||
#else
|
||||
_wm8978I2cWrite( 4,0b001001000); // Audio Interface - left-justified I2S, 24-bit
|
||||
#endif
|
||||
|
||||
_wm8978I2cWrite( 6,0b000000000); // Clock generation control - use external mclk
|
||||
_wm8978I2cWrite( 7,0b000000100); // Sets sample rate to ~24kHz (only used for internal calculations, not I2S)
|
||||
_wm8978I2cWrite(14,0b010001000); // 128x ADC oversampling - high pass filter disabled as it kills the bass response
|
||||
_wm8978I2cWrite(43,0b000110000); // Mute signal paths we don't use
|
||||
_wm8978I2cWrite(44,0b100000000); // Disconnect microphones
|
||||
_wm8978I2cWrite(45,0b111000000); // Mute signal paths we don't use
|
||||
_wm8978I2cWrite(46,0b111000000); // Mute signal paths we don't use
|
||||
_wm8978I2cWrite(47,0b001000000); // 0dB gain on left line-in
|
||||
_wm8978I2cWrite(48,0b001000000); // 0dB gain on right line-in
|
||||
_wm8978I2cWrite(49,0b000000011); // Mixer thermal shutdown enable and unused IOs to 30kΩ
|
||||
_wm8978I2cWrite(50,0b000010110); // Output mixer enable only left bypass at 0dB gain
|
||||
_wm8978I2cWrite(51,0b000010110); // Output mixer enable only right bypass at 0dB gain
|
||||
_wm8978I2cWrite(52,0b110111001); // Left line-out enabled at 0dB gain
|
||||
_wm8978I2cWrite(53,0b110111001); // Right line-out enabled at 0db gain
|
||||
_wm8978I2cWrite(54,0b111000000); // Mute left speaker output
|
||||
_wm8978I2cWrite(55,0b111000000); // Mute right speaker output
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
WM8978Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
|
||||
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
|
||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
|
||||
};
|
||||
|
||||
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
|
||||
DEBUGSR_PRINTLN("WM8978Source:: initialize();");
|
||||
|
||||
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
|
||||
// ERRORSR_PRINTF("\nAR: invalid I2S WM8978 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
|
||||
// return;
|
||||
// }
|
||||
// BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
|
||||
// Workaround: Set I2C pins here, which will also set them globally.
|
||||
// Bug also exists in ES7243.
|
||||
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
|
||||
ERRORSR_PRINTF("\nAR: invalid WM8978 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
return;
|
||||
}
|
||||
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
|
||||
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// First route mclk, then configure ADC over I2C, then configure I2S
|
||||
_wm8978InitAdc();
|
||||
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||
}
|
||||
|
||||
void deinitialize() {
|
||||
I2SSource::deinitialize();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class AC101Source : public I2SSource {
|
||||
private:
|
||||
// I2C initialization functions for WM8978
|
||||
void _ac101I2cBegin() {
|
||||
Wire.setClock(400000);
|
||||
}
|
||||
|
||||
void _ac101I2cWrite(uint8_t reg_addr, uint16_t val) {
|
||||
#ifndef AC101_ADDR
|
||||
#define AC101_ADDR 0x1A
|
||||
#endif
|
||||
char send_buff[3];
|
||||
send_buff[0] = reg_addr;
|
||||
send_buff[1] = uint8_t((val >> 8) & 0xff);
|
||||
send_buff[2] = uint8_t(val & 0xff);
|
||||
Wire.beginTransmission(AC101_ADDR);
|
||||
Wire.write((const uint8_t*)send_buff, 3);
|
||||
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
|
||||
if (i2cErr != 0) {
|
||||
DEBUGSR_PRINTF("AR: AC101 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, AC101_ADDR, reg_addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
void _ac101InitAdc() {
|
||||
// https://files.seeedstudio.com/wiki/ReSpeaker_6-Mics_Circular_Array_kit_for_Raspberry_Pi/reg/AC101_User_Manual_v1.1.pdf
|
||||
// This supports mostly the older AI Thinkier AudioKit A1S that has an AC101 chip
|
||||
// Newer versions use the ES3833 chip - which we also support.
|
||||
|
||||
_ac101I2cBegin();
|
||||
|
||||
#define CHIP_AUDIO_RS 0x00
|
||||
#define SYSCLK_CTRL 0x03
|
||||
#define MOD_CLK_ENA 0x04
|
||||
#define MOD_RST_CTRL 0x05
|
||||
#define I2S_SR_CTRL 0x06
|
||||
#define I2S1LCK_CTRL 0x10
|
||||
#define I2S1_SDOUT_CTRL 0x11
|
||||
#define I2S1_MXR_SRC 0x13
|
||||
#define ADC_DIG_CTRL 0x40
|
||||
#define ADC_APC_CTRL 0x50
|
||||
#define ADC_SRC 0x51
|
||||
#define ADC_SRCBST_CTRL 0x52
|
||||
#define OMIXER_DACA_CTRL 0x53
|
||||
#define OMIXER_SR 0x54
|
||||
#define HPOUT_CTRL 0x56
|
||||
|
||||
_ac101I2cWrite(CHIP_AUDIO_RS, 0x123); // I think anything written here is a reset as 0x123 is kinda suss.
|
||||
|
||||
delay(100);
|
||||
|
||||
_ac101I2cWrite(SYSCLK_CTRL, 0b0000100000001000); // System Clock is I2S MCLK
|
||||
_ac101I2cWrite(MOD_CLK_ENA, 0b1000000000001000); // I2S and ADC Clock Enable
|
||||
_ac101I2cWrite(MOD_RST_CTRL, 0b1000000000001000); // I2S and ADC Clock Enable
|
||||
_ac101I2cWrite(I2S_SR_CTRL, 0b0100000000000000); // set to 22050hz just in case
|
||||
_ac101I2cWrite(I2S1LCK_CTRL, 0b1000000000110000); // set I2S slave mode, 24-bit word size
|
||||
_ac101I2cWrite(I2S1_SDOUT_CTRL, 0b1100000000000000); // I2S enable Left/Right channels
|
||||
_ac101I2cWrite(I2S1_MXR_SRC, 0b0010001000000000); // I2S digital Mixer, ADC L/R data
|
||||
_ac101I2cWrite(ADC_SRCBST_CTRL, 0b0000000000000100); // mute all boosts. last 3 bits are reserved/default
|
||||
_ac101I2cWrite(OMIXER_SR, 0b0000010000001000); // Line L/R to output mixer
|
||||
_ac101I2cWrite(ADC_SRC, 0b0000010000001000); // Line L/R to ADC
|
||||
_ac101I2cWrite(ADC_DIG_CTRL, 0b1000000000000000); // Enable ADC
|
||||
_ac101I2cWrite(ADC_APC_CTRL, 0b1011100100000000); // ADC L/R enabled, 0dB gain
|
||||
_ac101I2cWrite(OMIXER_DACA_CTRL, 0b0011111110000000); // L/R Analog Output Mixer enabled, headphone DC offset default
|
||||
_ac101I2cWrite(HPOUT_CTRL, 0b1111101111110001); // Headphone out from Analog Mixer stage, no reduction in volume
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
AC101Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
|
||||
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
|
||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
|
||||
};
|
||||
|
||||
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
|
||||
DEBUGSR_PRINTLN("AC101Source:: initialize();");
|
||||
|
||||
// if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
|
||||
// ERRORSR_PRINTF("\nAR: invalid I2S WM8978 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
|
||||
// return;
|
||||
// }
|
||||
// BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
|
||||
// Workaround: Set I2C pins here, which will also set them globally.
|
||||
// Bug also exists in ES7243.
|
||||
if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
|
||||
ERRORSR_PRINTF("\nAR: invalid AC101 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
return;
|
||||
}
|
||||
// if (!PinManager::joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
|
||||
// ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// First route mclk, then configure ADC over I2C, then configure I2S
|
||||
_ac101InitAdc();
|
||||
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||
}
|
||||
|
||||
void deinitialize() {
|
||||
I2SSource::deinitialize();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC)
|
||||
#warning this MCU does not support analog sound input
|
||||
@@ -954,7 +553,7 @@ class AC101Source : public I2SSource {
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// ADC over I2S is only available in "classic" ESP32
|
||||
// ADC over I2S is only availeable in "classic" ESP32
|
||||
|
||||
/* ADC over I2S Microphone
|
||||
This microphone is an ADC pin sampled via the I2S interval
|
||||
@@ -964,7 +563,7 @@ class AC101Source : public I2SSource {
|
||||
class I2SAdcSource : public I2SSource {
|
||||
public:
|
||||
I2SAdcSource(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) :
|
||||
I2SSource(sampleRate, blockSize, sampleScale, true) {
|
||||
I2SSource(sampleRate, blockSize, sampleScale) {
|
||||
_config = {
|
||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
||||
.sample_rate = _sampleRate,
|
||||
@@ -988,18 +587,18 @@ class I2SAdcSource : public I2SSource {
|
||||
AudioSourceType getType(void) {return(Type_I2SAdc);}
|
||||
|
||||
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
DEBUGSR_PRINTLN("I2SAdcSource:: initialize().");
|
||||
DEBUGSR_PRINTLN(F("I2SAdcSource:: initialize()."));
|
||||
_myADCchannel = 0x0F;
|
||||
if(!PinManager::allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
|
||||
ERRORSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
|
||||
DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
|
||||
return;
|
||||
}
|
||||
_audioPin = audioPin;
|
||||
|
||||
// Determine Analog channel. Only Channels on ADC1 are supported
|
||||
int8_t channel = digitalPinToAnalogChannel(_audioPin);
|
||||
if ((channel < 0) || (channel > 9)) { // channel == -1 means "not an ADC pin"
|
||||
DEBUGSR_PRINTF("AR: Incompatible GPIO used for analog audio input: %d\n", _audioPin);
|
||||
if (channel > 9) {
|
||||
DEBUGSR_PRINTF("Incompatible GPIO used for analog audio input: %d\n", _audioPin);
|
||||
return;
|
||||
} else {
|
||||
adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel));
|
||||
@@ -1009,16 +608,16 @@ class I2SAdcSource : public I2SSource {
|
||||
// Install Driver
|
||||
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
|
||||
if (err != ESP_OK) {
|
||||
ERRORSR_PRINTF("Failed to install i2s driver: %d\n", err);
|
||||
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// adc1_config_width(ADC_WIDTH_BIT_12); // ensure that ADC runs with 12bit resolution - should not be needed, because i2s_set_adc_mode does that any way
|
||||
adc1_config_width(ADC_WIDTH_BIT_12); // ensure that ADC runs with 12bit resolution
|
||||
|
||||
// Enable I2S mode of ADC
|
||||
err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel));
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("AR: Failed to set i2s adc mode: %d\n", err);
|
||||
DEBUGSR_PRINTF("Failed to set i2s adc mode: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1155,17 +754,17 @@ class I2SAdcSource : public I2SSource {
|
||||
// a user recommended this: Try to set .communication_format to I2S_COMM_FORMAT_STAND_I2S and call i2s_set_clk() after i2s_set_pin().
|
||||
class SPH0654 : public I2SSource {
|
||||
public:
|
||||
SPH0654(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
|
||||
I2SSource(sampleRate, blockSize, sampleScale, i2sMaster)
|
||||
SPH0654(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) :
|
||||
I2SSource(sampleRate, blockSize, sampleScale)
|
||||
{}
|
||||
|
||||
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
DEBUGSR_PRINTLN("SPH0654:: initialize();");
|
||||
DEBUGSR_PRINTLN(F("SPH0654:: initialize();"));
|
||||
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// these registers are only existing in "classic" ESP32
|
||||
REG_SET_BIT(I2S_TIMING_REG(AR_I2S_PORT), BIT(9));
|
||||
REG_SET_BIT(I2S_CONF_REG(AR_I2S_PORT), I2S_RX_MSB_SHIFT);
|
||||
REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0), BIT(9));
|
||||
REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT);
|
||||
#else
|
||||
#warning FIX ME! Please.
|
||||
#endif
|
||||
|
||||
@@ -2032,7 +2032,7 @@ uint16_t mode_palette() {
|
||||
const mathType sourceX = xtSinTheta + ytCosTheta + centerX;
|
||||
// The computation was scaled just right so that the result should always be in range [0, maxXOut], but enforce this anyway
|
||||
// to account for imprecision. Then scale it so that the range is [0, 255], which we can use with the palette.
|
||||
int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * 255) / (sInt16Scale * maxXOut);
|
||||
int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * wideMathType(255)) / (sInt16Scale * maxXOut);
|
||||
// inputSize determines by how much we want to scale the palette:
|
||||
// values < 128 display a fraction of a palette,
|
||||
// values > 128 display multiple palettes.
|
||||
|
||||
@@ -1388,7 +1388,7 @@ void WS2812FX::service() {
|
||||
}
|
||||
#endif
|
||||
seg.call++;
|
||||
if (seg.isInTransition() && frameDelay > FRAMETIME) frameDelay = FRAMETIME; // force faster updates during transition
|
||||
if (seg.isInTransition() && frameDelay > FRAMETIME_FIXED) frameDelay = FRAMETIME_FIXED; // force faster updates during transition, if requested time is below 40 fps
|
||||
BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ void shortPressAction(uint8_t b)
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
// publish MQTT message
|
||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
char subuf[MQTT_MAX_TOPIC_LEN + 32];
|
||||
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||
mqtt->publish(subuf, 0, false, "short");
|
||||
}
|
||||
@@ -62,7 +62,7 @@ void longPressAction(uint8_t b)
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
// publish MQTT message
|
||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
char subuf[MQTT_MAX_TOPIC_LEN + 32];
|
||||
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||
mqtt->publish(subuf, 0, false, "long");
|
||||
}
|
||||
@@ -83,7 +83,7 @@ void doublePressAction(uint8_t b)
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
// publish MQTT message
|
||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
char subuf[MQTT_MAX_TOPIC_LEN + 32];
|
||||
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||
mqtt->publish(subuf, 0, false, "double");
|
||||
}
|
||||
@@ -151,7 +151,7 @@ void handleSwitch(uint8_t b)
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
// publish MQTT message
|
||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
char subuf[MQTT_MAX_TOPIC_LEN + 32];
|
||||
if (buttonType[b] == BTN_TYPE_PIR_SENSOR) sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)b);
|
||||
else sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||
mqtt->publish(subuf, 0, false, !buttonPressedBefore[b] ? "off" : "on");
|
||||
@@ -375,6 +375,7 @@ void handleIO()
|
||||
if (rlyPin>=0) {
|
||||
pinMode(rlyPin, rlyOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT);
|
||||
digitalWrite(rlyPin, rlyMde);
|
||||
delay(50); // wait for relay to switch and power to stabilize
|
||||
}
|
||||
offMode = false;
|
||||
}
|
||||
|
||||
@@ -417,36 +417,33 @@ class Usermod {
|
||||
#endif
|
||||
};
|
||||
|
||||
class UsermodManager {
|
||||
private:
|
||||
static Usermod* ums[WLED_MAX_USERMODS];
|
||||
static byte numMods;
|
||||
namespace UsermodManager {
|
||||
extern byte numMods;
|
||||
|
||||
public:
|
||||
static void loop();
|
||||
static void handleOverlayDraw();
|
||||
static bool handleButton(uint8_t b);
|
||||
static bool getUMData(um_data_t **um_data, uint8_t mod_id = USERMOD_ID_RESERVED); // USERMOD_ID_RESERVED will poll all usermods
|
||||
static void setup();
|
||||
static void connected();
|
||||
static void appendConfigData(Print&);
|
||||
static void addToJsonState(JsonObject& obj);
|
||||
static void addToJsonInfo(JsonObject& obj);
|
||||
static void readFromJsonState(JsonObject& obj);
|
||||
static void addToConfig(JsonObject& obj);
|
||||
static bool readFromConfig(JsonObject& obj);
|
||||
void loop();
|
||||
void handleOverlayDraw();
|
||||
bool handleButton(uint8_t b);
|
||||
bool getUMData(um_data_t **um_data, uint8_t mod_id = USERMOD_ID_RESERVED); // USERMOD_ID_RESERVED will poll all usermods
|
||||
void setup();
|
||||
void connected();
|
||||
void appendConfigData(Print&);
|
||||
void addToJsonState(JsonObject& obj);
|
||||
void addToJsonInfo(JsonObject& obj);
|
||||
void readFromJsonState(JsonObject& obj);
|
||||
void addToConfig(JsonObject& obj);
|
||||
bool readFromConfig(JsonObject& obj);
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
static void onMqttConnect(bool sessionPresent);
|
||||
static bool onMqttMessage(char* topic, char* payload);
|
||||
void onMqttConnect(bool sessionPresent);
|
||||
bool onMqttMessage(char* topic, char* payload);
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
static bool onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len);
|
||||
bool onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len);
|
||||
#endif
|
||||
static void onUpdateBegin(bool);
|
||||
static void onStateChange(uint8_t);
|
||||
static bool add(Usermod* um);
|
||||
static Usermod* lookup(uint16_t mod_id);
|
||||
static inline byte getModCount() {return numMods;};
|
||||
void onUpdateBegin(bool);
|
||||
void onStateChange(uint8_t);
|
||||
bool add(Usermod* um);
|
||||
Usermod* lookup(uint16_t mod_id);
|
||||
inline byte getModCount() {return numMods;};
|
||||
};
|
||||
|
||||
//usermods_list.cpp
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
#define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds
|
||||
|
||||
#if MQTT_MAX_TOPIC_LEN > 32
|
||||
#warning "MQTT topics length > 32 is not recommended for compatibility with usermods!"
|
||||
#endif
|
||||
|
||||
static void parseMQTTBriPayload(char* payload)
|
||||
{
|
||||
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; stateUpdated(CALL_MODE_DIRECT_CHANGE);}
|
||||
@@ -23,24 +27,24 @@ static void parseMQTTBriPayload(char* payload)
|
||||
static void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
//(re)subscribe to required topics
|
||||
char subuf[38];
|
||||
char subuf[MQTT_MAX_TOPIC_LEN + 6];
|
||||
|
||||
if (mqttDeviceTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strlcpy(subuf, mqttDeviceTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strlcpy(subuf, mqttDeviceTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
|
||||
if (mqttGroupTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
strlcpy(subuf, mqttGroupTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
strlcpy(subuf, mqttGroupTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
@@ -158,19 +162,19 @@ void publishMqtt()
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
char s[10];
|
||||
char subuf[48];
|
||||
char subuf[MQTT_MAX_TOPIC_LEN + 16];
|
||||
|
||||
sprintf_P(s, PSTR("%u"), bri);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strlcpy(subuf, mqttDeviceTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
strcat_P(subuf, PSTR("/g"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strlcpy(subuf, mqttDeviceTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
strcat_P(subuf, PSTR("/c"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strlcpy(subuf, mqttDeviceTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
strcat_P(subuf, PSTR("/status"));
|
||||
mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT
|
||||
|
||||
@@ -178,7 +182,7 @@ void publishMqtt()
|
||||
DynamicBuffer buf(1024);
|
||||
bufferPrint pbuf(buf.data(), buf.size());
|
||||
XML_response(pbuf);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strlcpy(subuf, mqttDeviceTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
strcat_P(subuf, PSTR("/v"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, buf.data(), pbuf.size()); // optionally retain message (#2263)
|
||||
#endif
|
||||
@@ -211,7 +215,7 @@ bool initMqtt()
|
||||
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
strlcpy(mqttStatusTopic, mqttDeviceTopic, 33);
|
||||
strlcpy(mqttStatusTopic, mqttDeviceTopic, MQTT_MAX_TOPIC_LEN + 1);
|
||||
strcat_P(mqttStatusTopic, PSTR("/status"));
|
||||
mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,16 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Pin management state variables
|
||||
#ifdef ESP8266
|
||||
static uint32_t pinAlloc = 0UL; // 1 bit per pin, we use first 17bits
|
||||
#else
|
||||
static uint64_t pinAlloc = 0ULL; // 1 bit per pin, we use 50 bits on ESP32-S3
|
||||
static uint16_t ledcAlloc = 0; // up to 16 LEDC channels (WLED_MAX_ANALOG_CHANNELS)
|
||||
#endif
|
||||
static uint8_t i2cAllocCount = 0; // allow multiple allocation of I2C bus pins but keep track of allocations
|
||||
static uint8_t spiAllocCount = 0; // allow multiple allocation of SPI bus pins but keep track of allocations
|
||||
static PinOwner ownerTag[WLED_NUM_PINS] = { PinOwner::None };
|
||||
|
||||
/// Actual allocation/deallocation routines
|
||||
bool PinManager::deallocatePin(byte gpio, PinOwner tag)
|
||||
@@ -214,8 +224,20 @@ bool PinManager::isPinOk(byte gpio, bool output)
|
||||
// JTAG: GPIO39-42 are usually used for inline debugging
|
||||
// GPIO46 is input only and pulled down
|
||||
#else
|
||||
if (gpio > 5 && gpio < 12) return false; //SPI flash pins
|
||||
if (strncmp_P(PSTR("ESP32-PICO"), ESP.getChipModel(), 10) == 0 && (gpio == 16 || gpio == 17)) return false; // PICO-D4: gpio16+17 are in use for onboard SPI FLASH
|
||||
|
||||
if ((strncmp_P(PSTR("ESP32-U4WDH"), ESP.getChipModel(), 11) == 0) || // this is the correct identifier, but....
|
||||
(strncmp_P(PSTR("ESP32-PICO-D2"), ESP.getChipModel(), 13) == 0)) { // https://github.com/espressif/arduino-esp32/issues/10683
|
||||
// this chip has 4 MB of internal Flash and different packaging, so available pins are different!
|
||||
if (((gpio > 5) && (gpio < 9)) || (gpio == 11))
|
||||
return false;
|
||||
} else {
|
||||
// for classic ESP32 (non-mini) modules, these are the SPI flash pins
|
||||
if (gpio > 5 && gpio < 12) return false; //SPI flash pins
|
||||
}
|
||||
|
||||
if (((strncmp_P(PSTR("ESP32-PICO"), ESP.getChipModel(), 10) == 0) ||
|
||||
(strncmp_P(PSTR("ESP32-U4WDH"), ESP.getChipModel(), 11) == 0))
|
||||
&& (gpio == 16 || gpio == 17)) return false; // PICO-D4/U4WDH: gpio16+17 are in use for onboard SPI FLASH
|
||||
if (gpio == 16 || gpio == 17) return !psramFound(); //PSRAM pins on ESP32 (these are IO)
|
||||
#endif
|
||||
if (output) return digitalPinCanOutput(gpio);
|
||||
@@ -278,13 +300,3 @@ void PinManager::deallocateLedc(byte pos, byte channels)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
uint32_t PinManager::pinAlloc = 0UL;
|
||||
#else
|
||||
uint64_t PinManager::pinAlloc = 0ULL;
|
||||
uint16_t PinManager::ledcAlloc = 0;
|
||||
#endif
|
||||
uint8_t PinManager::i2cAllocCount = 0;
|
||||
uint8_t PinManager::spiAllocCount = 0;
|
||||
PinOwner PinManager::ownerTag[WLED_NUM_PINS] = { PinOwner::None };
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#endif
|
||||
#include "const.h" // for USERMOD_* values
|
||||
|
||||
#ifdef ESP8266
|
||||
#define WLED_NUM_PINS (GPIO_PIN_COUNT+1) // somehow they forgot GPIO 16 (0-16==17)
|
||||
#else
|
||||
#define WLED_NUM_PINS (GPIO_PIN_COUNT)
|
||||
#endif
|
||||
|
||||
typedef struct PinManagerPinType {
|
||||
int8_t pin;
|
||||
bool isOutput;
|
||||
@@ -70,53 +76,39 @@ enum struct PinOwner : uint8_t {
|
||||
};
|
||||
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
|
||||
|
||||
class PinManager {
|
||||
private:
|
||||
#ifdef ESP8266
|
||||
#define WLED_NUM_PINS (GPIO_PIN_COUNT+1) // somehow they forgot GPIO 16 (0-16==17)
|
||||
static uint32_t pinAlloc; // 1 bit per pin, we use first 17bits
|
||||
#else
|
||||
#define WLED_NUM_PINS (GPIO_PIN_COUNT)
|
||||
static uint64_t pinAlloc; // 1 bit per pin, we use 50 bits on ESP32-S3
|
||||
static uint16_t ledcAlloc; // up to 16 LEDC channels (WLED_MAX_ANALOG_CHANNELS)
|
||||
#endif
|
||||
static uint8_t i2cAllocCount; // allow multiple allocation of I2C bus pins but keep track of allocations
|
||||
static uint8_t spiAllocCount; // allow multiple allocation of SPI bus pins but keep track of allocations
|
||||
static PinOwner ownerTag[WLED_NUM_PINS];
|
||||
namespace PinManager {
|
||||
// De-allocates a single pin
|
||||
bool deallocatePin(byte gpio, PinOwner tag);
|
||||
// De-allocates multiple pins but only if all can be deallocated (PinOwner has to be specified)
|
||||
bool deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag);
|
||||
bool deallocateMultiplePins(const managed_pin_type *pinArray, byte arrayElementCount, PinOwner tag);
|
||||
// Allocates a single pin, with an owner tag.
|
||||
// De-allocation requires the same owner tag (or override)
|
||||
bool allocatePin(byte gpio, bool output, PinOwner tag);
|
||||
// Allocates all the pins, or allocates none of the pins, with owner tag.
|
||||
// Provided to simplify error condition handling in clients
|
||||
// using more than one pin, such as I2C, SPI, rotary encoders,
|
||||
// ethernet, etc..
|
||||
bool allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag );
|
||||
|
||||
public:
|
||||
// De-allocates a single pin
|
||||
static bool deallocatePin(byte gpio, PinOwner tag);
|
||||
// De-allocates multiple pins but only if all can be deallocated (PinOwner has to be specified)
|
||||
static bool deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag);
|
||||
static bool deallocateMultiplePins(const managed_pin_type *pinArray, byte arrayElementCount, PinOwner tag);
|
||||
// Allocates a single pin, with an owner tag.
|
||||
// De-allocation requires the same owner tag (or override)
|
||||
static bool allocatePin(byte gpio, bool output, PinOwner tag);
|
||||
// Allocates all the pins, or allocates none of the pins, with owner tag.
|
||||
// Provided to simplify error condition handling in clients
|
||||
// using more than one pin, such as I2C, SPI, rotary encoders,
|
||||
// ethernet, etc..
|
||||
static bool allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag );
|
||||
[[deprecated("Replaced by three-parameter allocatePin(gpio, output, ownerTag), for improved debugging")]]
|
||||
inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); }
|
||||
[[deprecated("Replaced by two-parameter deallocatePin(gpio, ownerTag), for improved debugging")]]
|
||||
inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); }
|
||||
|
||||
[[deprecated("Replaced by three-parameter allocatePin(gpio, output, ownerTag), for improved debugging")]]
|
||||
static inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); }
|
||||
[[deprecated("Replaced by two-parameter deallocatePin(gpio, ownerTag), for improved debugging")]]
|
||||
static inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); }
|
||||
// will return true for reserved pins
|
||||
bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None);
|
||||
// will return false for reserved pins
|
||||
bool isPinOk(byte gpio, bool output = true);
|
||||
|
||||
bool isReadOnlyPin(byte gpio);
|
||||
|
||||
// will return true for reserved pins
|
||||
static bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None);
|
||||
// will return false for reserved pins
|
||||
static bool isPinOk(byte gpio, bool output = true);
|
||||
|
||||
static bool isReadOnlyPin(byte gpio);
|
||||
PinOwner getPinOwner(byte gpio);
|
||||
|
||||
static PinOwner getPinOwner(byte gpio);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
static byte allocateLedc(byte channels);
|
||||
static void deallocateLedc(byte pos, byte channels);
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
byte allocateLedc(byte channels);
|
||||
void deallocateLedc(byte pos, byte channels);
|
||||
#endif
|
||||
};
|
||||
|
||||
//extern PinManager pinManager;
|
||||
|
||||
@@ -164,7 +164,7 @@ void handlePresets()
|
||||
|
||||
DEBUG_PRINTF_P(PSTR("Applying preset: %u\n"), (unsigned)tmpPreset);
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3)
|
||||
#if defined(ARDUINO_ARCH_ESP32S3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3)
|
||||
unsigned long start = millis();
|
||||
while (strip.isUpdating() && millis() - start < FRAMETIME_FIXED) yield(); // wait for strip to finish updating, accessing FS during sendout causes glitches
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
* Registration and management utility for v2 usermods
|
||||
*/
|
||||
|
||||
static Usermod* ums[WLED_MAX_USERMODS] = {nullptr};
|
||||
byte UsermodManager::numMods = 0;
|
||||
|
||||
//Usermod Manager internals
|
||||
void UsermodManager::setup() { for (unsigned i = 0; i < numMods; i++) ums[i]->setup(); }
|
||||
void UsermodManager::connected() { for (unsigned i = 0; i < numMods; i++) ums[i]->connected(); }
|
||||
@@ -69,8 +72,6 @@ bool UsermodManager::add(Usermod* um)
|
||||
return true;
|
||||
}
|
||||
|
||||
Usermod* UsermodManager::ums[WLED_MAX_USERMODS] = {nullptr};
|
||||
byte UsermodManager::numMods = 0;
|
||||
|
||||
/* Usermod v2 interface shim for oappend */
|
||||
Print* Usermod::oappend_shim = nullptr;
|
||||
|
||||
@@ -482,10 +482,10 @@ WLED_GLOBAL unsigned long lastMqttReconnectAttempt _INIT(0); // used for other
|
||||
#endif
|
||||
WLED_GLOBAL AsyncMqttClient *mqtt _INIT(NULL);
|
||||
WLED_GLOBAL bool mqttEnabled _INIT(false);
|
||||
WLED_GLOBAL char mqttStatusTopic[40] _INIT(""); // this must be global because of async handlers
|
||||
WLED_GLOBAL char mqttDeviceTopic[MQTT_MAX_TOPIC_LEN+1] _INIT(""); // main MQTT topic (individual per device, default is wled/mac)
|
||||
WLED_GLOBAL char mqttGroupTopic[MQTT_MAX_TOPIC_LEN+1] _INIT("wled/all"); // second MQTT topic (for example to group devices)
|
||||
WLED_GLOBAL char mqttServer[MQTT_MAX_SERVER_LEN+1] _INIT(""); // both domains and IPs should work (no SSL)
|
||||
WLED_GLOBAL char mqttStatusTopic[MQTT_MAX_TOPIC_LEN + 8] _INIT(""); // this must be global because of async handlers
|
||||
WLED_GLOBAL char mqttDeviceTopic[MQTT_MAX_TOPIC_LEN + 1] _INIT(""); // main MQTT topic (individual per device, default is wled/mac)
|
||||
WLED_GLOBAL char mqttGroupTopic[MQTT_MAX_TOPIC_LEN + 1] _INIT("wled/all"); // second MQTT topic (for example to group devices)
|
||||
WLED_GLOBAL char mqttServer[MQTT_MAX_SERVER_LEN + 1] _INIT(""); // both domains and IPs should work (no SSL)
|
||||
WLED_GLOBAL char mqttUser[41] _INIT(""); // optional: username for MQTT auth
|
||||
WLED_GLOBAL char mqttPass[65] _INIT(""); // optional: password for MQTT auth
|
||||
WLED_GLOBAL char mqttClientID[41] _INIT(""); // override the client ID
|
||||
@@ -895,9 +895,6 @@ WLED_GLOBAL uint32_t ledMaps _INIT(0); // bitfield representation of available l
|
||||
WLED_GLOBAL uint16_t ledMaps _INIT(0); // bitfield representation of available ledmaps
|
||||
#endif
|
||||
|
||||
// Usermod manager
|
||||
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
|
||||
|
||||
// global I2C SDA pin (used for usermods)
|
||||
#ifndef I2CSDAPIN
|
||||
WLED_GLOBAL int8_t i2c_sda _INIT(-1);
|
||||
|
||||
4
wled00/wled_eeprom.cpp
Executable file → Normal file
4
wled00/wled_eeprom.cpp
Executable file → Normal file
@@ -2,6 +2,10 @@
|
||||
#include <EEPROM.h>
|
||||
#include "wled.h"
|
||||
|
||||
#if defined(WLED_ENABLE_MQTT) && MQTT_MAX_TOPIC_LEN < 32
|
||||
#error "MQTT topics length < 32 is not supported by the EEPROM module!"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DEPRECATED, do not use for new settings
|
||||
* Only used to restore config from pre-0.11 installations using the deEEP() methods
|
||||
|
||||
Reference in New Issue
Block a user