#include #include #include #include #include #include #include #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; }*/ }