mirror of
https://github.com/grillbaer/esp32-geiger-counter.git
synced 2025-12-21 21:33:03 +01:00
added MQTT publishing support
This commit is contained in:
22
README.md
22
README.md
@@ -1,13 +1,15 @@
|
|||||||
[](https://travis-ci.org/grillbaer/esp32-geiger-counter)
|
[](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
|
||||||

|

|
||||||
- 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)
|
[](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
|
||||||
|
|
||||||
|
|||||||
@@ -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_ */
|
||||||
|
|||||||
@@ -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_ */
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
25
src/TEMPLATE_secret_credentials.h
Normal file
25
src/TEMPLATE_secret_credentials.h
Normal 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";
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user