cleanup and formating after PlatformIO migration

This commit is contained in:
Holger Fleischmann
2019-04-16 20:43:40 +02:00
parent 261411597b
commit 3bdbe5f8ef
5 changed files with 201 additions and 169 deletions

View File

@@ -8,7 +8,8 @@
const float STS6_CPM_PER_USPH = 875; const float STS6_CPM_PER_USPH = 875;
// Holds pulse counter history and performs calculations // Holds pulse counter history and performs calculations
class GeigerData { class GeigerData
{
public: public:
const uint16_t sampleCount; const uint16_t sampleCount;
const uint16_t sampleSeconds; const uint16_t sampleSeconds;
@@ -21,7 +22,6 @@ private:
uint16_t prev(uint16_t index); uint16_t prev(uint16_t index);
public: public:
GeigerData(uint16_t sampleCount, uint16_t sampleSeconds, GeigerData(uint16_t sampleCount, uint16_t sampleSeconds,
float cpm_per_uSph); float cpm_per_uSph);
virtual ~GeigerData(); virtual ~GeigerData();

View File

@@ -1,45 +1,53 @@
#include "GeigerData.h" #include "GeigerData.h"
GeigerData::GeigerData(uint16_t sampleCount, uint16_t sampleSeconds, GeigerData::GeigerData(uint16_t sampleCount, uint16_t sampleSeconds,
float cpm_per_uSph) : float cpm_per_uSph) : sampleCount(sampleCount), sampleSeconds(sampleSeconds), cpm_per_uSph(cpm_per_uSph), pulsesPerSample(new uint16_t[sampleCount])
sampleCount(sampleCount), sampleSeconds(sampleSeconds), cpm_per_uSph( {
cpm_per_uSph), pulsesPerSample(new uint16_t[sampleCount]) {
currentSample = 0; currentSample = 0;
for (int i = 0; i < sampleCount; i++) { for (int i = 0; i < sampleCount; i++)
{
pulsesPerSample[i] = 0; pulsesPerSample[i] = 0;
} }
} }
GeigerData::~GeigerData() { GeigerData::~GeigerData()
{
delete[] pulsesPerSample; delete[] pulsesPerSample;
} }
uint16_t GeigerData::next(uint16_t index) { uint16_t GeigerData::next(uint16_t index)
{
return index + 1 < sampleCount ? index + 1 : 0; return index + 1 < sampleCount ? index + 1 : 0;
} }
uint16_t GeigerData::prev(uint16_t index) { uint16_t GeigerData::prev(uint16_t index)
{
return index > 0 ? index - 1 : sampleCount - 1; return index > 0 ? index - 1 : sampleCount - 1;
} }
void GeigerData::addPulses(uint16_t pulses) { void GeigerData::addPulses(uint16_t pulses)
{
if (pulsesPerSample[currentSample] <= UINT16_MAX - pulses) if (pulsesPerSample[currentSample] <= UINT16_MAX - pulses)
pulsesPerSample[currentSample] += pulses; pulsesPerSample[currentSample] += pulses;
} }
void GeigerData::nextSample() { void GeigerData::nextSample()
{
currentSample = next(currentSample); currentSample = next(currentSample);
pulsesPerSample[currentSample] = 0; pulsesPerSample[currentSample] = 0;
} }
uint16_t GeigerData::getCurrentSample() { uint16_t GeigerData::getCurrentSample()
{
return currentSample; return currentSample;
} }
uint32_t GeigerData::getPreviousPulses(uint16_t offset, uint16_t samples) { uint32_t GeigerData::getPreviousPulses(uint16_t offset, uint16_t samples)
{
uint32_t pulses = 0; uint32_t pulses = 0;
uint16_t index = (currentSample + sampleCount - offset) % sampleCount; uint16_t index = (currentSample + sampleCount - offset) % sampleCount;
for (uint16_t i = 0; i < samples; i++) { for (uint16_t i = 0; i < samples; i++)
{
pulses += pulsesPerSample[index]; pulses += pulsesPerSample[index];
index = prev(index); index = prev(index);
} }
@@ -47,7 +55,8 @@ uint32_t GeigerData::getPreviousPulses(uint16_t offset, uint16_t samples) {
return pulses; return pulses;
} }
float GeigerData::toMicroSievertPerHour(uint32_t pulses, uint16_t samples) { float GeigerData::toMicroSievertPerHour(uint32_t pulses, uint16_t samples)
{
float cpm = pulses / (sampleSeconds / 60. * samples); float cpm = pulses / (sampleSeconds / 60. * samples);
return cpm / cpm_per_uSph; return cpm / cpm_per_uSph;
} }

View File

@@ -6,13 +6,15 @@
// on I2C GPIOs SCL 22 and SDA 21 // on I2C GPIOs SCL 22 and SDA 21
U8G2_SH1106_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, 22, 21); U8G2_SH1106_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, 22, 21);
void initDisplay() { void initDisplay()
{
// high I2c clock still results in about 100ms buffer transmission to OLED: // high I2c clock still results in about 100ms buffer transmission to OLED:
u8g2.setBusClock(1000000); u8g2.setBusClock(1000000);
u8g2.begin(); u8g2.begin();
} }
void renderDigits(char uSphStr[16], char cpmStr[16]) { void renderDigits(char uSphStr[16], char cpmStr[16])
{
uint16_t y = 14; uint16_t y = 14;
uint16_t xCpm = 56; uint16_t xCpm = 56;
uint16_t xUSph = 127; uint16_t xUSph = 127;
@@ -37,17 +39,18 @@ void renderDigits(char uSphStr[16], char cpmStr[16]) {
u8g2.print("cnt/min"); u8g2.print("cnt/min");
} }
void renderHistoryBarGraph(GeigerData &geigerData) { void renderHistoryBarGraph(GeigerData &geigerData)
{
const uint16_t bars = 120; const uint16_t bars = 120;
const uint16_t maxBarHeight = 40; const uint16_t maxBarHeight = 40;
const uint16_t samplesPerBar = geigerData.sampleCount / bars; const uint16_t samplesPerBar = geigerData.sampleCount / bars;
const uint16_t barsPerMinute = 60 const uint16_t barsPerMinute = 60 / (samplesPerBar * geigerData.sampleSeconds);
/ (samplesPerBar * geigerData.sampleSeconds);
// determine max value for y scale: // determine max value for y scale:
uint16_t offset = geigerData.getCurrentSample() % samplesPerBar + 1; uint16_t offset = geigerData.getCurrentSample() % samplesPerBar + 1;
uint32_t maxPulses = 0; uint32_t maxPulses = 0;
for (int16_t i = 0; i < bars - 1; i++) { for (int16_t i = 0; i < bars - 1; i++)
{
const uint32_t prevPulses = geigerData.getPreviousPulses(offset, const uint32_t prevPulses = geigerData.getPreviousPulses(offset,
samplesPerBar); samplesPerBar);
if (prevPulses > maxPulses) if (prevPulses > maxPulses)
@@ -58,12 +61,12 @@ void renderHistoryBarGraph(GeigerData &geigerData) {
const float maxUSph = geigerData.toMicroSievertPerHour(maxPulses, const float maxUSph = geigerData.toMicroSievertPerHour(maxPulses,
samplesPerBar); samplesPerBar);
const float uSphPerPixel = maxUSph > 40. ? 10. : maxUSph > 4. ? 1. : const float uSphPerPixel = maxUSph > 40. ? 10. : maxUSph > 4. ? 1. : maxUSph > 0.4 ? 0.1 : 0.01;
maxUSph > 0.4 ? 0.1 : 0.01;
// labels and grid // labels and grid
u8g2.setFont(u8g2_font_4x6_tn); u8g2.setFont(u8g2_font_4x6_tn);
char s[10]; char s[10];
for (uint16_t i = 10; i <= maxBarHeight; i += 10) { for (uint16_t i = 10; i <= maxBarHeight; i += 10)
{
u8g2.setCursor(0, 63 - i + 3); u8g2.setCursor(0, 63 - i + 3);
if (uSphPerPixel >= 0.1) if (uSphPerPixel >= 0.1)
sprintf(s, "%.0f", i * uSphPerPixel); sprintf(s, "%.0f", i * uSphPerPixel);
@@ -71,14 +74,16 @@ void renderHistoryBarGraph(GeigerData &geigerData) {
sprintf(s, ".%.0f", i * uSphPerPixel * 10); sprintf(s, ".%.0f", i * uSphPerPixel * 10);
u8g2.print(s); u8g2.print(s);
for (int16_t x = 127 - barsPerMinute; x >= 8; x -= barsPerMinute) { for (int16_t x = 127 - barsPerMinute; x >= 8; x -= barsPerMinute)
{
u8g2.drawPixel(x, 63 - i); u8g2.drawPixel(x, 63 - i);
} }
} }
// bars // bars
offset = geigerData.getCurrentSample() % samplesPerBar + 1; offset = geigerData.getCurrentSample() % samplesPerBar + 1;
for (int16_t i = 0; i < bars - 1; i++) { for (int16_t i = 0; i < bars - 1; i++)
{
const uint32_t prevPulses = geigerData.getPreviousPulses(offset, const uint32_t prevPulses = geigerData.getPreviousPulses(offset,
samplesPerBar); samplesPerBar);
const float uSph = geigerData.toMicroSievertPerHour(prevPulses, const float uSph = geigerData.toMicroSievertPerHour(prevPulses,
@@ -92,7 +97,8 @@ void renderHistoryBarGraph(GeigerData &geigerData) {
} }
} }
void updateDisplay(GeigerData &geigerData, char uSphStr[16], char cpmStr[16]) { void updateDisplay(GeigerData &geigerData, char uSphStr[16], char cpmStr[16])
{
u8g2.clearBuffer(); u8g2.clearBuffer();
renderDigits(uSphStr, cpmStr); renderDigits(uSphStr, cpmStr);
renderHistoryBarGraph(geigerData); renderHistoryBarGraph(geigerData);

View File

@@ -6,21 +6,27 @@
const char *thingsPeakUrl = "api.thingspeak.com"; const char *thingsPeakUrl = "api.thingspeak.com";
bool connect() { bool connect()
{
uint16_t retries = 3; uint16_t retries = 3;
while (WiFi.status() != WL_CONNECTED && (--retries) > 0) { while (WiFi.status() != WL_CONNECTED && (--retries) > 0)
{
Serial.print("Trying to connect to "); Serial.print("Trying to connect to ");
Serial.print(wifiSsid); Serial.print(wifiSsid);
Serial.print(" ... "); Serial.print(" ... ");
WiFi.begin(wifiSsid, wifiPassword); WiFi.begin(wifiSsid, wifiPassword);
uint16_t waitRemaining = 8; uint16_t waitRemaining = 8;
while (WiFi.status() != WL_CONNECTED && (--waitRemaining) > 0) { while (WiFi.status() != WL_CONNECTED && (--waitRemaining) > 0)
{
delay(500); delay(500);
} }
if (WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED)
{
Serial.println("successful"); Serial.println("successful");
return true; return true;
} else { }
else
{
Serial.print("failed status="); Serial.print("failed status=");
Serial.println(WiFi.status()); Serial.println(WiFi.status());
} }
@@ -29,39 +35,43 @@ bool connect() {
return WiFi.status() == WL_CONNECTED; return WiFi.status() == WL_CONNECTED;
} }
void initIngest() { void initIngest()
{
connect(); connect();
} }
void deinitIngest() { void deinitIngest()
if (WiFi.status() == WL_CONNECTED) { {
if (WiFi.status() == WL_CONNECTED)
{
Serial.println("Disconnecting WiFi"); Serial.println("Disconnecting WiFi");
WiFi.disconnect(true, true); WiFi.disconnect(true, true);
} }
} }
void ingest(GeigerData &geigerData, uint16_t intervalSamples) { void ingest(GeigerData &geigerData, uint16_t intervalSamples)
{
if (!connect()) if (!connect())
return; return;
WiFiClient client; WiFiClient client;
if (!client.connect(thingsPeakUrl, 80)) { if (!client.connect(thingsPeakUrl, 80))
{
Serial.print("Connecting to "); Serial.print("Connecting to ");
Serial.print(thingsPeakUrl); Serial.print(thingsPeakUrl);
Serial.println(" failed"); Serial.println(" failed");
} else { }
else
{
const uint32_t pulses = geigerData.getPreviousPulses(1, const uint32_t pulses = geigerData.getPreviousPulses(1,
intervalSamples); intervalSamples);
const uint32_t cpm = uint32_t( const uint32_t cpm = uint32_t(
pulses pulses / ((float)intervalSamples * geigerData.sampleSeconds / 60.) + 0.5);
/ ((float) intervalSamples * geigerData.sampleSeconds
/ 60.) + 0.5);
const float uSph = geigerData.toMicroSievertPerHour(pulses, const float uSph = geigerData.toMicroSievertPerHour(pulses,
intervalSamples); intervalSamples);
const String content = "api_key=" + String(thingspeakApiKey) const String content = "api_key=" + String(thingspeakApiKey) + "&field1=" + String(cpm) + "&field2=" + String(uSph, 3);
+ "&field1=" + String(cpm) + "&field2=" + String(uSph, 3);
Serial.print("Ingesting cpm="); Serial.print("Ingesting cpm=");
Serial.print(cpm); Serial.print(cpm);
@@ -85,15 +95,18 @@ void ingest(GeigerData &geigerData, uint16_t intervalSamples) {
client.print(content); client.print(content);
uint16_t timeout = 40; uint16_t timeout = 40;
while (client.available() == 0 && (--timeout) > 0) { while (client.available() == 0 && (--timeout) > 0)
{
delay(50); delay(50);
} }
if (client.available() == 0) { if (client.available() == 0)
{
Serial.println("failed (no response)"); Serial.println("failed (no response)");
} }
Serial.println("response:"); Serial.println("response:");
while (client.available()) { while (client.available())
{
char c = client.read(); char c = client.read();
Serial.write(c); Serial.write(c);
} }

View File

@@ -6,24 +6,6 @@
#include "ingest.h" #include "ingest.h"
#include "GeigerData.h" #include "GeigerData.h"
void setup() ;
void pulse() ;
uint32_t calcRemainingWait() ;
boolean wifiSwitchOn() ;
uint16_t takeSampleNoSleep() ;
uint16_t takeSampleLowPower() ;
void loop() ;
#include "Arduino.h"
#include "driver/pcnt.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "display.h"
#include "ingest.h"
#include "GeigerData.h"
// ~400µs high pulses from Geiger tube on GPIO 18 // ~400µs high pulses from Geiger tube on GPIO 18
#define PULSE_PIN 18 #define PULSE_PIN 18
#define PULSE_GPIO GPIO_NUM_18 #define PULSE_GPIO GPIO_NUM_18
@@ -51,7 +33,14 @@ uint32_t sampleStart = 0;
const int16_t ingestInterval = 60; const int16_t ingestInterval = 60;
int16_t ingestCountdown; int16_t ingestCountdown;
void setup() { void pulse();
uint32_t calcRemainingWait();
boolean wifiSwitchOn();
uint16_t takeSampleNoSleep();
uint16_t takeSampleLowPower();
void setup()
{
Serial.begin(921600); Serial.begin(921600);
Serial.println("Starting!"); Serial.println("Starting!");
@@ -67,7 +56,8 @@ void setup() {
// WiFi switch input // WiFi switch input
pinMode(WIFI_SWITCH_PIN, INPUT_PULLUP); pinMode(WIFI_SWITCH_PIN, INPUT_PULLUP);
if (wifiSwitchOn()) { if (wifiSwitchOn())
{
initIngest(); initIngest();
} }
@@ -76,97 +66,8 @@ void setup() {
ingestCountdown = ingestInterval; ingestCountdown = ingestInterval;
} }
// interrupt handler void loop()
void pulse() { {
++intPulseCount;
}
uint32_t calcRemainingWait() {
const uint32_t remaining = sampleMicros - (micros() - sampleStart);
return remaining > sampleMicros ? 0 : remaining;
}
boolean wifiSwitchOn() {
return digitalRead(WIFI_SWITCH_PIN) == 0;
}
uint16_t takeSampleNoSleep() {
attachInterrupt(PULSE_PIN, pulse, RISING);
int32_t remainingWait = calcRemainingWait();
delayMicroseconds(remainingWait);
sampleStart = micros();
noInterrupts();
const int16_t pulses = intPulseCount;
intPulseCount = 0;
interrupts();
return pulses;
}
uint16_t takeSampleLowPower() {
// To save battery power, use light sleep as much as possible.
// During light sleep, no counters or interrupts are working.
// Therefore simply wake up on each pulse signal change. This
// is fast enough for the low frequencies from a Geiger tube
// (below 2kHz):
// Wake up at end of sample period. Also
// wake up on pulse getting high and getting low.
// Waking up directly on rising/falling edges is not possible,
// so wait until level change.
// Switch to interrupt counting while awake for calculations
// and display update.
// stop interrupt (switch to active wakeup counting loop):
detachInterrupt(PULSE_PIN);
int32_t remainingWait = calcRemainingWait();
esp_sleep_wakeup_cause_t cause = ESP_SLEEP_WAKEUP_UNDEFINED;
while (cause != ESP_SLEEP_WAKEUP_TIMER && remainingWait > 0) {
if (digitalRead(PULSE_PIN)) {
// wait for low pulse start or sample time end
esp_sleep_enable_timer_wakeup(remainingWait);
gpio_wakeup_enable(PULSE_GPIO, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_light_sleep_start();
cause = esp_sleep_get_wakeup_cause();
}
remainingWait = calcRemainingWait();
if (cause != ESP_SLEEP_WAKEUP_TIMER && remainingWait > 0) {
// wait for high pulse start or sample time end
esp_sleep_enable_timer_wakeup(remainingWait);
gpio_wakeup_enable(PULSE_GPIO, GPIO_INTR_HIGH_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_light_sleep_start();
cause = esp_sleep_get_wakeup_cause();
if (cause == ESP_SLEEP_WAKEUP_GPIO) {
++pulseCount;
}
}
remainingWait = calcRemainingWait();
}
// take sample and add to statistics
sampleStart = micros();
const int16_t pulses = pulseCount + intPulseCount;
// Serial.print("pc=");
// Serial.print(pulseCount);
// Serial.print(" ipc=");
// Serial.println(intPulseCount);
attachInterrupt(PULSE_PIN, pulse, RISING);
interrupts();
// reset counters AFTER enabling interrupt to avoid double-counting on high signal
pulseCount = 0;
intPulseCount = 0;
return pulses;
}
void loop() {
// blinky // blinky
@@ -179,13 +80,17 @@ void loop() {
geigerData.addPulses(pulses); geigerData.addPulses(pulses);
geigerData.nextSample(); geigerData.nextSample();
if (wifiSwitchOn()) { if (wifiSwitchOn())
{
ingestCountdown--; ingestCountdown--;
if (ingestCountdown <= 0) { if (ingestCountdown <= 0)
{
ingestCountdown = ingestInterval; ingestCountdown = ingestInterval;
ingest(geigerData, ingestInterval); ingest(geigerData, ingestInterval);
} }
} else { }
else
{
deinitIngest(); deinitIngest();
} }
@@ -217,3 +122,102 @@ void loop() {
updateDisplay(geigerData, uSphStr, cpmStr); updateDisplay(geigerData, uSphStr, cpmStr);
} }
// interrupt handler
void pulse()
{
++intPulseCount;
}
uint32_t calcRemainingWait()
{
const uint32_t remaining = sampleMicros - (micros() - sampleStart);
return remaining > sampleMicros ? 0 : remaining;
}
boolean wifiSwitchOn()
{
return digitalRead(WIFI_SWITCH_PIN) == 0;
}
uint16_t takeSampleNoSleep()
{
attachInterrupt(PULSE_PIN, pulse, RISING);
int32_t remainingWait = calcRemainingWait();
delayMicroseconds(remainingWait);
sampleStart = micros();
noInterrupts();
const int16_t pulses = intPulseCount;
intPulseCount = 0;
interrupts();
return pulses;
}
uint16_t takeSampleLowPower()
{
// To save battery power, use light sleep as much as possible.
// During light sleep, no counters or interrupts are working.
// Therefore simply wake up on each pulse signal change. This
// is fast enough for the low frequencies from a Geiger tube
// (below 2kHz):
// Wake up at end of sample period. Also
// wake up on pulse getting high and getting low.
// Waking up directly on rising/falling edges is not possible,
// so wait until level change.
// Switch to interrupt counting while awake for calculations
// and display update.
// stop interrupt (switch to active wakeup counting loop):
detachInterrupt(PULSE_PIN);
int32_t remainingWait = calcRemainingWait();
esp_sleep_wakeup_cause_t cause = ESP_SLEEP_WAKEUP_UNDEFINED;
while (cause != ESP_SLEEP_WAKEUP_TIMER && remainingWait > 0)
{
if (digitalRead(PULSE_PIN))
{
// wait for low pulse start or sample time end
esp_sleep_enable_timer_wakeup(remainingWait);
gpio_wakeup_enable(PULSE_GPIO, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_light_sleep_start();
cause = esp_sleep_get_wakeup_cause();
}
remainingWait = calcRemainingWait();
if (cause != ESP_SLEEP_WAKEUP_TIMER && remainingWait > 0)
{
// wait for high pulse start or sample time end
esp_sleep_enable_timer_wakeup(remainingWait);
gpio_wakeup_enable(PULSE_GPIO, GPIO_INTR_HIGH_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_light_sleep_start();
cause = esp_sleep_get_wakeup_cause();
if (cause == ESP_SLEEP_WAKEUP_GPIO)
{
++pulseCount;
}
}
remainingWait = calcRemainingWait();
}
// take sample and add to statistics
sampleStart = micros();
const int16_t pulses = pulseCount + intPulseCount;
// Serial.print("pc=");
// Serial.print(pulseCount);
// Serial.print(" ipc=");
// Serial.println(intPulseCount);
attachInterrupt(PULSE_PIN, pulse, RISING);
interrupts();
// reset counters AFTER enabling interrupt to avoid double-counting on high signal
pulseCount = 0;
intPulseCount = 0;
return pulses;
}