initial commit
This commit is contained in:
46
lib/README
Normal file
46
lib/README
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
5
lib/audio_input/library.json
Normal file
5
lib/audio_input/library.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"build": {
|
||||
"flags": "-Ofast"
|
||||
}
|
||||
}
|
||||
32
lib/audio_input/src/ADCSampler.cpp
Normal file
32
lib/audio_input/src/ADCSampler.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "ADCSampler.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
|
||||
ADCSampler::ADCSampler(adc_unit_t adcUnit, adc1_channel_t adcChannel, const i2s_config_t &i2s_config) : I2SSampler(I2S_NUM_0, i2s_config)
|
||||
{
|
||||
m_adcUnit = adcUnit;
|
||||
m_adcChannel = adcChannel;
|
||||
}
|
||||
|
||||
void ADCSampler::configureI2S()
|
||||
{
|
||||
//init ADC pad
|
||||
i2s_set_adc_mode(m_adcUnit, m_adcChannel);
|
||||
// enable the adc
|
||||
i2s_adc_enable(m_i2sPort);
|
||||
}
|
||||
|
||||
int ADCSampler::read(int16_t *samples, int count)
|
||||
{
|
||||
// read from i2s
|
||||
size_t bytes_read = 0;
|
||||
i2s_read(m_i2sPort, samples, sizeof(int16_t) * count, &bytes_read, portMAX_DELAY);
|
||||
int samples_read = bytes_read / sizeof(int16_t);
|
||||
for (int i = 0; i < samples_read; i++)
|
||||
{
|
||||
samples[i] = (2048 - (uint16_t(samples[i]) & 0xfff)) * 15;
|
||||
}
|
||||
return samples_read;
|
||||
}
|
||||
|
||||
#endif
|
||||
18
lib/audio_input/src/ADCSampler.h
Normal file
18
lib/audio_input/src/ADCSampler.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <driver/adc.h>
|
||||
#include "I2SSampler.h"
|
||||
|
||||
class ADCSampler : public I2SSampler
|
||||
{
|
||||
private:
|
||||
adc_unit_t m_adcUnit;
|
||||
adc1_channel_t m_adcChannel;
|
||||
|
||||
protected:
|
||||
void configureI2S();
|
||||
|
||||
public:
|
||||
ADCSampler(adc_unit_t adc_unit, adc1_channel_t adc_channel, const i2s_config_t &i2s_config);
|
||||
virtual int read(int16_t *samples, int count);
|
||||
};
|
||||
52
lib/audio_input/src/I2SMEMSSampler.cpp
Normal file
52
lib/audio_input/src/I2SMEMSSampler.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "I2SMEMSSampler.h"
|
||||
#include "soc/i2s_reg.h"
|
||||
|
||||
I2SMEMSSampler::I2SMEMSSampler(
|
||||
i2s_port_t i2s_port,
|
||||
i2s_pin_config_t &i2s_pins,
|
||||
i2s_config_t i2s_config,
|
||||
int raw_samples_size,
|
||||
bool fixSPH0645) : I2SSampler(i2s_port, i2s_config)
|
||||
{
|
||||
m_i2sPins = i2s_pins;
|
||||
m_fixSPH0645 = fixSPH0645;
|
||||
m_raw_samples_size = raw_samples_size;
|
||||
m_raw_samples = (int32_t *)malloc(sizeof(int32_t) * raw_samples_size);
|
||||
}
|
||||
|
||||
I2SMEMSSampler::~I2SMEMSSampler()
|
||||
{
|
||||
free(m_raw_samples);
|
||||
}
|
||||
|
||||
void I2SMEMSSampler::configureI2S()
|
||||
{
|
||||
if (m_fixSPH0645)
|
||||
{
|
||||
// FIXES for SPH0645
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
REG_SET_BIT(I2S_TIMING_REG(m_i2sPort), BIT(9));
|
||||
REG_SET_BIT(I2S_CONF_REG(m_i2sPort), I2S_RX_MSB_SHIFT);
|
||||
#endif
|
||||
}
|
||||
|
||||
i2s_set_pin(m_i2sPort, &m_i2sPins);
|
||||
}
|
||||
|
||||
int I2SMEMSSampler::read(int16_t *samples, int count)
|
||||
{
|
||||
// read from i2s
|
||||
size_t bytes_read = 0;
|
||||
if (count>m_raw_samples_size)
|
||||
{
|
||||
count = m_raw_samples_size; // Buffer is too small
|
||||
}
|
||||
i2s_read(m_i2sPort, m_raw_samples, sizeof(int32_t) * count, &bytes_read, portMAX_DELAY);
|
||||
int samples_read = bytes_read / sizeof(int32_t);
|
||||
for (int i = 0; i < samples_read; i++)
|
||||
{
|
||||
int32_t temp = m_raw_samples[i] >> 11;
|
||||
samples[i] = (temp > INT16_MAX) ? INT16_MAX : (temp < -INT16_MAX) ? -INT16_MAX : (int16_t)temp;
|
||||
}
|
||||
return samples_read;
|
||||
}
|
||||
25
lib/audio_input/src/I2SMEMSSampler.h
Normal file
25
lib/audio_input/src/I2SMEMSSampler.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "I2SSampler.h"
|
||||
|
||||
class I2SMEMSSampler : public I2SSampler
|
||||
{
|
||||
private:
|
||||
i2s_pin_config_t m_i2sPins;
|
||||
bool m_fixSPH0645;
|
||||
int32_t *m_raw_samples;
|
||||
int m_raw_samples_size;
|
||||
|
||||
protected:
|
||||
void configureI2S();
|
||||
|
||||
public:
|
||||
I2SMEMSSampler(
|
||||
i2s_port_t i2s_port,
|
||||
i2s_pin_config_t &i2s_pins,
|
||||
i2s_config_t i2s_config,
|
||||
int raw_samples_size,
|
||||
bool fixSPH0645 = false);
|
||||
~I2SMEMSSampler();
|
||||
virtual int read(int16_t *samples, int count);
|
||||
};
|
||||
21
lib/audio_input/src/I2SSampler.cpp
Normal file
21
lib/audio_input/src/I2SSampler.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
#include "I2SSampler.h"
|
||||
#include "driver/i2s.h"
|
||||
|
||||
I2SSampler::I2SSampler(i2s_port_t i2sPort, const i2s_config_t &i2s_config) : m_i2sPort(i2sPort), m_i2s_config(i2s_config)
|
||||
{
|
||||
}
|
||||
|
||||
void I2SSampler::start()
|
||||
{
|
||||
//install and start i2s driver
|
||||
i2s_driver_install(m_i2sPort, &m_i2s_config, 0, NULL);
|
||||
// set up the I2S configuration from the subclass
|
||||
configureI2S();
|
||||
}
|
||||
|
||||
void I2SSampler::stop()
|
||||
{
|
||||
// stop the i2S driver
|
||||
i2s_driver_uninstall(m_i2sPort);
|
||||
}
|
||||
28
lib/audio_input/src/I2SSampler.h
Normal file
28
lib/audio_input/src/I2SSampler.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <driver/i2s.h>
|
||||
|
||||
/**
|
||||
* Base Class for both the ADC and I2S sampler
|
||||
**/
|
||||
class I2SSampler
|
||||
{
|
||||
protected:
|
||||
i2s_port_t m_i2sPort = I2S_NUM_0;
|
||||
i2s_config_t m_i2s_config;
|
||||
virtual void configureI2S() = 0;
|
||||
virtual void processI2SData(void *samples, size_t count){
|
||||
// nothing to do for the default case
|
||||
};
|
||||
|
||||
public:
|
||||
I2SSampler(i2s_port_t i2sPort, const i2s_config_t &i2sConfig);
|
||||
void start();
|
||||
virtual int read(int16_t *samples, int count) = 0;
|
||||
void stop();
|
||||
int sample_rate()
|
||||
{
|
||||
return m_i2s_config.sample_rate;
|
||||
}
|
||||
};
|
||||
5
lib/audio_output/library.json
Normal file
5
lib/audio_output/library.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"build": {
|
||||
"flags": "-Ofast"
|
||||
}
|
||||
}
|
||||
30
lib/audio_output/src/DACOutput.cpp
Normal file
30
lib/audio_output/src/DACOutput.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "DACOutput.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
|
||||
void DACOutput::start(int sample_rate)
|
||||
{
|
||||
// i2s config for writing both channels of I2S
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
|
||||
.sample_rate = sample_rate,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S_MSB),
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 2,
|
||||
.dma_buf_len = 1024,
|
||||
.use_apll = false,
|
||||
.tx_desc_auto_clear = true,
|
||||
.fixed_mclk = 0};
|
||||
//install and start i2s driver
|
||||
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
|
||||
// enable the DAC channels
|
||||
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
||||
// clear the DMA buffers
|
||||
i2s_zero_dma_buffer(I2S_NUM_0);
|
||||
|
||||
i2s_start(I2S_NUM_0);
|
||||
}
|
||||
|
||||
#endif
|
||||
20
lib/audio_output/src/DACOutput.h
Normal file
20
lib/audio_output/src/DACOutput.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <driver/i2s.h>
|
||||
|
||||
#include "Output.h"
|
||||
|
||||
/**
|
||||
* Base Class for both the ADC and I2S sampler
|
||||
**/
|
||||
class DACOutput : public Output
|
||||
{
|
||||
public:
|
||||
DACOutput(i2s_port_t i2s_port) : Output(i2s_port) {}
|
||||
void start(int sample_rate);
|
||||
virtual int16_t process_sample(int16_t sample)
|
||||
{
|
||||
// DAC needs unsigned 16 bit samples
|
||||
return sample + 32768;
|
||||
}
|
||||
};
|
||||
35
lib/audio_output/src/I2SOutput.cpp
Normal file
35
lib/audio_output/src/I2SOutput.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
#include "I2SOutput.h"
|
||||
|
||||
I2SOutput::I2SOutput(i2s_port_t i2s_port, i2s_pin_config_t &i2s_pins) : Output(i2s_port), m_i2s_pins(i2s_pins)
|
||||
{
|
||||
}
|
||||
|
||||
void I2SOutput::start(int sample_rate)
|
||||
{
|
||||
// i2s config for writing both channels of I2S
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
|
||||
.sample_rate = sample_rate,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S),
|
||||
#else
|
||||
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S),
|
||||
#endif
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 2,
|
||||
.dma_buf_len = 1024,
|
||||
.use_apll = false,
|
||||
.tx_desc_auto_clear = true,
|
||||
.fixed_mclk = 0};
|
||||
//install and start i2s driver
|
||||
i2s_driver_install(m_i2s_port, &i2s_config, 0, NULL);
|
||||
// set up the i2s pins
|
||||
i2s_set_pin(m_i2s_port, &m_i2s_pins);
|
||||
// clear the DMA buffers
|
||||
i2s_zero_dma_buffer(m_i2s_port);
|
||||
|
||||
i2s_start(m_i2s_port);
|
||||
}
|
||||
16
lib/audio_output/src/I2SOutput.h
Normal file
16
lib/audio_output/src/I2SOutput.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "Output.h"
|
||||
|
||||
/**
|
||||
* Base Class for both the ADC and I2S sampler
|
||||
**/
|
||||
class I2SOutput : public Output
|
||||
{
|
||||
private:
|
||||
i2s_pin_config_t m_i2s_pins;
|
||||
|
||||
public:
|
||||
I2SOutput(i2s_port_t i2s_port, i2s_pin_config_t &i2s_pins);
|
||||
void start(int sample_rate);
|
||||
};
|
||||
51
lib/audio_output/src/Output.cpp
Normal file
51
lib/audio_output/src/Output.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
#include "Output.h"
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2s.h>
|
||||
|
||||
static const char *TAG = "OUT";
|
||||
|
||||
// number of frames to try and send at once (a frame is a left and right sample)
|
||||
const int NUM_FRAMES_TO_SEND = 256;
|
||||
|
||||
Output::Output(i2s_port_t i2s_port) : m_i2s_port(i2s_port)
|
||||
{
|
||||
// this will contain the prepared samples for sending to the I2S device
|
||||
m_frames = (int16_t *)malloc(2 * sizeof(int16_t) * NUM_FRAMES_TO_SEND);
|
||||
}
|
||||
|
||||
Output::~Output()
|
||||
{
|
||||
free(m_frames);
|
||||
}
|
||||
|
||||
void Output::stop()
|
||||
{
|
||||
// stop the i2S driver
|
||||
i2s_stop(m_i2s_port);
|
||||
i2s_driver_uninstall(m_i2s_port);
|
||||
}
|
||||
|
||||
void Output::write(int16_t *samples, int count)
|
||||
{
|
||||
int sample_index = 0;
|
||||
while (sample_index < count)
|
||||
{
|
||||
int samples_to_send = 0;
|
||||
for (int i = 0; i < NUM_FRAMES_TO_SEND && sample_index < count; i++)
|
||||
{
|
||||
int sample = process_sample(samples[sample_index]);
|
||||
m_frames[i * 2] = sample; // left channel
|
||||
m_frames[i * 2 + 1] = sample; // right channel
|
||||
samples_to_send++;
|
||||
sample_index++;
|
||||
}
|
||||
// write data to the i2s peripheral
|
||||
size_t bytes_written = 0;
|
||||
i2s_write(m_i2s_port, m_frames, samples_to_send * sizeof(int16_t) * 2, &bytes_written, portMAX_DELAY);
|
||||
if (bytes_written != samples_to_send * sizeof(int16_t) * 2)
|
||||
{
|
||||
ESP_LOGE(TAG, "Did not write all bytes");
|
||||
}
|
||||
}
|
||||
}
|
||||
27
lib/audio_output/src/Output.h
Normal file
27
lib/audio_output/src/Output.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <driver/i2s.h>
|
||||
|
||||
/**
|
||||
* Base Class for both the DAC and I2S output
|
||||
**/
|
||||
class Output
|
||||
{
|
||||
private:
|
||||
int16_t *m_frames;
|
||||
|
||||
protected:
|
||||
i2s_port_t m_i2s_port = I2S_NUM_0;
|
||||
|
||||
public:
|
||||
Output(i2s_port_t i2s_port);
|
||||
~Output();
|
||||
virtual void start(int sample_rate) = 0;
|
||||
void stop();
|
||||
// override this in derived classes to turn the sample into
|
||||
// something the output device expects - for the default case
|
||||
// this is simply a pass through
|
||||
virtual int16_t process_sample(int16_t sample) { return sample; }
|
||||
void write(int16_t *samples, int count);
|
||||
};
|
||||
99
lib/audio_output/src/OutputBuffer.h
Normal file
99
lib/audio_output/src/OutputBuffer.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
|
||||
/**
|
||||
* @brief Circular buffer for 8 bit unsigned PCM samples
|
||||
*
|
||||
*/
|
||||
class OutputBuffer
|
||||
{
|
||||
private:
|
||||
// how many samples should we buffer before outputting data?
|
||||
int m_number_samples_to_buffer;
|
||||
// where are we reading from
|
||||
int m_read_head;
|
||||
// where are we writing to
|
||||
int m_write_head;
|
||||
// keep track of how many samples we have
|
||||
int m_available_samples;
|
||||
// the total size of the buffer
|
||||
int m_buffer_size;
|
||||
// are we currently buffering samples?
|
||||
bool m_buffering;
|
||||
// the sample buffer
|
||||
uint8_t *m_buffer;
|
||||
// thread safety
|
||||
SemaphoreHandle_t m_semaphore;
|
||||
|
||||
public:
|
||||
OutputBuffer(int number_samples_to_buffer) : m_number_samples_to_buffer(number_samples_to_buffer)
|
||||
{
|
||||
// create a semaphore and make it available for locking
|
||||
m_semaphore = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(m_semaphore);
|
||||
// set reading and writing to the beginning of the buffer
|
||||
m_read_head = 0;
|
||||
m_write_head = 0;
|
||||
m_available_samples = 0;
|
||||
// we'll start off buffering data as we have no samples yet
|
||||
m_buffering = true;
|
||||
// make sufficient space for the bufferring and incoming data
|
||||
m_buffer_size = 3 * number_samples_to_buffer;
|
||||
m_buffer = (uint8_t *)malloc(m_buffer_size);
|
||||
memset(m_buffer, 0, m_buffer_size);
|
||||
if (!m_buffer)
|
||||
{
|
||||
Serial.println("Failed to allocate buffer");
|
||||
}
|
||||
}
|
||||
|
||||
// we're adding samples that are 8 bit as they are coming from the transport
|
||||
void add_samples(const uint8_t *samples, int count)
|
||||
{
|
||||
xSemaphoreTake(m_semaphore, portMAX_DELAY);
|
||||
// copy the samples into the buffer wrapping around as needed
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
m_buffer[m_write_head] = samples[i];
|
||||
m_write_head = (m_write_head + 1) % m_buffer_size;
|
||||
}
|
||||
m_available_samples += count;
|
||||
xSemaphoreGive(m_semaphore);
|
||||
}
|
||||
|
||||
// convert the samples to 16 bit as they are going to the output
|
||||
void remove_samples(int16_t *samples, int count)
|
||||
{
|
||||
xSemaphoreTake(m_semaphore, portMAX_DELAY);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
samples[i] = 0;
|
||||
// if we have no samples and we aren't already buffering then we need to start buffering
|
||||
if (m_available_samples == 0 && !m_buffering)
|
||||
{
|
||||
Serial.println("Buffering");
|
||||
m_buffering = true;
|
||||
samples[i] = 0;
|
||||
}
|
||||
// are we buffering?
|
||||
if (m_buffering && m_available_samples < m_number_samples_to_buffer)
|
||||
{
|
||||
// just return 0 as we don't have enough samples yet
|
||||
samples[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we've buffered enough samples so no need to buffer anymore
|
||||
m_buffering = false;
|
||||
// just send back the samples we've got and move the read head forward
|
||||
int16_t sample = m_buffer[m_read_head];
|
||||
samples[i] = (sample - 128) << 5;
|
||||
m_read_head = (m_read_head + 1) % m_buffer_size;
|
||||
m_available_samples--;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(m_semaphore);
|
||||
}
|
||||
};
|
||||
26
lib/indicator_led/src/GenericDevBoardIndicatorLed.cpp
Normal file
26
lib/indicator_led/src/GenericDevBoardIndicatorLed.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "Arduino.h"
|
||||
#include "GenericDevBoardIndicatorLed.h"
|
||||
|
||||
#ifdef LED_BUILTIN
|
||||
const uint8_t BUILT_IN_LED = LED_BUILTIN;
|
||||
#else
|
||||
const uint8_t BUILT_IN_LED = GPIO_NUM_2;
|
||||
#endif
|
||||
|
||||
GenericDevBoardIndicatorLed::GenericDevBoardIndicatorLed()
|
||||
{
|
||||
pinMode(BUILT_IN_LED, OUTPUT);
|
||||
}
|
||||
|
||||
// we don't really have any colors so just use the built in LED
|
||||
void GenericDevBoardIndicatorLed::set_led_rgb(uint32_t color)
|
||||
{
|
||||
if (color == 0)
|
||||
{
|
||||
digitalWrite(BUILT_IN_LED, LOW);
|
||||
}
|
||||
else
|
||||
{
|
||||
digitalWrite(BUILT_IN_LED, HIGH);
|
||||
}
|
||||
}
|
||||
12
lib/indicator_led/src/GenericDevBoardIndicatorLed.h
Normal file
12
lib/indicator_led/src/GenericDevBoardIndicatorLed.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "IndicatorLed.h"
|
||||
|
||||
class GenericDevBoardIndicatorLed : public IndicatorLed
|
||||
{
|
||||
protected:
|
||||
void set_led_rgb(uint32_t color);
|
||||
|
||||
public:
|
||||
GenericDevBoardIndicatorLed();
|
||||
};
|
||||
34
lib/indicator_led/src/IndicatorLed.cpp
Normal file
34
lib/indicator_led/src/IndicatorLed.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <Arduino.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include "IndicatorLed.h"
|
||||
|
||||
void update_indicator_task(void *param)
|
||||
{
|
||||
IndicatorLed *indicator = reinterpret_cast<IndicatorLed *>(param);
|
||||
while (true)
|
||||
{
|
||||
if (indicator->m_is_flashing)
|
||||
{
|
||||
indicator->set_led_rgb(indicator->m_flash_color);
|
||||
vTaskDelay(100);
|
||||
}
|
||||
indicator->set_led_rgb(indicator->m_default_color);
|
||||
vTaskDelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
void IndicatorLed::begin()
|
||||
{
|
||||
TaskHandle_t task_handle;
|
||||
xTaskCreate(update_indicator_task, "Indicator LED Task", 4096, this, 0, &task_handle);
|
||||
}
|
||||
|
||||
void IndicatorLed::set_is_flashing(bool is_flashing, uint32_t flash_color)
|
||||
{
|
||||
m_is_flashing = is_flashing;
|
||||
m_flash_color = flash_color;
|
||||
}
|
||||
void IndicatorLed::set_default_color(uint32_t color)
|
||||
{
|
||||
m_default_color = color;
|
||||
}
|
||||
21
lib/indicator_led/src/IndicatorLed.h
Normal file
21
lib/indicator_led/src/IndicatorLed.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class IndicatorLed
|
||||
{
|
||||
private:
|
||||
bool m_is_flashing = false;
|
||||
uint32_t m_default_color = 0;
|
||||
uint32_t m_flash_color = 0;
|
||||
|
||||
protected:
|
||||
virtual void set_led_rgb(uint32_t color) = 0;
|
||||
|
||||
public:
|
||||
void begin();
|
||||
void set_is_flashing(bool is_flashing, uint32_t flash_color);
|
||||
void set_default_color(uint32_t color);
|
||||
|
||||
friend void update_indicator_task(void *param);
|
||||
};
|
||||
14
lib/indicator_led_pico/src/TinyPICOIndicatorLed.cpp
Normal file
14
lib/indicator_led_pico/src/TinyPICOIndicatorLed.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "TinyPICOIndicatorLed.h"
|
||||
|
||||
#ifdef ARDUINO_TINYPICO
|
||||
|
||||
TinyPICOIndicatorLed::TinyPICOIndicatorLed()
|
||||
{
|
||||
m_tp = new TinyPICO();
|
||||
}
|
||||
void TinyPICOIndicatorLed::set_led_rgb(uint32_t color)
|
||||
{
|
||||
m_tp->DotStar_SetPixelColor(color);
|
||||
}
|
||||
|
||||
#endif
|
||||
21
lib/indicator_led_pico/src/TinyPICOIndicatorLed.h
Normal file
21
lib/indicator_led_pico/src/TinyPICOIndicatorLed.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "IndicatorLed.h"
|
||||
|
||||
#ifdef ARDUINO_TINYPICO
|
||||
#include <TinyPICO.h>
|
||||
|
||||
class TinyPICO;
|
||||
class TinyPICOIndicatorLed : public IndicatorLed
|
||||
{
|
||||
private:
|
||||
TinyPICO *m_tp = NULL;
|
||||
|
||||
protected:
|
||||
void set_led_rgb(uint32_t color);
|
||||
|
||||
public:
|
||||
TinyPICOIndicatorLed();
|
||||
};
|
||||
|
||||
#endif
|
||||
72
lib/transport/src/EspNowTransport.cpp
Normal file
72
lib/transport/src/EspNowTransport.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <esp_now.h>
|
||||
#include <esp_wifi.h>
|
||||
#include "OutputBuffer.h"
|
||||
#include "EspNowTransport.h"
|
||||
|
||||
const int MAX_ESP_NOW_PACKET_SIZE = 250;
|
||||
const uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
static EspNowTransport *instance = NULL;
|
||||
|
||||
void receiveCallback(const uint8_t *macAddr, const uint8_t *data, int dataLen)
|
||||
{
|
||||
// annoyingly we can't pass an param into this so we need to do a bit of hack to access the EspNowTransport instance
|
||||
int header_size = instance->m_header_size;
|
||||
|
||||
// first m_header_size bytes of m_buffer are the expected header
|
||||
if ((dataLen > header_size) && (dataLen<=MAX_ESP_NOW_PACKET_SIZE) && (memcmp(data,instance->m_buffer,header_size) == 0))
|
||||
{
|
||||
instance->m_output_buffer->add_samples(data + header_size, dataLen - header_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool EspNowTransport::begin()
|
||||
{
|
||||
// Set Wifi channel
|
||||
esp_wifi_set_promiscuous(true);
|
||||
esp_wifi_set_channel(m_wifi_channel, WIFI_SECOND_CHAN_NONE);
|
||||
esp_wifi_set_promiscuous(false);
|
||||
|
||||
esp_err_t result = esp_now_init();
|
||||
if (result == ESP_OK)
|
||||
{
|
||||
Serial.println("ESPNow Init Success");
|
||||
esp_now_register_recv_cb(receiveCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("ESPNow Init failed: %s\n", esp_err_to_name(result));
|
||||
return false;
|
||||
}
|
||||
// this will broadcast a message to everyone in range
|
||||
esp_now_peer_info_t peerInfo = {};
|
||||
memcpy(&peerInfo.peer_addr, broadcastAddress, 6);
|
||||
if (!esp_now_is_peer_exist(broadcastAddress))
|
||||
{
|
||||
result = esp_now_add_peer(&peerInfo);
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
Serial.printf("Failed to add broadcast peer: %s\n", esp_err_to_name(result));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
EspNowTransport::EspNowTransport(OutputBuffer *output_buffer, uint8_t wifi_channel) : Transport(output_buffer, MAX_ESP_NOW_PACKET_SIZE)
|
||||
{
|
||||
instance = this;
|
||||
m_wifi_channel = wifi_channel;
|
||||
}
|
||||
|
||||
void EspNowTransport::send()
|
||||
{
|
||||
|
||||
esp_err_t result = esp_now_send(broadcastAddress, m_buffer, m_index + m_header_size);
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
Serial.printf("Failed to send: %s\n", esp_err_to_name(result));
|
||||
}
|
||||
}
|
||||
16
lib/transport/src/EspNowTransport.h
Normal file
16
lib/transport/src/EspNowTransport.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "Transport.h"
|
||||
|
||||
class OutputBuffer;
|
||||
|
||||
class EspNowTransport: public Transport {
|
||||
private:
|
||||
uint8_t m_wifi_channel;
|
||||
protected:
|
||||
void send();
|
||||
public:
|
||||
EspNowTransport(OutputBuffer *output_buffer, uint8_t wifi_channel);
|
||||
virtual bool begin() override;
|
||||
friend void receiveCallback(const uint8_t *macAddr, const uint8_t *data, int dataLen);
|
||||
};
|
||||
46
lib/transport/src/Transport.cpp
Normal file
46
lib/transport/src/Transport.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "Arduino.h"
|
||||
#include "Transport.h"
|
||||
|
||||
Transport::Transport(OutputBuffer *output_buffer, size_t buffer_size)
|
||||
{
|
||||
m_output_buffer = output_buffer;
|
||||
m_buffer_size = buffer_size;
|
||||
m_buffer = (uint8_t *)malloc(m_buffer_size);
|
||||
m_index = 0;
|
||||
m_header_size = 0;
|
||||
}
|
||||
|
||||
void Transport::add_sample(int16_t sample)
|
||||
{
|
||||
m_buffer[m_index+m_header_size] = (sample + 32768) >> 8;
|
||||
m_index++;
|
||||
// have we reached a full packet?
|
||||
if ((m_index+m_header_size) == m_buffer_size)
|
||||
{
|
||||
send();
|
||||
m_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::flush()
|
||||
{
|
||||
if (m_index >0 )
|
||||
{
|
||||
send();
|
||||
m_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int Transport::set_header(const int header_size, const uint8_t *header)
|
||||
{
|
||||
if ((header_size<m_buffer_size) && (header))
|
||||
{
|
||||
m_header_size = header_size;
|
||||
memcpy(m_buffer, header, header_size);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
26
lib/transport/src/Transport.h
Normal file
26
lib/transport/src/Transport.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class OutputBuffer;
|
||||
|
||||
class Transport
|
||||
{
|
||||
protected:
|
||||
// audio buffer for samples we need to send
|
||||
uint8_t *m_buffer = NULL;
|
||||
int m_buffer_size = 0;
|
||||
int m_index = 0;
|
||||
int m_header_size;
|
||||
|
||||
OutputBuffer *m_output_buffer = NULL;
|
||||
|
||||
virtual void send() = 0;
|
||||
|
||||
public:
|
||||
Transport(OutputBuffer *output_buffer, size_t buffer_size);
|
||||
int set_header(const int header_size, const uint8_t *header);
|
||||
void add_sample(int16_t sample);
|
||||
void flush();
|
||||
virtual bool begin() = 0;
|
||||
};
|
||||
37
lib/transport/src/UdpTransport.cpp
Normal file
37
lib/transport/src/UdpTransport.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <Arduino.h>
|
||||
#include <AsyncUDP.h>
|
||||
#include "UdpTransport.h"
|
||||
#include "OutputBuffer.h"
|
||||
|
||||
const int MAX_UDP_SIZE = 1436;
|
||||
|
||||
UdpTransport::UdpTransport(OutputBuffer *output_buffer) : Transport(output_buffer, MAX_UDP_SIZE)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned long last_packet;
|
||||
bool UdpTransport::begin()
|
||||
{
|
||||
udp = new AsyncUDP();
|
||||
last_packet = millis();
|
||||
if (udp->listen(8192))
|
||||
{
|
||||
udp->onPacket([this](AsyncUDPPacket packet)
|
||||
{
|
||||
// our packets contain unsigned 8 bit PCM samples
|
||||
// so we can push them straight into the output buffer
|
||||
if ((packet.length() > this->m_header_size) && (packet.length() <= MAX_UDP_SIZE) && (memcmp(packet.data(), this->m_buffer, this->m_header_size) == 0))
|
||||
{
|
||||
this->m_output_buffer->add_samples(packet.data() + m_header_size, packet.length() - m_header_size);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
Serial.println("Failed to listen");
|
||||
return false;
|
||||
}
|
||||
|
||||
void UdpTransport::send()
|
||||
{
|
||||
udp->broadcast(m_buffer, m_index);
|
||||
}
|
||||
19
lib/transport/src/UdpTransport.h
Normal file
19
lib/transport/src/UdpTransport.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Transport.h"
|
||||
|
||||
class OutputBuffer;
|
||||
class AsyncUDP;
|
||||
|
||||
class UdpTransport : public Transport
|
||||
{
|
||||
private:
|
||||
AsyncUDP *udp;
|
||||
|
||||
protected:
|
||||
void send();
|
||||
|
||||
public:
|
||||
UdpTransport(OutputBuffer *output_buffer);
|
||||
bool begin() override;
|
||||
};
|
||||
Reference in New Issue
Block a user