256 lines
6.2 KiB
C++
256 lines
6.2 KiB
C++
#include <Arduino.h>
|
|
|
|
#include <WiFi.h>
|
|
#include <WiFiMulti.h>
|
|
#include <HTTPClient.h>
|
|
|
|
#include <AutoAnalogAudio.h>
|
|
|
|
#include <ArduinoOTA.h>
|
|
|
|
#include <SPIFFS.h>
|
|
|
|
#include "Config.h"
|
|
#include "fft_config.h"
|
|
//#include "httpserver.h"
|
|
|
|
extern "C" {
|
|
#include "fft.h"
|
|
}
|
|
|
|
#define ENDLESS_LOOP() while(true) { delay(100); }
|
|
#define ARRAY_SIZE(x) ((size_t)(sizeof(x) / sizeof(x[0])))
|
|
|
|
#define OUTPUT_INTERVAL 10000 // milliseconds
|
|
|
|
#define SAMPLES_PER_BLOCK 512
|
|
|
|
static WiFiMulti wiFiMulti;
|
|
static bool wiFiConnectedToStation;
|
|
|
|
static AutoAnalog recorder;
|
|
|
|
// too large for the stack
|
|
static size_t timedomain_write_offset;
|
|
static value_type timedomain[BLOCK_LEN];
|
|
static value_type fft_re[BLOCK_LEN], fft_im[BLOCK_LEN];
|
|
|
|
const static uint16_t band_edges[] = {0, 40, 80, 120, 200, 400, 800, 1600, 3200, 6400, 12800, 20000};
|
|
static value_type total_energy[ARRAY_SIZE(band_edges) - 1];
|
|
|
|
void wifi_setup(void)
|
|
{
|
|
// Connect the WiFi network (or start an AP if that doesn't work)
|
|
for (auto &net : Config::instance().getWLANList())
|
|
{
|
|
Serial.print(F("Adding network "));
|
|
Serial.println(net.ssid.c_str());
|
|
wiFiMulti.addAP(net.ssid.c_str(), net.password.c_str());
|
|
}
|
|
|
|
Serial.println(F("Trying to connect for 5 Minutes..."));
|
|
|
|
WiFi.setHostname("loudness");
|
|
WiFi.mode(WIFI_STA);
|
|
//WiFi.setSleepMode(WIFI_MODEM_SLEEP);
|
|
|
|
wiFiConnectedToStation = true; // assume the best
|
|
|
|
bool led_on = true;
|
|
for(size_t tries = 0; tries < 3000; tries++)
|
|
{
|
|
if(wiFiMulti.run() == WL_CONNECTED) {
|
|
Serial.println(F(""));
|
|
Serial.println(F("WiFi connected"));
|
|
Serial.println(F("IP address: "));
|
|
Serial.println(WiFi.localIP());
|
|
break;
|
|
}
|
|
|
|
led_on = !led_on;
|
|
digitalWrite(LED_BUILTIN, led_on);
|
|
delay(100);
|
|
|
|
Serial.print(F("."));
|
|
}
|
|
|
|
if(WiFi.status() != WL_CONNECTED) {
|
|
Serial.println(F("Connection failed, setting up access point..."));
|
|
|
|
wiFiConnectedToStation = false;
|
|
|
|
IPAddress apIP(192, 168, 42, 1);
|
|
|
|
WiFi.mode(WIFI_AP);
|
|
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
|
WiFi.softAP("loudness", "dB(A)");
|
|
WiFi.enableAP(true);
|
|
|
|
digitalWrite(LED_BUILTIN, false); // LED ON (active low)
|
|
} else {
|
|
digitalWrite(LED_BUILTIN, true); // LED OFF (active low)
|
|
}
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
Serial.begin(115300);
|
|
|
|
pinMode(LED_BUILTIN, OUTPUT);
|
|
|
|
Serial.println(F("Hello World!"));
|
|
|
|
Serial.println(F("Initializing LittleFS…"));
|
|
|
|
if(!SPIFFS.begin()) {
|
|
Serial.println(F("LittleFS setup failed!"));
|
|
ENDLESS_LOOP();
|
|
}
|
|
|
|
Config::instance().load();
|
|
|
|
Serial.println(F("Initializing WiFi…"));
|
|
|
|
wifi_setup();
|
|
|
|
//Serial.println(F("Initializing HTTP Server…"));
|
|
|
|
//httpserver_setup();
|
|
|
|
//Serial.println(F("Testing display…"));
|
|
|
|
//epaper_test();
|
|
|
|
Serial.println(F("Initialization routine complete."));
|
|
|
|
init_fft();
|
|
|
|
timedomain_write_offset = 0;
|
|
memset(total_energy, 0, sizeof(total_energy));
|
|
|
|
// start sampling
|
|
recorder.enableAdcChannel(6);
|
|
recorder.begin(true, false);
|
|
recorder.autoAdjust = 0; //Disable auto adjust of timers
|
|
recorder.adcBitsPerSample = 12;
|
|
recorder.setSampleRate(48000, false);
|
|
|
|
recorder.getADC(SAMPLES_PER_BLOCK);
|
|
}
|
|
|
|
void loop() {
|
|
static uint32_t last_output_time = 0;
|
|
static uint32_t nsamples = 0;
|
|
|
|
wiFiMulti.run(); // maintain the WiFi connection
|
|
|
|
recorder.getADC(SAMPLES_PER_BLOCK);
|
|
|
|
// convert to Volt (float)
|
|
for(size_t i = 0; i < SAMPLES_PER_BLOCK; i++) {
|
|
timedomain[i + timedomain_write_offset] = (value_type)recorder.adcBuffer16[i] / 4096.0f * 3.30f;
|
|
}
|
|
timedomain_write_offset += SAMPLES_PER_BLOCK;
|
|
|
|
if(timedomain_write_offset >= ARRAY_SIZE(timedomain)) {
|
|
timedomain_write_offset = 0;
|
|
|
|
// calculate average value
|
|
value_type avg = 0;
|
|
for(size_t i = 0; i < ARRAY_SIZE(timedomain); i++) {
|
|
avg += timedomain[i];
|
|
}
|
|
|
|
avg /= ARRAY_SIZE(timedomain);
|
|
|
|
// and remove it from the data
|
|
for(size_t i = 0; i < ARRAY_SIZE(timedomain); i++) {
|
|
timedomain[i] -= avg;
|
|
}
|
|
|
|
apply_hanning(timedomain);
|
|
fft_transform(timedomain, fft_re, fft_im);
|
|
|
|
// HACK: reuse now unused timedomain memory for absolute FFT
|
|
value_type *fft_abs = timedomain;
|
|
|
|
complex_to_absolute(fft_re, fft_im, fft_abs);
|
|
|
|
// accumulate energy in bands
|
|
for(size_t i = 0; i < ARRAY_SIZE(total_energy); i++) {
|
|
total_energy[i] += get_spectral_density_in_band(fft_abs, band_edges[i], band_edges[i+1]);
|
|
}
|
|
|
|
// force restart sampling due to calculation delay (might cause glitches)
|
|
recorder.getADC(SAMPLES_PER_BLOCK);
|
|
timedomain_write_offset = 0;
|
|
|
|
nsamples++;
|
|
}
|
|
|
|
uint32_t now = millis();
|
|
if(now - last_output_time > OUTPUT_INTERVAL) {
|
|
String json_string = "{";
|
|
|
|
Serial.print("Samples taken: ");
|
|
Serial.println(nsamples);
|
|
|
|
for(size_t i = 0; i < ARRAY_SIZE(total_energy); i++) {
|
|
value_type dBV_per_sqrt_Hz = 20*log10f(total_energy[i] / nsamples);
|
|
|
|
uint16_t fstart = band_edges[i];
|
|
uint16_t fend = band_edges[i+1];
|
|
|
|
Serial.printf("%5d - %5d Hz: %.2f dBV/√Hz / %f Vrms\r\n", fstart, fend, dBV_per_sqrt_Hz, total_energy[i]);
|
|
|
|
if(i != 0) {
|
|
json_string += ", ";
|
|
}
|
|
|
|
// encode the data into JSON
|
|
char indexstr[5];
|
|
snprintf(indexstr, sizeof(indexstr), "%03zu_", i);
|
|
json_string += "\"" + String(indexstr) + String(fstart) + "_to_" + String(fend) + "_hz\": " + String(dBV_per_sqrt_Hz, 2);
|
|
}
|
|
|
|
json_string += "}";
|
|
|
|
// send the data to graphite
|
|
HTTPClient client;
|
|
client.begin("http://stats.tkolb.de/sensor/loudness.php");
|
|
client.addHeader("Content-Type", "application/json");
|
|
|
|
int httpResponseCode = client.POST(json_string);
|
|
|
|
if(httpResponseCode>0) {
|
|
Serial.print("HTTP request result: ");
|
|
Serial.println(httpResponseCode); //Print return code
|
|
|
|
//String response = http.getString(); //Get the response to the request
|
|
//Serial.println(response); //Print request answer
|
|
} else {
|
|
Serial.print("Error on sending POST: ");
|
|
Serial.println(httpResponseCode);
|
|
}
|
|
|
|
// reset accumulation
|
|
memset(total_energy, 0, sizeof(total_energy));
|
|
nsamples = 0;
|
|
|
|
// force restart sampling due to transmit delay (causes glitches)
|
|
recorder.getADC(SAMPLES_PER_BLOCK);
|
|
timedomain_write_offset = 0;
|
|
|
|
last_output_time = now;
|
|
}
|
|
|
|
//Serial.println(analogRead(A6));
|
|
|
|
/*unsigned long now = millis();
|
|
if((now - displayLastUpdateTime) >= DISPLAY_UPDATE_INTERVAL) {
|
|
Serial.println(F("Sending HTTP request…"));
|
|
|
|
displayLastUpdateTime = now;
|
|
}*/
|
|
}
|