Compare commits
14 Commits
24e352069f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce69c34ce0 | ||
|
|
83887c1b8e | ||
|
|
962eddbd40 | ||
| 55578b5636 | |||
| ace8ec01e8 | |||
|
|
ebd10751fa | ||
| 9dbc2dec58 | |||
| 54f325a4bb | |||
|
|
08dde0f633 | ||
|
|
2e867717f2 | ||
|
|
fddc52d53d | ||
| 1d5c542471 | |||
|
|
b9a5ec28e8 | ||
| 4e2a972a7b |
27
README.md
27
README.md
@@ -1,15 +1,18 @@
|
||||
# Geiger-interface - WIP
|
||||
#### TODO:
|
||||
- RadMon integration
|
||||
- ~~RadMon integration~~
|
||||
- Clean code
|
||||
- check MQTT reliability
|
||||
- OLED display support
|
||||
- Check MQTT reliability
|
||||
- ~~OLED display support~~
|
||||
- Radioactive@home integration (as a standalone sensor without Boinc client)
|
||||
- Fix saving params after DRD
|
||||
|
||||
### Hardware
|
||||
**RadiationD v1.1 (CAJOE)** it's a cheap(est) Geiger counter that you can found on China market. I recommend to order assembled ;) due lacks of documentations. Next one is ESP32. I like generic dev boards like **HW-394 - ESP32-WROOM-32** - they are cheap, have many gpio, now they have even USB-C ;) Let's go connect it together. My CAJOE arrived with DuPont jumper wire, but don't be confused: **VIN** on CAJOE is an signal output! After everything is connected, you can flash ESP with **Geiger-interface.ino**. On first boot, WiFiManager create access point named **ESP_xxxxxx**. After connect there will be captive portal (or under address http://192.168.4.1) where you can configure:
|
||||
- Wifi connection
|
||||
- MQTT server address
|
||||
- GMC accound and geiger ID
|
||||
- RadMon Username and Password
|
||||
|
||||
After **save** interface should connect to Wifi and send data :) If you provide wrong data you can enable captive portal by double reset of ESP.
|
||||
|
||||
@@ -43,7 +46,8 @@ reload configuration and voilà!
|
||||
<img src="assets/ha2.png" width="600">
|
||||
|
||||
### RadMon integration
|
||||
**TBD**
|
||||
- Create account on https://radmon.org/
|
||||
- Login, go to **Control Panel** -> fill form to add sensor
|
||||
|
||||
### J3x5 Tube specification
|
||||
| Property | J3x5 |
|
||||
@@ -64,10 +68,21 @@ About J305 Tube conversion factor, you can read intresting article on: https://i
|
||||
|
||||
### 3D Printed case
|
||||
|
||||
Files download here https://git.noop.re/drops/Geiger-interface/src/branch/main/stl - case_bottom_oled.stl have more height due a gold pins on oled display and esp32 board.
|
||||
If you have / order boards without gold pins and you use soldering wires to connect, you can print standard case.
|
||||
|
||||
<img src="assets/bottom1.png" width="600"><br>
|
||||
<img src="assets/case1.png" width="600">
|
||||
<img src="assets/case1.png" width="600"><br>
|
||||
<img src="assets/case2.png" width="600"><br>
|
||||
<img src="assets/photo4.jpg" width="600"><br>
|
||||
<img src="assets/photo1.jpg" width="600"><br>
|
||||
<img src="assets/photo3.jpg" width="600"><br>
|
||||
<img src="assets/photo5.jpg" width="600"><br>
|
||||
<img src="assets/photo6.jpg" width="600"><br>
|
||||
<img src="assets/photo7.jpg" width="600"><br>
|
||||
|
||||
### Resources
|
||||
- https://iot-devices.com.ua/
|
||||
- https://www.gmcmap.com/
|
||||
- https://radmon.org/
|
||||
- https://radmon.org/
|
||||
- http://radioactiveathome.org/
|
||||
BIN
assets/case2.png
Normal file
BIN
assets/case2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 710 KiB |
BIN
assets/photo5.jpg
Normal file
BIN
assets/photo5.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 259 KiB |
BIN
assets/photo6.jpg
Normal file
BIN
assets/photo6.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 324 KiB |
BIN
assets/photo7.jpg
Normal file
BIN
assets/photo7.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 314 KiB |
@@ -4,6 +4,10 @@
|
||||
#define JSON_CONFIG_FILE "/config.json"
|
||||
#define TUBE_NAME "J305"
|
||||
#define TUBE_FACTOR 0.00812
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define i2c_Address 0x3c
|
||||
#define OLED_RESET -1
|
||||
|
||||
#include <ESP_DoubleResetDetector.h>
|
||||
#include <LittleFS.h>
|
||||
@@ -12,6 +16,11 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <HTTPClient.h>
|
||||
#include "time.h"
|
||||
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_SH110X.h>
|
||||
|
||||
DoubleResetDetector* drd;
|
||||
|
||||
@@ -19,6 +28,8 @@ WiFiClient espClient; // connection for MQTT
|
||||
WiFiClient espClient2; // connection fot HTTP Client
|
||||
PubSubClient client(espClient);
|
||||
|
||||
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||
|
||||
// Initialize LittleFS
|
||||
void initLittleFS() {
|
||||
if (!LittleFS.begin(true)) {
|
||||
@@ -32,6 +43,8 @@ char mqttUser[50] = "";
|
||||
char mqttPass[50] = "";
|
||||
char gmcAccountID[50] = "";
|
||||
char gmcDeviceID[50] = "";
|
||||
char rmUser[50] = "";
|
||||
char rmPass[50] = "";
|
||||
bool shouldSaveConfig = false;
|
||||
|
||||
char buf[10];
|
||||
@@ -53,11 +66,17 @@ void ICACHE_RAM_ATTR handleInterrupt() {
|
||||
GeigerCounts++;
|
||||
}
|
||||
|
||||
const char* ntpServer = "europe.pool.ntp.org";
|
||||
const long gmtOffset_sec = 3600; // GMT +1 = 3600
|
||||
const int daylightOffset_sec = 3600;
|
||||
|
||||
String GMCMap_Request_payload = "http://www.GMCmap.com/log2.asp";
|
||||
String GMCMap_Request = "";
|
||||
String GMCMap_Data = "";
|
||||
String GMCMap = "NONE";
|
||||
|
||||
struct tm timeinfo;
|
||||
|
||||
void saveConfigFile()
|
||||
{
|
||||
Serial.println(F("Saving config"));
|
||||
@@ -67,6 +86,8 @@ void saveConfigFile()
|
||||
json["mqttPass"] = mqttPass;
|
||||
json["gmcAccountID"] = gmcAccountID;
|
||||
json["gmcDeviceID"] = gmcDeviceID;
|
||||
json["rmUser"] = rmUser;
|
||||
json["rmPass"] = rmPass;
|
||||
|
||||
File configFile = FileFS.open(JSON_CONFIG_FILE, "w");
|
||||
if (!configFile)
|
||||
@@ -110,7 +131,8 @@ bool loadConfigFile()
|
||||
strcpy(mqttPass, json["mqttPass"]);
|
||||
strcpy(gmcAccountID, json["gmcAccountID"]);
|
||||
strcpy(gmcDeviceID, json["gmcDeviceID"]);
|
||||
|
||||
strcpy(rmUser, json["rmUser"]);
|
||||
strcpy(rmPass, json["rmPass"]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -152,6 +174,103 @@ void reconnect() {
|
||||
}
|
||||
}
|
||||
|
||||
void lcdPrint(String x) {
|
||||
display.clearDisplay();
|
||||
display.setTextSize(1);
|
||||
display.setTextColor(SH110X_WHITE);
|
||||
display.setCursor(0, 10);
|
||||
display.println(x);
|
||||
display.display();
|
||||
}
|
||||
|
||||
void lcdUpdate() {
|
||||
display.clearDisplay();
|
||||
display.setTextSize(1);
|
||||
display.setTextColor(SH110X_WHITE);
|
||||
display.setCursor(0, 0);
|
||||
display.println(printLocalTime());
|
||||
display.println("uSv/h: " + String(buf2));
|
||||
display.println("CPM: " + String(buf));
|
||||
display.display();
|
||||
}
|
||||
|
||||
String printLocalTime()
|
||||
{
|
||||
//if(!getLocalTime(&timeinfo)){
|
||||
// Serial.println("Failed to obtain time");
|
||||
// return "Failed to obtain time";
|
||||
//}
|
||||
char buffer[80];
|
||||
//strftime(buffer, 80, "%H:%M:%S %d-%m-%Y ", &timeinfo);
|
||||
strftime(buffer, 80, "%H:%M <|> %d-%m-%Y ", &timeinfo);
|
||||
return String(buffer);
|
||||
}
|
||||
|
||||
void radmonUpload(int cpm) {
|
||||
const char *cmdFormat = "http://radmon.org/radmon.php?function=submit&user=%s&password=%s&value=%d&unit=CPM";
|
||||
char url[256];
|
||||
|
||||
HTTPClient http2;
|
||||
|
||||
// create the request URL
|
||||
sprintf(url, cmdFormat, rmUser, rmPass, cpm);
|
||||
|
||||
Serial.print("[HTTP] begin: ");
|
||||
Serial.println(url);
|
||||
|
||||
if (http2.begin(espClient2, url)) {
|
||||
Serial.print("[HTTP] GET...");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = http2.GET();
|
||||
|
||||
// HTTP header has been sent and server response header has been handled
|
||||
Serial.printf(" code: %d\n", httpCode);
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = http2.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Serial.printf("[HTTP] GET failed, error: %s\n", http2.errorToString(httpCode).c_str());
|
||||
}
|
||||
http2.end();
|
||||
}
|
||||
}
|
||||
|
||||
void gmcUpload() {
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
GMCMap_Request = GMCMap_Request_payload + "?AID=" + gmcAccountID + "&GID=" + gmcDeviceID + "&CPM=" + buf + "&ACPM=" + buf + "&uSV=" + buf2;
|
||||
Serial.println(GMCMap_Request);
|
||||
HTTPClient http;
|
||||
if (http.begin(espClient2, GMCMap_Request)) { // HTTP
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
int httpCode = http.GET();
|
||||
if (httpCode > 0) {
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
if (payload.indexOf("ERR0") > 0) GMCMap = "OK";
|
||||
if (payload.indexOf("ERR1") > 0) GMCMap = "Error! User is not found";
|
||||
if (payload.indexOf("ERR2") > 0) GMCMap = "Error! Geiger Counter is not found";
|
||||
if (payload.indexOf("Warning") > 0) GMCMap = "Warning! The Geiger Counter location changed, please confirm the location";
|
||||
Serial.println(GMCMap);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
GMCMap = http.errorToString(httpCode).c_str();
|
||||
}
|
||||
http.end();
|
||||
} else {
|
||||
Serial.println("[HTTP] Unable to connect");
|
||||
GMCMap = "Unable to connect";
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
|
||||
// it is a good practice to make sure your code sets wifi mode how you want it.
|
||||
@@ -161,6 +280,10 @@ void setup() {
|
||||
attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, FALLING );
|
||||
initLittleFS();
|
||||
|
||||
display.begin(i2c_Address, true);
|
||||
display.setContrast (0);
|
||||
lcdPrint("Geiger Interace");
|
||||
|
||||
drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
|
||||
bool forceConfig = false;
|
||||
if (drd->detectDoubleReset()) {
|
||||
@@ -175,6 +298,8 @@ void setup() {
|
||||
WiFiManagerParameter custom_mqttPass("mqttPass", "MQTT Password", mqttPass, 50);
|
||||
WiFiManagerParameter custom_gmcAccountID("gmcAccountID", "GMC MAP Account ID", gmcAccountID, 50);
|
||||
WiFiManagerParameter custom_gmcDeviceID("gmcDeviceID", "GMC MAP Device ID", gmcDeviceID, 50);
|
||||
WiFiManagerParameter custom_rmUser("rmUser", "RadMon User", rmUser, 50);
|
||||
WiFiManagerParameter custom_rmPass("rmPass", "RadMon Password", rmPass, 50);
|
||||
|
||||
|
||||
wm.addParameter(&custom_mqttServer);
|
||||
@@ -182,6 +307,8 @@ void setup() {
|
||||
wm.addParameter(&custom_mqttPass);
|
||||
wm.addParameter(&custom_gmcAccountID);
|
||||
wm.addParameter(&custom_gmcDeviceID);
|
||||
wm.addParameter(&custom_rmUser);
|
||||
wm.addParameter(&custom_rmPass);
|
||||
// wm.resetSettings();
|
||||
|
||||
bool res;
|
||||
@@ -205,18 +332,21 @@ void setup() {
|
||||
}
|
||||
}
|
||||
|
||||
//tutaj bez sensu
|
||||
strcpy(mqttServer, custom_mqttServer.getValue());
|
||||
strcpy(mqttUser, custom_mqttUser.getValue());
|
||||
strcpy(mqttPass, custom_mqttPass.getValue());
|
||||
strcpy(gmcAccountID, custom_gmcAccountID.getValue());
|
||||
strcpy(gmcDeviceID, custom_gmcDeviceID.getValue());
|
||||
strcpy(rmUser, custom_rmUser.getValue());
|
||||
strcpy(rmPass, custom_rmPass.getValue());
|
||||
Serial.println("The values in the file are: ");
|
||||
Serial.println("\tmqtt_server : " + String(mqttServer));
|
||||
Serial.println("\tmqtt_user : " + String(mqttUser));
|
||||
Serial.println("\tmqtt_pass : " + String(mqttPass));
|
||||
Serial.println("\tgmc_AccountID : " + String(gmcAccountID));
|
||||
Serial.println("\tgmc_DeviceID : " + String(gmcDeviceID));
|
||||
Serial.println("\trm_User : " + String(rmUser));
|
||||
Serial.println("\trm_Pass : " + String(rmPass));
|
||||
|
||||
if (shouldSaveConfig)
|
||||
{
|
||||
@@ -228,9 +358,13 @@ void setup() {
|
||||
|
||||
client.setServer(mqttServer, 1883);
|
||||
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||
printLocalTime();
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Double Reset Detection
|
||||
drd->loop();
|
||||
|
||||
// if MQTT server provided
|
||||
@@ -239,12 +373,6 @@ void loop() {
|
||||
reconnect();
|
||||
}
|
||||
|
||||
if(millis() >= time_now + 5000){
|
||||
time_now += 5000;
|
||||
client.publish("esp32/heartbeat", "PING");
|
||||
}
|
||||
|
||||
|
||||
if (millis() >= mold + 60000) {
|
||||
ltoa(GeigerCounts, buf, 10);
|
||||
dtostrf(GeigerCounts*TUBE_FACTOR, -4, 2, buf2);
|
||||
@@ -252,42 +380,37 @@ void loop() {
|
||||
client.publish("esp32/uSv", buf2);
|
||||
GeigerCounts = 0;
|
||||
mold += 60000;
|
||||
lcdUpdate();
|
||||
}
|
||||
client.loop();
|
||||
|
||||
//if(millis() >= time_now + 5000){
|
||||
// time_now += 5000;
|
||||
// client.publish("esp32/heartbeat", "PING");
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
// if GMC IDs provided
|
||||
if (String(gmcAccountID) != "") {
|
||||
currentMillis = millis();
|
||||
// Update time on LCD
|
||||
getLocalTime(&timeinfo);
|
||||
if (timeinfo.tm_sec == 0){
|
||||
lcdUpdate();
|
||||
}
|
||||
|
||||
// Sending HTTP requests in every 10 minutes
|
||||
currentMillis = millis();
|
||||
if (currentMillis - previousMillis_10m >= interval_10m) {
|
||||
previousMillis_10m = currentMillis;
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
GMCMap_Request = GMCMap_Request_payload + "?AID=" + gmcAccountID + "&GID=" + gmcDeviceID + "&CPM=" + buf + "&ACPM=" + buf + "&uSV=" + buf2;
|
||||
Serial.println(GMCMap_Request);
|
||||
HTTPClient http;
|
||||
if (http.begin(espClient2, GMCMap_Request)) { // HTTP
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
int httpCode = http.GET();
|
||||
if (httpCode > 0) {
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
if (payload.indexOf("ERR0") > 0) GMCMap = "OK";
|
||||
if (payload.indexOf("ERR1") > 0) GMCMap = "Error! User is not found";
|
||||
if (payload.indexOf("ERR2") > 0) GMCMap = "Error! Geiger Counter is not found";
|
||||
if (payload.indexOf("Warning") > 0) GMCMap = "Warning! The Geiger Counter location changed, please confirm the location";
|
||||
Serial.println(GMCMap);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
GMCMap = http.errorToString(httpCode).c_str();
|
||||
}
|
||||
http.end();
|
||||
} else {
|
||||
Serial.println("[HTTP] Unable to connect");
|
||||
GMCMap = "Unable to connect";
|
||||
|
||||
// if RadMon username provided
|
||||
if (String(rmUser) != "") {
|
||||
radmonUpload(atoi(buf));
|
||||
}
|
||||
|
||||
// if GMC IDs provided
|
||||
if (String(gmcAccountID) != "") {
|
||||
gmcUpload();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
stl/case_bottom_oled.stl
Normal file
BIN
stl/case_bottom_oled.stl
Normal file
Binary file not shown.
BIN
stl/case_lid_oled.stl
Normal file
BIN
stl/case_lid_oled.stl
Normal file
Binary file not shown.
Reference in New Issue
Block a user