Hello folks,
this is my first post here. Till now I have always found a solution in these boards or other forums, but this time I can't find a solution.
Couple years ago I had made a code I used on the Adafruit Feather M0 Bluefruit. I tried to port it to the Nano 33 IoT, however, I notice that I can't make the Bluetooth eventHandler work. In the Serialport I can see, that BLE.poll()
is called, however, I can't connect my phone via ble to it when the switch is set to ProgState = ='O'. If I remove the code for ProgState 'O' or change the start condition to something else like ProgState == 'R'. The eventHandler is working.
Can you please help me?
The purpose of this code is to sample signals from a electret microphone and then do a FFT and turn it into a color spectrum on a SK9822 RGB Strip. For this I am using FASTLED.
// Audio Spectrum Display
// Copyright 2021 Dave Sircar
// Used hardware: Arduino Nano 33 IoT + Adafruit MAX9814 AGC Electret Microphone with in-built Amplifier
#define ARM_MATH_CM0PLUS
#include <arm_math.h>
#include "arm_const_structs.h"
#include <FastLED.h>
#include <ArduinoBLE.h>
#define TIMER_PRESCALER_DIV 1024
#define SK9822_PIXEL_COUNT 23 // Number of Sk9822 pixels
#define BRIGHTNESS 127
#define LED_TYPE SK9822
#define COLOR_ORDER BGR
////////////////////////////////////////////////////////////////////////////////
// CONIFIGURATION
// These values can be changed to alter the behavior of the spectrum display.
////////////////////////////////////////////////////////////////////////////////
int SAMPLE_RATE_HZ = 9600; // Sample rate of the audio in hertz.
float SPECTRUM_MIN_DB = 30.0; // Audio intensity (in decibels) that maps to low LED brightness.
float SPECTRUM_MAX_DB = 60.0; // Audio intensity (in decibels) that maps to high LED brightness.
int LEDS_ENABLED = 1; // Control if the LED's should display the spectrum or not. 1 is true, 0 is false.
// Useful for turning the LED display on and off with commands from the serial port.
const int FFT_SIZE = 256; // Size of the FFT. Realistically can only be at most 256
// without running out of memory for buffers and other state.
const int AUDIO_INPUT_PIN = 18; // Input ADC pin for audio data 18=A4
const int POWER_LED_PIN = 13; // Output pin for power LED (pin 13 to use Teensy 3.0's onboard LED).
uint32_t refIndex = 255, testIndex = 0;// needed by the arm_cmsis_cfft code
////////////////////////////////////////////////////////////////////////////////
// INTERNAL STATE
// These shouldn't be modified unless you know what you're doing.
////////////////////////////////////////////////////////////////////////////////
float samples[FFT_SIZE*2];
float magnitudes[FFT_SIZE];
int sampleCounter = 0;
//#define SPI_DATA 11 //This value is here as a reminder, that I made some manual correction in the fastpin_arm_d21.h file of FastLED!!!
//#define SPI_CLOCK 13
CRGB leds[SK9822_PIXEL_COUNT];
float frequencyWindow[SK9822_PIXEL_COUNT+1];
float hues[SK9822_PIXEL_COUNT];
CRGBPalette16 currentPalette;
TBlendType currentBlending;
char ProgState = 'O'; // O is spectrum analyser state
int redL,blueL,greenL;
BLEService ledService("19B16860-E8F2-537E-4F6C-D104768A1214"); // create service BLEService
BLEByteCharacteristic switchCharacteristic("19B16861-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); // create switch characteristic and allow remote device to read and write
////////////////////////////////////////////////////////////////////////////////
// MAIN SKETCH FUNCTIONS
////////////////////////////////////////////////////////////////////////////////
void setup() {
// Set up serial port.
Serial.begin(115200);
while (!Serial);
//Begin BLE
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1);
}
// set the local name peripheral advertises
BLE.setLocalName("TheName");
// set the UUID for the service this peripheral advertises
BLE.setAdvertisedService(ledService);
// add the characteristic to the service
ledService.addCharacteristic(switchCharacteristic);
// add service
BLE.addService(ledService);
// assign event handlers for connected, disconnected to peripheral
BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
// assign event handlers for characteristic
switchCharacteristic.setEventHandler(BLEWritten, switchCharacteristicWritten);
// set an initial value for the characteristic
switchCharacteristic.setValue(0);
// start advertising
BLE.advertise();
Serial.println(("Bluetooth device active, waiting for connections..."));
// Set up ADC and audio input.
pinMode(AUDIO_INPUT_PIN, INPUT);
// Turn on the power indicator LED.
pinMode(POWER_LED_PIN, OUTPUT);
digitalWrite(POWER_LED_PIN, HIGH);
// Initialize FastLED library and turn off the LEDs
FastLED.addLeds<LED_TYPE, COLOR_ORDER>(leds, SK9822_PIXEL_COUNT).setCorrection( TypicalLEDStrip );
for(int dot = 0; dot < SK9822_PIXEL_COUNT; dot++){
leds[dot] = CRGB::Black;
FastLED.show();
}
// Initialize spectrum display
spectrumSetup();
tcConfigure(SAMPLE_RATE_HZ); //configure the timer to run at <SAMPLE_RATE_HZ>
// Begin sampling audio
samplingBegin();
}
void loop() {
switch (ProgState){
case 'O': // Spectrum Analyser Mode
// Calculate FFT if a full sample is available.
if (samplingIsDone()) {
// Run FFT on sample data.
arm_status status;
float32_t maxValue;
status = ARM_MATH_SUCCESS;
/* Process the data through the CFFT/CIFFT module */
arm_cfft_f32(&arm_cfft_sR_f32_len256, samples, 0, 1);
/* Process the data through the Complex Magnitude Module for
calculating the magnitude at each bin */
arm_cmplx_mag_f32(samples, magnitudes, FFT_SIZE);
/* Calculates maxValue and returns corresponding BIN value */
arm_max_f32(magnitudes, FFT_SIZE, &maxValue, &testIndex);
if(testIndex != refIndex){
status = ARM_MATH_TEST_FAILURE;
}
if (LEDS_ENABLED == 1)
{
spectrumLoop();
}
// Restart audio sampling.
samplingBegin();
Serial.print("SamplingBegin() ");
Serial.println(micros());
}
break;
case 'C': //Const Colour shown as inputed by bluetooth
FastLED.show();
break;
case 'F':
static uint8_t var_k = 0;
fill_solid(leds, SK9822_PIXEL_COUNT, CHSV(var_k, 255, 51) );
FastLED.show();
var_k++;
FastLED.delay(200);
break;
case 'R': //Theater chase in Rainbow Palette Colours
currentPalette = RainbowColors_p; //IMPLEMENT OTHER COLORPALETTES!!!!!!!!!!!!!!!!!!!!!
currentBlending = LINEARBLEND;
static uint8_t startIndex = 0;
EVERY_N_MILLISECONDS(200) {
startIndex = startIndex + 1; /* motion speed */
FillLEDsFromPaletteColors( startIndex);
FastLED.show();
}
break;
}
// poll for BLE events
BLE.poll();
Serial.print("\t Marker BLE.poll: ");
Serial.println(micros());
}
////////////////////////////////////////////////////////////////////////////////
//This function gets called by the interrupt at <SAMPLE_RATE_HZ>
////////////////////////////////////////////////////////////////////////////////
void TC4_Handler (void) {
//YOUR CODE HERE
samplingCallback();
// END OF YOUR CODE
TC4->COUNT16.INTFLAG.bit.MC0 = 1; //don't change this, it's part of the timer code
}
////////////////////////////////////////////////////////////////////////////////
// UTILITY FUNCTIONS
////////////////////////////////////////////////////////////////////////////////
// Compute the average magnitude of a target frequency window vs. all other frequencies.
void windowMean(float* magnitudes, int lowBin, int highBin, float* windowMean, float* otherMean) {
*windowMean = 0;
*otherMean = 0;
// Notice the first magnitude bin is skipped because it represents the
// average power of the signal.
for (int i = 1; i < FFT_SIZE/2; ++i) {
if (i >= lowBin && i <= highBin) {
*windowMean += magnitudes[i];
}
else {
*otherMean += magnitudes[i];
}
}
*windowMean /= (highBin - lowBin) + 1;
*otherMean /= (FFT_SIZE / 2 - (highBin - lowBin));
}
// Convert a frequency to the appropriate FFT bin it will fall within.
int frequencyToBin(float frequency) { //Actually "frequency" should be named "frequencyWindow", however, not done as this variable already exists
float binFrequency = float(SAMPLE_RATE_HZ) / float(FFT_SIZE);
return int(frequency / binFrequency);
}
////////////////////////////////////////////////////////////////////////////////
// SPECTRUM DISPLAY FUNCTIONS
///////////////////////////////////////////////////////////////////////////////
void spectrumSetup() {
// Set the frequency window values by evenly dividing the possible frequency
// spectrum across the number of SK9822 pixels.
//float windowSize = (SAMPLE_RATE_HZ / 2.0) / float(SK9822_PIXEL_COUNT/2); //For centered LEDs useful
//for (int i = 0; i < SK9822_PIXEL_COUNT/2+1; ++i) {
float windowSize = (SAMPLE_RATE_HZ / 2.0) / float(SK9822_PIXEL_COUNT);
for (int i = 0; i < SK9822_PIXEL_COUNT+1; ++i) {
frequencyWindow[i] = i*windowSize; //upper window frequencies are calculated
}
// Evenly spread hues across all pixels.
for (int i = 0; i < SK9822_PIXEL_COUNT+1; ++i) {
leds[i] = CHSV(255 * (float(i)/float(SK9822_PIXEL_COUNT)), 255, 51);
}
FastLED.show();
}
void spectrumLoop() {
// Update each LED based on the intensity of the audio
// in the associated frequency window.
float intensity, otherMean;
for (int i = 0; i < SK9822_PIXEL_COUNT; ++i) {
windowMean(magnitudes,
frequencyToBin(frequencyWindow[i]),
frequencyToBin(frequencyWindow[i+1]),
&intensity,
&otherMean);
// Convert intensity to decibels.
intensity = 20.0*log10(intensity);
// Scale the intensity and clamp between 0 and 1.0.
intensity -= SPECTRUM_MIN_DB;
intensity = intensity < 0.0 ? 0.0 : intensity;
intensity /= (SPECTRUM_MAX_DB-SPECTRUM_MIN_DB);
intensity = intensity > 1.0 ? 1.0 : intensity;
leds[i] = CHSV(240-intensity*240, 255, 127*intensity); //The stonger the frequencies, the more the colour changes from dark blue to bright red
}
FastLED.show();
}
void FillLEDsFromPaletteColors( uint8_t colorIndex){
uint8_t brightness = 255;
for( int i = 0; i < SK9822_PIXEL_COUNT; ++i) {
leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
colorIndex += 3;
}
}
////////////////////////////////////////////////////////////////////////////////
// SAMPLING FUNCTIONS
////////////////////////////////////////////////////////////////////////////////
void samplingCallback() {
// Read from the ADC and store the sample data
samples[sampleCounter] = (float32_t)analogRead(AUDIO_INPUT_PIN);
// Complex FFT functions require a coefficient for the imaginary part of the input.
// Since we only have real data, set this coefficient to zero.
samples[sampleCounter+1] = 0.0;
// Update sample buffer position and stop after the buffer is filled
sampleCounter += 2;
if (sampleCounter >= FFT_SIZE*2) {
tcDisable();
//tcReset();
}
}
void samplingBegin() {
// Reset sample buffer position and start callback at necessary rate.
sampleCounter = 0;
tcStartCounter(); //starts the timer
}
bool samplingIsDone(){
return sampleCounter >= FFT_SIZE*2;
}
////////////////////////////////////////////////////////////////////////////////
// TIMER SPECIFIC FUNCTIONS FOLLOW
// you shouldn't change these unless you know what you're doing
////////////////////////////////////////////////////////////////////////////////
//Configures the TC to generate output events at the sample frequency.
//Configures the TC in Frequency Generation mode, with an event output once
//each time the audio sample frequency period expires.
void tcConfigure(int SAMPLE_RATE_HZ){
// Enable GCLK for TCC2 and TC4 (timer counter input clock)
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)) ;
while (GCLK->STATUS.bit.SYNCBUSY);
tcReset(); //reset TC4
// Set Timer counter Mode to 16 bits
TC4->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
// Set TC4 mode as match frequency
TC4->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
//set prescaler and enable TC4
TC4->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1024 | TC_CTRLA_ENABLE;
//set TC4 timer counter based off of the system clock and the user defined sample rate or waveform
TC4->COUNT16.CC[0].reg = (uint16_t) (SystemCoreClock / (TIMER_PRESCALER_DIV*SAMPLE_RATE_HZ) - 1);
while (tcIsSyncing());
// Configure interrupt request
NVIC_DisableIRQ(TC4_IRQn);
NVIC_ClearPendingIRQ(TC4_IRQn);
NVIC_SetPriority(TC4_IRQn, 0);
NVIC_EnableIRQ(TC4_IRQn);
// Enable the TC4 interrupt request
TC4->COUNT16.INTENSET.bit.MC0 = 1;
while (tcIsSyncing()); //wait until TC4 is done syncing
}
//Function that is used to check if TC4 is done syncing
//returns true when it is done syncing
bool tcIsSyncing(){
return TC4->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;
}
//This function enables TC4 and waits for it to be ready
void tcStartCounter(){
TC4->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; //set the CTRLA register
while (tcIsSyncing()); //wait until snyc'd
}
//Reset TC4
void tcReset(){
TC4->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
while (tcIsSyncing());
while (TC4->COUNT16.CTRLA.bit.SWRST);
}
//disable TC4
void tcDisable(){
TC4->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
while (tcIsSyncing());
}
////////////////////////////////////////////////////////////////////////////////
// Functons and variables for the BLE module
////////////////////////////////////////////////////////////////////////////////
void blePeripheralConnectHandler(BLEDevice central) {
// central connected event handler
Serial.print("Connected event, central: ");
Serial.println(central.address());
}
void blePeripheralDisconnectHandler(BLEDevice central) {
// central disconnected event handler
Serial.print("Disconnected event, central: ");
Serial.println(central.address());
}
void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) {
// central wrote new value to characteristic
Serial.print("Characteristic event, written: ");
if (switchCharacteristic.value()) {
Serial.println("LED on");
digitalWrite(POWER_LED_PIN, HIGH);
} else {
Serial.println("LED off");
digitalWrite(POWER_LED_PIN, LOW);
}
//MORE CODE
}```