added MQTT publishing support

This commit is contained in:
Holger Fleischmann
2020-11-14 14:44:22 +01:00
parent f3faddad05
commit ee81a2f49b
8 changed files with 134 additions and 19 deletions

View File

@@ -1,13 +1,15 @@
[![Build Status](https://travis-ci.org/grillbaer/esp32-geiger-counter.svg?branch=master)](https://travis-ci.org/grillbaer/esp32-geiger-counter) [![Build Status](https://travis-ci.org/grillbaer/esp32-geiger-counter.svg?branch=master)](https://travis-ci.org/grillbaer/esp32-geiger-counter)
# IoT Geiger Counter with ESP32, OLED Display and Thingspeak Channel # IoT Geiger Counter with ESP32, OLED Display, Thingspeak Channel and MQTT
- Measures radioactive gamma and beta radiation with quite good resolution at the typical low levels of the natural radiation (due to the big STS-6 tube) - Measures radioactive gamma and beta radiation with quite good resolution at the typical low levels of the natural radiation (due to the big STS-6 tube)
- Displays the current counts per minute (CPM), estimated dose equivalent rate in micro-Sievert per hour (µS/h) and 10 minutes history with 5 second resolution as bargraph - Displays the current counts per minute (CPM), estimated dose equivalent rate in micro-Sievert per hour (µS/h) and 10 minutes history with 5 second resolution as bargraph
![Circuit Board](media/geiger-counter-pcb.jpg) ![Circuit Board](media/geiger-counter-pcb.jpg)
- Low-power mode for use with batteries, OLED display and click sounds on, WiFi off - Low-power mode for use with batteries, OLED display and click sounds on, WiFi off
- WiFi mode for thingspeak data upload every minute, see https://thingspeak.com/channels/758223 - WiFi mode for
- optional thingspeak data upload every minute, see https://thingspeak.com/channels/758223
[![https://thingspeak.com/channels/758223](media/thingspeak.png)](https://thingspeak.com/channels/758223) [![https://thingspeak.com/channels/758223](media/thingspeak.png)](https://thingspeak.com/channels/758223)
- MQTT publishing to a broker every minute
Feel free to use this project as a base for your own projects AT YOUR OWN RISK! Feel free to use this project as a base for your own projects AT YOUR OWN RISK!
@@ -25,10 +27,22 @@ Feel free to use this project as a base for your own projects AT YOUR OWN RISK!
# Software # Software
- [PlatformIO VSCode project](https://github.com/platformio/platformio-vscode-ide) using [Arduino](https://github.com/arduino/Arduino) library, [Espressif ESP-IDF](https://github.com/espressif/esp-idf) for sleep functions and [U8g2](https://github.com/olikraus/u8g2) for display output - [PlatformIO VSCode project](https://github.com/platformio/platformio-vscode-ide) using [Arduino](https://github.com/arduino/Arduino) library, [Espressif ESP-IDF](https://github.com/espressif/esp-idf) for sleep functions, [U8g2](https://github.com/olikraus/u8g2) for display output and [Arduino-MQTT](https://github.com/256dpi/arduino-mqtt).
- Low-power mode uses light sleep, a wake-up for each signal pulse change and a wake-up every 1000 ms to update pulse statistics and OLED. This results in about 90% sleep. Could be improved using deep sleep and ULP. However, light sleep is already quite good and much easier. - Low-power mode uses light sleep, a wake-up for each signal pulse change and a wake-up every 1000 ms to update pulse statistics and OLED. This results in about 90% sleep. Could be improved using deep sleep and ULP. However, light sleep is already quite good and much easier.
- WiFi mode uses no sleep and simple interrupts for pulse counting. Pulse statistics and OLED are updated every 1000 ms, data is sent to thingspeak every 60 s. - WiFi mode uses no sleep and simple interrupts for pulse counting. Pulse statistics and OLED are updated every 1000 ms, data is sent to thingspeak every 60 s.
- Credentials (WiFi SSID, password, thingspeak channel key) are declared in `credentials.h` and replaced by dummy values in `credentials.cpp` by default. Define your real secrets in a sibling file named `secret_credentials.h` (which is never committed!). It will automatically be included by `credentials.cpp` if it exists. - Credentials, addresses and user for WiFi, thingspeak channel and mqtt broker are declared in `credentials.h` and replaced by dummy values in `credentials.cpp` by default. Define your real secrets in a sibling file named `secret_credentials.h` (do never committed!). It will automatically be included by `credentials.cpp` if it exists. You may copy `TEMPLATE_secret_credentials.h` as a starting point.
# MQTT Message Format
Published messages have the following JSON format:
```json
{
"pulses": 69,
"cpm": 69,
"uSph": 0.08,
"secs": 60
}
```
# Geiger Tube Pulse Forming # Geiger Tube Pulse Forming

View File

@@ -5,4 +5,10 @@ extern const char *wifiSsid;
extern const char *wifiPassword; extern const char *wifiPassword;
extern const char *thingspeakApiKey; extern const char *thingspeakApiKey;
extern const char *mqttHost;
extern int mqttPort;
extern const char *mqttUser;
extern const char *mqttPassword;
extern const char *mqttTopic;
#endif /* CREDENTIALS_H_ */ #endif /* CREDENTIALS_H_ */

View File

@@ -5,6 +5,7 @@
void initIngest(); void initIngest();
void deinitIngest(); void deinitIngest();
void ingest(GeigerData &geigerData, uint16_t intervalSamples); void ingestToThingspeak(GeigerData &geigerData, uint16_t intervalSamples);
void ingestToMqtt(GeigerData &geigerData, uint16_t intervalSamples);
#endif /* INGEST_H_ */ #endif /* INGEST_H_ */

View File

@@ -12,7 +12,7 @@
platform = espressif32 platform = espressif32
board = lolin32 board = lolin32
framework = arduino framework = arduino
monitor_speed = 115200 monitor_speed = 115200
lib_deps =
lib_deps = U8g2@2.26.1 U8g2@2.26.1
256dpi/MQTT@^2.4.7

View File

@@ -0,0 +1,25 @@
#define _SECRET_CREDENTIALS
// Copy this file to secret_credentials.h and adjust for
// your needs and environment.
// !!! NEVER COMMIT YOUR SECRETS !!!
const char *wifiSsid = "MyWiFi";
const char *wifiPassword = "mypassword";
// ThingsPeak Channel
// set key to NULL or empty string to disable ThingsPeak ingest:
const char *thingspeakApiKey = "MYAPIKEY";
// MQTT Broker
// set host to NULL or empty string to disable MQTT publishing:
const char *mqttHost = "my.mqtt.server";
int mqttPort = 1833;
const char *mqttUser = "user";
const char *mqttPassword = "mypassword";
const char *mqttTopic = "home/radioactivity";

View File

@@ -10,6 +10,12 @@
// use dummy credentials to make it compilable without secrets // use dummy credentials to make it compilable without secrets
const char *wifiSsid = "unset-wifi-ssid"; const char *wifiSsid = "unset-wifi-ssid";
const char *wifiPassword = "unset-password"; const char *wifiPassword = "unset-password";
const char *thingspeakApiKey = "unset-thinkspeak-api-key"; const char *thingspeakApiKey = ""; // empty to disable thinkspeak
const char *mqttHost = ""; // empty to disable MQTT
int mqttPort = 1833;
const char *mqttUser = "user";
const char *mqttPassword = "unset-password";
const char *mqttTopic = "home/geiger/radioactivity";
#endif #endif

View File

@@ -1,4 +1,5 @@
#include <WiFi.h> #include <WiFi.h>
#include <MQTT.h>
#include "GeigerData.h" #include "GeigerData.h"
@@ -6,10 +7,13 @@
const char *thingsPeakUrl = "api.thingspeak.com"; const char *thingsPeakUrl = "api.thingspeak.com";
bool connect() WiFiClient mqttWifiClient;
MQTTClient mqttClient;
bool connectWiFi()
{ {
uint16_t retries = 3; uint16_t retries = 10;
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);
@@ -35,7 +39,7 @@ bool connect()
return WiFi.status() == WL_CONNECTED; return WiFi.status() == WL_CONNECTED;
} }
void disconnect() void disconnectWiFi()
{ {
Serial.println("Disconnecting WiFi"); Serial.println("Disconnecting WiFi");
WiFi.disconnect(true, true); WiFi.disconnect(true, true);
@@ -43,20 +47,23 @@ void disconnect()
void initIngest() void initIngest()
{ {
connect(); connectWiFi();
} }
void deinitIngest() void deinitIngest()
{ {
if (WiFi.status() == WL_CONNECTED) if (WiFi.status() == WL_CONNECTED)
{ {
disconnect(); disconnectWiFi();
} }
} }
void ingest(GeigerData &geigerData, uint16_t intervalSamples) void ingestToThingspeak(GeigerData &geigerData, uint16_t intervalSamples)
{ {
if (!connect()) if (!connectWiFi())
return;
if (thingsPeakUrl == NULL || thingsPeakUrl[0] == 0)
return; return;
WiFiClient client; WiFiClient client;
@@ -66,7 +73,7 @@ void ingest(GeigerData &geigerData, uint16_t intervalSamples)
Serial.print(thingsPeakUrl); Serial.print(thingsPeakUrl);
Serial.println(" failed"); Serial.println(" failed");
// disconnect from WiFi to trigger fresh connect on next ingest // disconnect from WiFi to trigger fresh connect on next ingest
disconnect(); disconnectWiFi();
} }
else else
{ {
@@ -123,3 +130,58 @@ void ingest(GeigerData &geigerData, uint16_t intervalSamples)
client.stop(); client.stop();
} }
} }
bool connectMqtt()
{
if (!connectWiFi())
return false;
if (mqttHost == NULL || mqttHost[0] == 0)
return false;
if (!mqttClient.connected())
{
Serial.print("Connecting to MQTT host ");
Serial.print(mqttHost);
Serial.print(":");
Serial.print(mqttPort);
Serial.print(" user ");
Serial.print(mqttUser);
mqttClient.begin(mqttHost, mqttPort, mqttWifiClient);
if (mqttClient.connect("esp32-geiger-counter", mqttUser, mqttPassword))
{
Serial.println(" successful");
}
else
{
Serial.println(" failed");
// disconnect from WiFi to trigger fresh connect on next ingest
disconnectWiFi();
return false;
}
}
return true;
}
void disconnectMqtt()
{
mqttClient.disconnect();
}
void ingestToMqtt(GeigerData &geigerData, uint16_t intervalSamples)
{
if (connectMqtt())
{
const unsigned long pulses = geigerData.getPreviousPulses(1,
intervalSamples);
const unsigned long cpm = (unsigned long)(pulses / ((float)intervalSamples * geigerData.sampleSeconds / 60.) + 0.5);
const float uSph = geigerData.toMicroSievertPerHour(pulses,
intervalSamples);
char payload[129];
sprintf(payload, "{\"pulses\":%lu, \"cpm\":%lu,\"uSph\":%.2f,\"secs\":%d}",
pulses, cpm, uSph, (int)intervalSamples);
mqttClient.publish(mqttTopic, payload);
}
}

View File

@@ -97,7 +97,8 @@ void loop()
if (ingestCountdown <= 0) if (ingestCountdown <= 0)
{ {
ingestCountdown = ingestInterval; ingestCountdown = ingestInterval;
ingest(geigerData, ingestInterval); ingestToThingspeak(geigerData, ingestInterval);
ingestToMqtt(geigerData, ingestInterval);
} }
} }
else else