Hello,
I am running into a problem that I cannot seem to solve.
My device is acting as a WIFI server with an interactive webpage loaded to SPIFFS.
I am using I2S to perform high speed sampling of a sensor which is then processed for the user.
I did change the WIFI sleep mode, so that it never sleeps and that allowed me to run both WIFI and I2S correctly.
However, I also require low speed occasional sampling of another sensor, and I cannot seem to make that work.
If I try to disable and then enable the I2s bus with I2S_adc_enable and I2s_adc_disable, it crashes with the following error.
assertion "pxQueue->pcHead != ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle()" failed: file "IDF/components/freertos/queue.c", line 782, function: xQueueGenericSend
If I try to simply sample with analogRead, the program gets stuck there and never returns.
Any advice or insight would be greatly appreciated.
AnalogRead with I2S Sampling while running WIFI server isn't working
Re: AnalogRead with I2S Sampling while running WIFI server isn't working
Here is the code running on the ESP32.
The Sample Battery task is the task I am trying to get working.
Let me know if you have any other questions.
The Sample Battery task is the task I am trying to get working.
Let me know if you have any other questions.
Code: Select all
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
//#include "soc/soc.h"
//#include "soc/rtc_cntl_reg.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#include <driver/adc.h>
#include "esp_adc_cal.h"
#include <ESPmDNS.h>
#include "FS.h"
#include <LITTLEFS.h>
#include <SPIFFS.h>
#include "AsyncTCP.h"
#include <ESPAsyncWebServer.h>
#include <driver/i2s.h>
#define ADC_INPUT ADC1_CHANNEL_0 //pin 36
const double samplingFrequency = 20000;
const int SAMPLEBLOCK = 200;
const int NUM_BUFS = 8;
const int PROC_SIZE = 200;
const int FINAL_SIZE = 1020;
const i2s_port_t I2S_PORT = I2S_NUM_0;
uint16_t i2s_data[PROC_SIZE] = {0}; // I2s read buffer.
uint16_t i2s_data1[PROC_SIZE] = {0}; // Storage/processing buffer 1
uint16_t i2s_data2[PROC_SIZE] = {0}; // storage/processing buffer 2
uint16_t i2s_data3[FINAL_SIZE] = {0}; // storage buffer 3 - this is after the drop has been detected.
uint16_t i2s_data_dummy[PROC_SIZE] = {0}; // dump data to here when not actively sampling
bool buf_sel = false;
static QueueHandle_t i2s_queue;
TaskHandle_t S_BAT = NULL;
TaskHandle_t JSON_T = NULL;
TaskHandle_t S_acc_offset = NULL;
TaskHandle_t BUT = NULL;
TaskHandle_t DET = NULL;
#define Bat_pin 39
// define the tasks
void JSON_Task( void *pvParameters );
void Sample_Bat( void *pvParameters );
void Get_acc_offset( void *pvParameters );
void Butterworth_Filter( void *pvParameters );
void Impact_Detection( void *pvParameters );
// define the I2S setup function.
void setupI2S();
bool Sample = false;
bool Sample_bat = false;
bool Buf_sel = false;
const int ADC_SAMPLES_COUNT = 1020;
int Trig_loc = 200;
double ACC_G[ADC_SAMPLES_COUNT] = { 0 };
double Trig_offset_G = 15; // This is level of acceleration in Gs that the unit gets triggered.
int TRIGGER_VAL = 0;
bool Trigger = false;
String JSON;
int Trigger_offset = 100;
int Samples_Remaining = 0;
int glob_count = 0;
const char* PARAM_INPUT_1 = "acc";
const char* PARAM_INPUT_2 = "bat";
// Assign ssid and password for ESP32 AP
const char *ssid = "Testing";
const char *password = "12345678";
#define WIFI_TIMEOUT_MS 20000;
double ADC_TO_V = 1240.91; // divide by this number to convert ADC input to volts
double Bat_correction = 4.025; // converts back correct battery voltage due to voltage divider
double fudge_factor = 1.234; // slight correction factor based on real world test results.
double ACC_C = 500*fudge_factor; // Multiply by this conversion factor to go from volts to Gs. Currently setup for 100mV/G accelerometer. will need to update for final 2mV/G ACC.
double ACC_offset = 0; // This is calculated offset in Volts. This parameter gets updated in software when starting up.
double ACC_offset_G; // Offset in Gs
int Bat_in = 0;
double Bat_V = 0;
int Acc_state = 0; // 0 is idle or don't take a reading. 1 is actively take the reading, 2 is ready to send the data to update the webpage.
int Bat_state = 0; // 0 is not taking the reading, 1 is take a reading, 2 is send the reading
AsyncWebServer server(80);
// Replaces placeholder with button section in your web page
String processor(const String& var){
//Serial.println(var);
if(var == "BUTTONPLACEHOLDER"){
String buttons ="";
String outputStateValue = "1";
String BatStateValue = "1";
buttons+= "<h4>Sampling <span id=\"Acc_outputState\"></span></h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"1\" " + outputStateValue + "><span class=\"slider\"></span></label>";
buttons+= "<h4>Get Battery Voltage <span id=\"Bat_outputState\"></span></h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"2\" " + BatStateValue + "><span class=\"slider\"></span></label>";
return buttons;
}
return String();
}
void setup() {
Serial.begin(115200);
Serial.println("starting ESP32");
delay(1000);
if(!SPIFFS.begin()){
Serial.println("An Error has occurred while mounting SPIFFS");
}
Serial.println("Wifi about to be set up");
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid, password);
Serial.println("Wait 100 ms for AP_START...");
delay(100);
Serial.println("Set softAPConfig");
IPAddress Ip(192, 168, 4, 4);
IPAddress NMask(255, 255, 255, 0);
WiFi.softAPConfig(Ip, Ip, NMask);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
//Turn off wifi power saving mode to allow i2s to work.
WiFi.setSleep(false);
Serial.println("First server call");
server.serveStatic("/", SPIFFS, "/");
Serial.println("Second server call");
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Send a GET request to <ESP_IP>/update?state=<inputMessage>
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage1;
String inputMessage2;
String inputParam;
if (request->hasParam(PARAM_INPUT_1)){
Serial.println(request->getParam(PARAM_INPUT_1)->value());
}
else{
Serial.println("No Param 1");
}
if (request->hasParam(PARAM_INPUT_2)){
Serial.println(request->getParam(PARAM_INPUT_2)->value());
}
else{
Serial.println("No Param 2");
}
// GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
inputParam = PARAM_INPUT_1;
Serial.print("INPUT Bat State: ");
Serial.println(inputMessage1);
Serial.print("INPUT Acc State: ");
Serial.println(inputMessage2);
Serial.print("Curent Bat State: ");
Serial.println(Bat_state);
Serial.print("CURRENT Acc State: ");
Serial.println(Acc_state);
if (Bat_state != 2){
Bat_state = inputMessage1.toInt(); // 0 is Don't sample, 1 is Sample for battery
Serial.println(Bat_state);
}
if(Acc_state != 2 && Acc_state != 3){
Acc_state = inputMessage2.toInt(); // 0 is Don't Sample, 1 is Sample for accelerometer
}
if (Acc_state == 1){
Sample = true;
Serial.print("Sampling on");
}
if (Bat_state == 1){
Serial.println("Sampling Battery");
vTaskResume( S_BAT );
}
}
else {
inputMessage1 = "No message sent";
inputMessage2 = "No message sent";
inputParam = "none";
}
Serial.print("The input Message is ");
Serial.print(inputMessage1);
Serial.print(" and ");
Serial.println(inputMessage2);
request->send(200, "text/plain", "OK");
});
// Send a GET request to <ESP_IP>/state
server.on("/state", HTTP_GET, [] (AsyncWebServerRequest *request) {
request->send(200, "text/plain", String(Acc_state) +","
+ String(Bat_state));
});
// Send graph data for drop
server.on("/readings", HTTP_GET, [] (AsyncWebServerRequest *request){
//Serial.println(JSON);
if (Acc_state == 3){
request->send(200, "text/plain", JSON);
Acc_state = 4;
}
else{
request->send(200,"text/plain","Waiting");
}
});
server.on("/voltage", HTTP_GET, [] (AsyncWebServerRequest *request){
if(Bat_state == 2){
request->send(200, "text/plain", String(Bat_V));
Bat_state = 0;
}
else{
request->send(200, "text/plain" , "Waiting");
}
});
server.begin();
Serial.println("Server Started");
pinMode(Bat_pin, INPUT);
adcAttachPin(Bat_pin);
analogSetAttenuation(ADC_11db);
Serial.println("Setting up Input I2S");
setupI2S();
Serial.println("I2S input setup completed");
Serial.println("Starting Sample bat Task");
xTaskCreatePinnedToCore(
Sample_Bat
, "Sample_Bat"
, 8192 // Stack size
, NULL
, 3 // Priority
, &S_BAT
, NULL);
vTaskSuspend( S_BAT );
Serial.println("Starting JSON Task");
xTaskCreatePinnedToCore(
JSON_Task
, "JSON_Task" // A name just for humans
, 8192*4 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 3 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, &JSON_T
, NULL);
vTaskSuspend( JSON_T );
Serial.println("Starting Get ACC Offset Task");
xTaskCreatePinnedToCore(
Get_acc_offset
, "Get_acc_offset"
, 8192 // Stack size
, NULL
, 2 // Priority
, &S_acc_offset
, NULL);
delay(50);
Serial.println(" Starting Butterworth Filter Task");
xTaskCreatePinnedToCore(
Butterworth_Filter
, "Butterworth_Filter"
, 8192*2 // Stack size
, NULL
, 3 // Priority
, &BUT
, NULL);
vTaskSuspend( BUT );
Serial.println(" Starting Impact Detection Task");
xTaskCreatePinnedToCore(
Impact_Detection
, "Impact_Detection"
, 8192 // Stack size
, NULL
, 2 // Priority
, &DET
, NULL);
}
void loop() {
}
void JSON_Task( void *pvParameters ){
(void) pvParameters;
// portENTER_CRITICAL_ISR(&timerMux);
for(;;)
{
Serial.println("JSON Task Running");
JSON = "{\"data\": [ ";
for (uint16_t w = 0; w < 1000; w++){
JSON = JSON + (String(ACC_G[w],2)) + ", ";
}
int len = JSON.length();
Serial.println(JSON[len-2]);
JSON[len-1] = '}';
JSON[len-2] = ']';
Serial.println(JSON[len-2]);
Serial.println(JSON[len-1]);
Acc_state = 3;
vTaskSuspend( NULL );
}
}
void Sample_Bat( void *pvParameters ){
(void) pvParameters;
for(;;)
{
Serial.println("Sample Bat Running");
Bat_in = analogRead(Bat_pin);
Bat_V = Bat_in / ADC_TO_V * Bat_correction;
Serial.print("Battery Voltage is ");
Serial.println(Bat_V);
Bat_state = 2;
Serial.print("Bat State: ");
Serial.println(Bat_state);
vTaskSuspend( NULL );
}
}
void Get_acc_offset( void *pvParameters ){
(void) pvParameters;
for(;;)
{
size_t bytesRead = 0;
// Read some data from the input pin.
Serial.println("Starting Get_acc_offset");
i2s_event_t evt;
if (xQueueReceive(i2s_queue, &evt, 1) == pdPASS){
if (evt.type == I2S_EVENT_RX_DONE){
i2s_read(I2S_NUM_0, i2s_data_dummy, sizeof(i2s_data_dummy), &bytesRead, portMAX_DELAY);
Serial.println(bytesRead);
//i2s_adc_disable(I2S_NUM_0);
int counter = 0;
for (uint16_t k = 0; k < PROC_SIZE; k++){
counter++;
Serial.printf("The count is %i is: %u \n", counter, i2s_data_dummy[k]);
ACC_offset = ACC_offset + i2s_data_dummy[k];
}
ACC_offset = ACC_offset/ counter;
ACC_offset_G = ACC_offset / ADC_TO_V * ACC_C;
TRIGGER_VAL = (ACC_offset_G - Trig_offset_G) / ACC_C * ADC_TO_V;
Serial.println();
Serial.print(" The accelerometer calibration constant is ");
Serial.println(ACC_offset);
Serial.println();
Serial.print("The accelerometer Trigger Value is: ");
Serial.println(TRIGGER_VAL);
vTaskSuspend( NULL );
}
}
// vTaskResume(BUT);
}
}
void Impact_Detection( void *pvParameters ){
(void) pvParameters;
for(;;)
{
i2s_event_t evt;
if (xQueueReceive(i2s_queue, &evt, 1) == pdPASS){
if (evt.type == I2S_EVENT_RX_DONE){
// Read the data to a buffer
size_t bytesRead = 0;
i2s_read(I2S_NUM_0, i2s_data, sizeof(i2s_data), &bytesRead, portMAX_DELAY);
// if sampling is turned on, do something with the data just read.
if (Sample == true){
// filter for random anomolies
for (uint16_t i = 0; i < PROC_SIZE; i++){
if(i > 1 && i < PROC_SIZE-2){
int ave1 = (i2s_data[i-2]+i2s_data[i-1]+i2s_data[i+1]+i2s_data[i+2])/4;
if (abs(i2s_data[i]-ave1) > 5){
i2s_data[i] = ave1;
}
}
else if(i <= 2){
int ave2 = (i2s_data[i+1]+i2s_data[i+2]+i2s_data[i+3]+i2s_data[i+4])/4;
if (abs(i2s_data[i]-ave2) > 5){
i2s_data[i] = ave2;
}
}
else if(i>=PROC_SIZE-2){
int ave2 = (i2s_data[i-1]+i2s_data[i-2]+i2s_data[i-3]+i2s_data[i-4])/4;
if (abs(i2s_data[i]-ave2) > 5){
i2s_data[i] = ave2;
}
}
}
if (Trigger == false){
//Check for a trigger
for (uint16_t i = 0; i < PROC_SIZE; i++){
//Serial.println(i2s_data1[i]);
if (i2s_data[i] < TRIGGER_VAL){
Trig_loc = i;
Trigger = true;
Serial.print("this is the info that triggered it: ");
Serial.println(i2s_data[i]);
Serial.print("Trigger location: ");
Serial.println(i);
if (Trig_loc < Trigger_offset){
Samples_Remaining = FINAL_SIZE - PROC_SIZE - (Trigger_offset - Trig_loc);
}
else{
Samples_Remaining = FINAL_SIZE - (PROC_SIZE - Trig_loc + Trigger_offset);
}
glob_count = 0; // Resetting a global counter to count up to Samples Remaining.
//Serial.print("Number of samples remaining: ");
//Serial.println(Samples_Remaining);
break;
}
}
if (Trigger == false){
for (uint16_t i = 0; i < PROC_SIZE; i++){
i2s_data1[i] = i2s_data[i];
}
}
else{
for (uint16_t i = 0; i < PROC_SIZE; i++){
i2s_data2[i] = i2s_data[i];
}
}
}
else{
int happened = 0;
for (uint16_t i = 0; i < PROC_SIZE; i++){
if (glob_count < Samples_Remaining){
i2s_data3[glob_count++] = i2s_data[i];
//happened++;
//Serial.print("This happened: ");
//Serial.println(happened);
}
else{
//Serial.print("global counter value: ");
//Serial.println(glob_count);
Trigger = false;
Sample = false;
vTaskResume( BUT );
break;
}
}
}
}
}
}
}
}
void Butterworth_Filter( void *pvParameters ){
(void) pvParameters;
for(;;)
{
int count = 0;
if(Trig_loc < Trigger_offset){
for(uint16_t x = (PROC_SIZE - (Trigger_offset - Trig_loc)); x < PROC_SIZE; x++){
ACC_G[count++] = -(i2s_data1[x] - ACC_offset) / ADC_TO_V * ACC_C;
}
for (uint16_t j = 0; j < PROC_SIZE; j++){
ACC_G[count++] = -(i2s_data2[j] - ACC_offset) / ADC_TO_V * ACC_C;
}
}
else{
for (uint16_t k = (Trig_loc-Trigger_offset); k < PROC_SIZE; k++){
ACC_G[count++] = -(i2s_data2[k]-ACC_offset) / ADC_TO_V * ACC_C;
}
}
Serial.print("Count 1 is: ");
Serial.println(count);
for (uint16_t q = 0; q < Samples_Remaining; q++){
ACC_G[count++] = -(i2s_data3[q] - ACC_offset) / ADC_TO_V * ACC_C;
}
Serial.println(" Processing Complete. ");
Serial.print(" The count is: ");
Serial.println(count);
double temp[count] = { 0 };
double a[3] = {0};
double b[3] = {0};
// Second order butterworth coefficients
// Fc = 2077.5 Hz
// Fs = 20000 Hz
a[0] = 0.071893;
a[1] = 0.143786;
a[2] = a[0];
b[1] = 1.111586;
b[2] = -0.399156;
// Set temp to ACC_G
for (int j = 0; j < count; j++){
temp[j] = ACC_G[j];
}
// First pass in forward direction
for (int i = 2; i <= (count - 1); i++){
ACC_G[i] = (a[0]*temp[i]) + (a[1]*temp[i-1]) + (a[2]*temp[i-2]) + (b[1]*ACC_G[i+1]) + (b[2]*ACC_G[i+2]);
}
// Set temp to intermediate ACC_G
for (int j = 0; j < count; j++){
temp[j] = ACC_G[j];
}
// Second pass in backward direction
for (int i = (count - 3); i >= 0; i--){
ACC_G[i] = (a[0]*temp[i]) + (a[1]*temp[i+1]) + (a[2]*temp[i+2]) + (b[1]*ACC_G[i+1]) + (b[2]*ACC_G[i+2]);
}
// Run the next task which processes the data and gets it sent to the computer
vTaskResume( JSON_T );
vTaskSuspend( NULL );
}
}
void setupI2S() {
Serial.println("Configuring I2S...");
esp_err_t err;
// The I2S config as per the example
const i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
.sample_rate = samplingFrequency,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // could only get it to work with 32bits
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, // although the SEL config should be left, it seems to transmit on right
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Interrupt level 1
.dma_buf_count = NUM_BUFS, // number of buffers
.dma_buf_len = SAMPLEBLOCK, // samples per buffer
.use_apll = false//,
//.tx_desc_auto_clear = false,
//.fixed_mclk = 0
};
err = adc_gpio_init(ADC_UNIT_1, ADC_CHANNEL_0); //step 1
if (err != ESP_OK) {
Serial.printf("Failed setting up adc channel: %d\n", err);
while (true);
}
err = adc1_config_width(ADC_WIDTH_BIT_12);
if (err != ESP_OK) {
Serial.printf("Failed setting up adc width: %d\n", err);
while (true);
}
err = adc1_config_channel_atten(ADC1_CHANNEL_0,ADC_ATTEN_DB_11);
if (err != ESP_OK) {
Serial.printf("Failed setting up adc channel attenuation: %d\n", err);
while (true);
}
err = i2s_driver_install(I2S_NUM_0, &i2s_config, NUM_BUFS, &i2s_queue); //step 2
if (err != ESP_OK) {
Serial.printf("Failed installing driver: %d\n", err);
while (true);
}
err = i2s_set_adc_mode(ADC_UNIT_1, ADC_INPUT);
if (err != ESP_OK) {
Serial.printf("Failed setting up adc mode: %d\n", err);
while (true);
}
i2s_adc_enable(I2S_NUM_0);
Serial.println("I2S driver installed.");
}
Who is online
Users browsing this forum: No registered users and 65 guests