Here is the code running on the ESP32.
The Sample Battery task is the task I am trying to get working.
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.");
}