Licznik Geigera

Przeglądając Aliexpress wpadł mi w oczy “licznik Geigera”. Ale bezsens, niepotrzebne, po co mi to…

Dwa tygodnie później miałem płytkę na biurku :slight_smile: Z lenistwa zamówiłem już złożony i z okablowaniem. Oczywiście bez jakiejkolwiek dokumentacji, ale chwila szukanie i poustawiałem na płytce jumpery. Po podłączeniu zasilania zaczęło pikać i błyskać, po położeniu BED (Bananowy ekwiwalent – Wikipedia, wolna encyklopedia) jakby pikało bardziej - chyba działa.

Nie jest to licznik promieniowania, bo nie ma nic w sobie z liczenia - to tylko zabawka która ma błyskać jak wykryje coś co emituje cząstki wysokoenergetyczne. Ot, w razie apokalipsy atomowej będę wiedział pierwszy. No i płytka ma od razu wyjście cyfrowe na którym pojawiają się impulsy po zliczeniu każdej cząstki. Miodzio! Aż się prosiło dołożenie…

Hardware

Coś małego i prostego, więc pierwsza myśl to Arduino. Ale miałem lepszy pomysł: ESP2866 a dokładniej gotowa płytka pod nazwą Wemos - no i ma WiFi. Płytka Geigera ma trzy piny wyjściowe: VCC, GND i VIN - to wbrew pozorom oznaczenie wyjścia. Podłączenie wprost do tych pinów okazało się problematyczne, bo płytka stałaby na “sztorc” i szkoda mi było miejsca. Okazało się, że rozstaw otworów i pinów zasilania całkiem nieźle pasuje w jednym miejscu do spodniej części płytki i wymaga to tylko podciągnięcia GND.

Nie wywaliło bezpieczników, więc czas na …

Software

Najpierw próbowałem z gotowcem, czyli Tasmotą, ale pod tym kątem jednak się nie sprawdziła: zliczanie impulsów używając Rules czy Skryptów to masakra. Stwierdziłem że napiszę coś samemu. Klik-klik i zadziałało. Kod nie jest piękny i na pewno da się zrobić lepiej, ale jest jak pływaczka NRD - ma działać a nie wyglądać (Geiger_pub — Codefile). Program realizuje na starcie następujące funkcje:

  • łączy się z WiFi
  • uruchamia serwer www do podglądu stanu urządzenia
  • łączy się z brookerem MQTT
  • pobiera aktualny czas z serwera NTP
  • zlicza impulsy wykorzystując mechanizm przerwań

Następnie prowadzi statystykę impulsów - ilość impulsów zliczonych w odstępach sekundowych, co 10 sekund i co minutę. Wyznacza również średnia ruchomą (średnia z określonej liczny pomiarów, najnowszy pomiar zastępuje najstarszy). Ponieważ zliczana ilość impulsów na minutę (CPM) jest wartością zależną od typu lampy, to program przelicza to na bardziej naukowe wartości - mikro-Siwerty na godzinę (uSv/h). Nie jest to przyrząd pomiarowy a raczej “szacownik” bez kalibracji, niemniej porównując wartości z danymi ze strony Polskiej Agencji Atomistyki wygląda bardzo podobnie. Na końcu z tych danych wyznacza zmierzoną dawkę dobową w milirentgenach (mR) - ot, aby zobaczyć czy jeszcze jest “not great, not terrible”. Niejako “przy okazji” wysyłam pomierzone dane na stronę Geiger Counter Map (https://www.gmcmap.com/) gdzie mogę sobie porównać z innymi licznikami.

A już dla czystej wygody dodałem też obsługuje OTA (Over-The-Air) co pozwala na przeprogramowanie układu przez WiFi, bez potrzeby podłączania kabla USB. Plus parę drobniejszych bajerów jak zapisywanie części pomiarów do EEPROM z rotacją adresów co rok dla uniknięcia limitu zapisów, uwzględnienie “szumu” lampy i takie tam. Całość napisana pod ArduinoIDE na bazie gotowców, no bo czemu by nie. Sumarycznie wyszło więc siermiężnie, ale za to interfejs ma brzydki :smiley:

Samo zliczanie jest nudne, więc dołóżmy trochę emocji i wyślijmy to w końcu do…

HomeAssistant

Program wysyła to do MQTT, a stąd już tylko krok do zrobienia ładnych wykresów. Pozostaje tylko poinstruować HA żeby słuchał czujnika. Dodałem do configuration.yaml kilka magicznych zaklęć:

mqtt:
  sensor:
    - name: "Wemos Geiger counts"
      state_topic: "my_geiger/counts"
      unit_of_measurement: "counts"
      icon: mdi:radioactive
    - name: "Wemos Geiger counts 1s"
      state_topic: "my_geiger/counts_1s"
      unit_of_measurement: "counts/s"
      icon: mdi:radioactive
    - name: "Wemos Geiger counts 10s"
      state_topic: "my_geiger/counts_10s"
      unit_of_measurement: "counts/10s"
      icon: mdi:radioactive
    - name: "Wemos Geiger CPM"
      state_topic: "my_geiger/counts_1m"
      unit_of_measurement: "counts/min"
      icon: mdi:radioactive
    - name: "Wemos Geiger counts 10s average"
      state_topic: "my_geiger/counts_10s_average"
      unit_of_measurement: "counts/10s"
      icon: mdi:radioactive   
      
    - name: "Wemos Geiger μSv/h "
      state_topic: "my_geiger/uSvh"
      unit_of_measurement: "μSv/h"
      icon: mdi:radioactive      
      value_template: '{{ value | multiply(0.001) | round(3) }}'      

    - name: "Wemos Geiger μSv/h average"
      state_topic: "my_geiger/uSvh_1m_average"
      unit_of_measurement: "μSv/h"
      icon: mdi:radioactive    
      value_template: '{{ value | multiply(0.001) | round(3) }}'
    - name: "Wemos Geiger mR/24h"
      state_topic: "my_geiger/mR_1d"
      unit_of_measurement: "mR"
      icon: mdi:radioactive     
      
    - name: "Wemos Geiger WiFi RSSI"
      state_topic: "my_geiger/rssi"
      unit_of_measurement: "dBm"
      icon: mdi:access-point        
    - name: "Wemos Geiger MQTT reconnect"
      state_topic: "my_geiger/mqtt_reconnect"
      unit_of_measurement: "counts"
      icon: mdi:counter  

Jedyne “oszustwo” to mnożnik przy μSv/h - Wemos wysyłał dane zaokrąglając do dwóch miejsc po przecinku, wiec najpierw w swoim programie mnożyłem to x1000, a potem pod HA dzieliłem przez 1000. Pewnie da się bardziej elegancko. Po restarcie pojawiają się nowe encje, a to już znaczy że można robić wykresy, wskaźniki, automatyzacje i wszystkie słodkości na jakie pozwala HA.



G1

Na koniec zapakowanie całości do pudełka hermetycznego, hermetyczny przepust kablowy, pochłaniacz wilgoci, licznik na zewnątrz i tylko czekać na powiadomienie “Alarm radioaktywny, przewidywany koniec świata za 3 minuty 50 sekund” :wink:

5 polubień

Tak mnie ciekawi dlaczego nie użyłeś gotowej funkcji Counter do zliczania impulsów?

Tutaj jest dyskusja o pomiarze impulsów w Tasmocie: Pulse count: per second, per seconds, per minute... · arendst/Tasmota · Discussion #18901 · GitHub. Generalnie się da, i nawet działało to całkiem-całkiem, ale zrezygnowałem z dwóch powodów: po pierwsze wymagało ekwilibrystyki przy pisaniu Rules i koszmarnie to wyglądało dla pomiarów sekundowych , po drugie: są dostępne tylko 3 Rules. Oczywiście da się to łączyć w większe łańcuchy, ale w końcu wygląd kodu przyprawiał o migrenę. Nieco lepiej było ze Smart Meter Interface - ale tu trzeba było robić własne firmware no i składnia poleceń typu “1,1-0:1.8.1*255(@1,T” skutecznie odbierał mi chęć do dalszych eksperymentów :slight_smile:

Wiadomo, pomiary sekundowe nie były konieczne, wystarczyły minutowe, impulsów nie spodziewam się tak dużo aby martwić się dokładnością, ale gdzie tylko się nie spojrzałem to piętrzyły się trudności które nie istniały przy obsłudze przerwań a’la Arduino.

Generalnie to konkretnie pod ten licznik Geigera-Müllera jest gotowy projekt dla ESPHome

1 polubienie

Tak, ale u siebie nie używam ESPHome i musiałbym to instalować tylko dla jednego urządzenia. Plus, mam mierzenie impulsów na sekundę, uśrednianie, upload do GMCmap itd. Odwieczny problem - czy używać gotowca, czy rzeźbić od początku :slight_smile:

Cześć, strona Codefile.io generuje błąd przy próbie skorzystania z Twojego linku, czy mógłbyś udostępnić kod gdzie indziej? Będę wdzięczny, bo nie chce mi się klepać samemu :grinning:

Jasne :slight_smile:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <ESP8266HTTPClient.h>

#include <ArduinoOTA.h>
#include <ESP8266mDNS.h>

#include <EEPROM.h>
unsigned long eeprom_err = 0;

int addr;
 String LocalIP="0.0.0.0";
 int httpCode=0;
 String payload="";

#ifndef STASSID
#define STASSID "xxxxxx"

#define STAPSK "xxxxxxx"
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

WiFiServer server(80);
WiFiClient espClient;
String Version = "1.1";
String GMCMap = "NONE";

WiFiUDP ntpUDP;
const long utcOffsetInSeconds = 3600;
NTPClient timeClient(ntpUDP, "xxxxx", utcOffsetInSeconds, 86400000);

#include "DHTesp.h"
DHTesp dht;
float humidity;
float temperature;

time_t epochTime = timeClient.getEpochTime();
struct tm *ptm = gmtime ((time_t *)&epochTime);
int monthDay = ptm->tm_mday;
int currentMonth = ptm->tm_mon + 1;
int currentYear = ptm->tm_year + 1900;

volatile  unsigned long GeigerCounts = 0;
volatile  bool mqtt_connected = false;;
byte state = HIGH;

void ICACHE_RAM_ATTR  handleInterrupt() {
  if (mqtt_connected==true) {GeigerCounts++;}
}

const byte interruptPin = 14; //wemos D5


String GMCMap_Request_payload = "http://www.GMCmap.com/log2.asp?AID=xxxxx&GID=xxxxx";
String GMCMap_Request = "";
String GMCMap_Data = "";

PubSubClient mqttClient(espClient);

#define mqtt_server "xxxxxxx"
#define mqtt_user "xxxxxxx"
#define mqtt_password "xxxxxxxx"

#define counts_topic "my_geiger/counts"
#define counts_1s_topic "my_geiger/counts_1s"
#define counts_10s_topic "my_geiger/counts_10s"
#define counts_10s_average_topic "my_geiger/counts_10s_average"
#define counts_1m_topic "my_geiger/counts_1m"
#define uSvh_topic "my_geiger/uSvh"
#define uSvh_average_topic "my_geiger/uSvh_1m_average"
#define mR_1d_topic "my_geiger/mR_1d"
#define rssi_topic "my_geiger/rssi"
#define mqtt_reconnect_topic "my_geiger/mqtt_reconnect"
#define geiger_humidity_topic  "my_geiger/humidity"
#define geiger_temperature_topic "my_geiger/temperature"
#define geiger_ip_topic "my_geiger/ip"

#define TUBE_NAME "J305"

 #define TUBE_FACTOR 0.00812
 const byte GeigerTubeNoise = 12;



unsigned long currentMillis = 0;


int rssi = 0;
unsigned long mqtt_reconnect = 0;

unsigned long previousMillis_1s = 0;
const int  interval_1s = 1000;
long GeigerCounts_1sec = 0;
unsigned int previousGeigerCounts_1sec = 0;

unsigned long previousMillis_10s = 0;
const int  interval_10s = 10000;
long GeigerCounts_10sec = 0;
unsigned long previousGeigerCounts_10sec = 0;

unsigned long previousMillis_60s = 0;
const long interval_60s = 60000;
long GeigerCounts_60sec = 0;
unsigned long previousGeigerCounts_60sec = 0;

unsigned long previousMillis_10m = 0;
const long interval_10m = 600000;
long GeigerCounts_10m = 0;
unsigned long previousGeigerCounts_10m = 0;

unsigned long previousMillis_1h = 0;
const long interval_1h = 3600000;
long GeigerCounts_1h = 0;
unsigned long previousGeigerCounts_1h = 0;

unsigned long previousMillis_1d = 0;
const long interval_1d = 86400000;

float uSvh = 0;
int CPM_1m;
bool FirstRun10s = true, FirstRun10m = true, FirstRunRoentgen = true;

const byte Geiger10sAverage_number = 10;
float Geiger10sAverage_array[Geiger10sAverage_number];
byte Geiger10sAverage_position = 0;
int x = 0, Geiger10sAverage_cumulative = 0, Geiger10sAverage = 0;

const byte CPM1mAverage_number = 10;
float CPM1mAverage_array[CPM1mAverage_number];
byte CPM1mAverage_position = 0;
int CPM1mAverage_cumulative = 0;
float CPM1mAverage = 0;

const byte Geiger1mAverage_number = 10;
float Geiger1mAverage_array[Geiger1mAverage_number];
byte Geiger1mAverage_position = 0;
int Geiger1mAverage_cumulative = 0, Geiger1mAverage = 0;

const byte Roentgen1dAverage_number = 24;
unsigned int Roentgen1dAverage_array[Roentgen1dAverage_number];
byte Roentgen1dAverage_position = 0;
float Roentgen1dAverage_cumulative = 0, Roentgen1dAverage = 0;

unsigned long maxCPS = 0, maxCP10S = 0, maxCPM = 0, maxuSV = 0;

String SPIFFSstatus;

void setup() {
yield();
ESP.wdtDisable();    
*((volatile uint32_t*) 0x60000900) &= ~(1); // Hardware WDT OFF
  
 pinMode(12, OUTPUT);
 digitalWrite(12, LOW);
 pinMode(13, INPUT_PULLUP);
dht.setup(13, DHTesp::AM2302);
delay(dht.getMinimumSamplingPeriod());
humidity = dht.getHumidity();
temperature = dht.getTemperature();

  timeClient.begin();
  for (x = 0; x < Geiger10sAverage_number; x++) {
    Geiger10sAverage_array[x] = 0;
  }
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, state);
  randomSeed(analogRead(0));
  Serial.begin(115200);


    
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
    digitalWrite(LED_BUILTIN, !state);
    state = !state;
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  rssi = WiFi.RSSI();
  Serial.print(WiFi.localIP()); Serial.print(" RSSI:"); Serial.print(rssi);
  Serial.print(" ["); Serial.print(dBmtoPercentage(rssi)); Serial.println("%]");
  Serial.println(F("EEPROM init"));
  EEPROM.begin(4000);

    Serial.print("{\"humidity\": ");
    Serial.print(humidity);
    Serial.print(", \"temp\": ");
    Serial.print(temperature);
    Serial.print("}\n");

  server.begin();
  Serial.println(F("WWW server started"));

  mqttClient.setServer(mqtt_server, 1883);

  Serial.print(F("NTP time: "));
  timeClient.update();
  Serial.println(timeClient.getFormattedTime());

  epochTime = timeClient.getEpochTime();
  tm *ptm = gmtime ((time_t *)&epochTime);
  monthDay = ptm->tm_mday;
  currentMonth = ptm->tm_mon + 1;
  currentYear = ptm->tm_year + 1900;

  pinMode( interruptPin, INPUT );
  attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, FALLING   );


  ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("OTA started");



  if (currentYear < 2023) {
    currentYear = 2023;
  };
  for (x = 0; x < Roentgen1dAverage_number; x++) {
    addr = (currentYear - 2023) * sizeof(Roentgen1dAverage_array) + x * sizeof(Roentgen1dAverage_array[x]);
    EEPROM.get(addr, Roentgen1dAverage_array[x]);
    Serial.print(" x:"); Serial.print(x); Serial.print(" address:"); Serial.print(addr); Serial.print(":"); Serial.print(Roentgen1dAverage_array[x]);
  }
Roentgen1dAverage_array[Roentgen1dAverage_position]=0;


}

void loop() {
  WiFiClient client = server.accept();
  ArduinoOTA.handle();

  if (!mqttClient.connected()) {
    reconnect();
  }

  mqttClient.loop();

  digitalWrite(LED_BUILTIN, state);

  currentMillis = millis();
  if (currentMillis - previousMillis_1s >= interval_1s) {
    GeigerCounts_1sec = GeigerCounts - previousGeigerCounts_1sec;
    previousMillis_1s = currentMillis;
    previousGeigerCounts_1sec = GeigerCounts;
  //  mqttClient.publish(counts_1s_topic, String(GeigerCounts_1sec).c_str(), true);
  //  Serial.print("["); Serial.print(timeClient.getFormattedTime()); Serial.print("] @1sec:"); Serial.println(GeigerCounts_1sec);
    digitalWrite(LED_BUILTIN, state);
    state = !state;

if (GeigerCounts_1sec > maxCPS) maxCPS = GeigerCounts_1sec;


    
  }

  currentMillis = millis();
  if (currentMillis - previousMillis_10s >= interval_10s) {
    GeigerCounts_10sec = GeigerCounts - previousGeigerCounts_10sec;
    previousMillis_10s = currentMillis;
    previousGeigerCounts_10sec = GeigerCounts;

    Geiger10sAverage_position++;
    if (Geiger10sAverage_position == Geiger10sAverage_number) Geiger10sAverage_position = 0;

    if (FirstRun10s == true) {
      for (x = 0; x < Geiger10sAverage_number; x++) Geiger10sAverage_array[x] = GeigerCounts_10sec;
      FirstRun10s = false;
    }

    Geiger10sAverage_array[Geiger10sAverage_position] = GeigerCounts_10sec;

    for (x = 0; x < Geiger10sAverage_number; x++) {
      Geiger10sAverage_cumulative = Geiger10sAverage_cumulative + Geiger10sAverage_array[x];
    }
    Geiger10sAverage = Geiger10sAverage_cumulative / Geiger10sAverage_number;
    Geiger10sAverage_cumulative = 0;

    if (GeigerCounts_10sec > maxCP10S) maxCP10S = GeigerCounts_10sec;

    mqttClient.publish(counts_10s_topic, String(GeigerCounts_10sec).c_str(), true);
    mqttClient.publish(counts_10s_average_topic, String(Geiger10sAverage).c_str(), true);
    mqttClient.publish(counts_topic, String(GeigerCounts).c_str(), true);

    Serial.print("["); Serial.print(timeClient.getFormattedTime()); Serial.print("] @10sec:"); Serial.println(GeigerCounts_10sec);
    Serial.print("["); Serial.print(timeClient.getFormattedTime()); Serial.print("] Running average@10sec:"); Serial.println(Geiger10sAverage);
  }

  currentMillis = millis();
  if (currentMillis - previousMillis_60s >= interval_60s) {
    GeigerCounts_60sec = GeigerCounts - previousGeigerCounts_60sec;
    previousMillis_60s = currentMillis;
    previousGeigerCounts_60sec = GeigerCounts;
    if ((GeigerCounts_60sec - GeigerTubeNoise) > 0 )
    {
      uSvh = (GeigerCounts_60sec - GeigerTubeNoise) * TUBE_FACTOR * 1000.0;
      CPM_1m = (GeigerCounts_60sec - GeigerTubeNoise);
    }
    else
    {
      uSvh = 0;
      CPM_1m = 0;
    }

    if (FirstRun10m == true) {
      for (x = 0; x < Geiger1mAverage_number; x++) Geiger1mAverage_array[x] = uSvh;
      for (x = 0; x < CPM1mAverage_number; x++) CPM1mAverage_array[x] = CPM_1m;
      FirstRun10m = false;
    }

    Geiger1mAverage_position++;
    CPM1mAverage_position++;
    if (Geiger1mAverage_position == Geiger1mAverage_number) {
      Geiger1mAverage_position = 0;
      CPM1mAverage_position = 0;
    }

    Geiger1mAverage_array[Geiger1mAverage_position] = uSvh;
    CPM1mAverage_array[CPM1mAverage_position] = CPM_1m;

    for (x = 0; x < Geiger1mAverage_number; x++) {
      Geiger1mAverage_cumulative = Geiger1mAverage_cumulative + Geiger1mAverage_array[x];
    }
    Geiger1mAverage = Geiger1mAverage_cumulative / Geiger1mAverage_number;
    Geiger1mAverage_cumulative = 0;


    for (x = 0; x < CPM1mAverage_number; x++) {
      CPM1mAverage_cumulative = CPM1mAverage_cumulative + CPM1mAverage_array[x];
    }
    CPM1mAverage = float(1.0 * CPM1mAverage_cumulative / CPM1mAverage_number);
    CPM1mAverage_cumulative = 0;

if (GeigerCounts_60sec > maxCPM) maxCPM = GeigerCounts_60sec;
if (uSvh > maxuSV) maxuSV = uSvh;
    
    Serial.println("");

    mqttClient.publish(counts_1m_topic, String(GeigerCounts_60sec).c_str(), true);
    mqttClient.publish(uSvh_topic, String(uSvh).c_str(), true);
    mqttClient.publish(uSvh_average_topic, String(Geiger1mAverage).c_str(), true);
    LocalIP = String() + WiFi.localIP()[0] + "." + WiFi.localIP()[1] + "." + WiFi.localIP()[2] + "." + WiFi.localIP()[3];
    mqttClient.publish(geiger_ip_topic, LocalIP.c_str(), true);



    Serial.print("["); Serial.print(timeClient.getFormattedTime()); Serial.print("] counts:"); Serial.println(GeigerCounts);
    Serial.print("["); Serial.print(timeClient.getFormattedTime()); Serial.print("] CPM:"); Serial.println(GeigerCounts_60sec);
    Serial.print("["); Serial.print(timeClient.getFormattedTime()); Serial.print("] uSv/h:"); Serial.print(uSvh / 1000.0); Serial.print(", uSv/h average:"); Serial.println(Geiger1mAverage / 1000.0);



    if (currentYear < 2023) {
      currentYear = 2023;
    };
    for (x = 0; x < Roentgen1dAverage_number; x++) {
  //    addr = (currentYear - 2023) * sizeof(Roentgen1dAverage_array) + x * sizeof(Roentgen1dAverage_array[x]);
   //   EEPROM.get(addr, Roentgen1dAverage_array[x]);
      Serial.print(", "); Serial.print(Roentgen1dAverage_array[x]);
    }

  }

  currentMillis = millis();
  if (currentMillis - previousMillis_10m >= interval_10m) {
    previousMillis_10m = currentMillis;

humidity = dht.getHumidity();
temperature = dht.getTemperature();

    mqttClient.publish(geiger_humidity_topic, String(humidity, 1).c_str(), true);
    mqttClient.publish(geiger_temperature_topic, String(temperature, 1).c_str(), true);
    
    Serial.print("[HTTP] begin...\n");
    GMCMap_Request = GMCMap_Request_payload + "&CPM=" + String(int(CPM1mAverage)).c_str() + "&ACPM=" +  String(CPM1mAverage / 1.0, 1).c_str() +  "&uSV=" + String(Geiger1mAverage / 1000.0, 3).c_str();
    Serial.println(GMCMap_Request);

    HTTPClient http;
    if (http.begin(espClient, GMCMap_Request)) {  // HTTP
      Serial.print("[HTTP] GET...\n");
      httpCode = http.GET();
      if (httpCode > 0) {
        Serial.printf("[HTTP] GET... code: %d\n", httpCode);
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          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";
    }

  

      Roentgen1dAverage_array[Roentgen1dAverage_position] = Roentgen1dAverage_array[Roentgen1dAverage_position] + Geiger1mAverage;

  }

  currentMillis = millis();
  if (currentMillis - previousMillis_1h >= interval_1h) {
    previousMillis_1h = currentMillis;



    
    if (currentYear < 2023) {
      currentYear = 2023;
    };

    addr = (currentYear - 2023) * sizeof(Roentgen1dAverage_array) + Roentgen1dAverage_position * sizeof(Roentgen1dAverage_array[x]);
    EEPROM.put(addr, Roentgen1dAverage_array[Roentgen1dAverage_position] );

    Serial.print("EEPROM "); Serial.print(addr); Serial.print(":");  Serial.println(Roentgen1dAverage_array[Roentgen1dAverage_position] );
    eeprom_commit();




    Roentgen1dAverage_cumulative = 0;
    for (x = 0; x < Roentgen1dAverage_number; x++) {
      Roentgen1dAverage_cumulative = Roentgen1dAverage_cumulative + Roentgen1dAverage_array[x] / 1000.0 / 100.0;
    }
    
    Roentgen1dAverage_position++;
    if (Roentgen1dAverage_position == Roentgen1dAverage_number) Roentgen1dAverage_position = 0;

    Roentgen1dAverage_array[Roentgen1dAverage_position] = 0;    
    
    rssi = WiFi.RSSI();
    mqttClient.publish(mqtt_reconnect_topic, String(mqtt_reconnect).c_str(), true);
    mqttClient.publish(rssi_topic, String(WiFi.RSSI()).c_str(), true);
    mqttClient.publish(mR_1d_topic, String(Roentgen1dAverage_cumulative / 1.0, 3).c_str(), true);

    Serial.print("["); Serial.print(timeClient.getFormattedTime()); Serial.print("] MQTT reconnect:"); Serial.println(mqtt_reconnect);
    Serial.print("["); Serial.print(timeClient.getFormattedTime()); Serial.print("] RSSI:"); Serial.print(rssi);
    Serial.print(" ["); Serial.print(dBmtoPercentage(rssi)); Serial.println("%]");
    timeClient.update();

  }


  currentMillis = millis();
  if (currentMillis - previousMillis_1d >= interval_1d) {
    previousMillis_1d = currentMillis;
 //   maxCPS = 0; maxCP10S = 0; maxCPM = 0; maxuSV = 0;
  }  


  client.print(F("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n"\
  "<head> <title>Geiger counter v."));
  client.print(Version);
  client.print(F("</title><meta name=\"description\" content=\"Geiger\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"\
  "<style>html {font-family: Arial;display: inline-block;}</style></head><body>"));

  currentMillis = millis();

  unsigned long Seconds = currentMillis / 1000;
  unsigned long Minutes = Seconds / 60;
  unsigned long Hours = Minutes / 60;
  unsigned long Days = Hours / 24;
  currentMillis %= 1000;
  Seconds %= 60;
  Minutes %= 60;
  Hours %= 24;

//float bulb = log((humidity/100)*exp((b-temperature/d)*(temperature/(c+temperature))));
//float dewpoint = c*bulb/(b-bulb);




  client.print(F("<h2><b>Geiger counter v.</b>"));

  client.print(Version);
  
       client.print(" (compilation: ");client.print(__DATE__);
     client.print(" ");client.print(__TIME__);
     client.print(")");
  client.print(F("</h2><br><div><table align=\"left\" border=\"0\" cellpadding=\"1\" cellspacing=\"1\">  <tbody>    <tr>      <td style=\"text-align:right\">uptime:&nbsp;</td>      <td>"));
  client.print(Days); client.print("d:"); client.print(Hours); client.print("h:"); client.print(Minutes); client.print("m:"); client.print(Seconds); client.print("s");
  client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">WiFi RSSI:&nbsp;</td>      <td>"));
  client.print(rssi); client.print("dBm ["); client.print(dBmtoPercentage(rssi)); client.println("%] "); client.println(LocalIP); //client.println(")"); 
  client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">MQTT reconnect:&nbsp;</td>      <td>"));
  client.print(mqtt_reconnect);

  client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">EEPROM errors:&nbsp;</td>      <td>"));
  client.print(eeprom_err);
  
  client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">time:&nbsp;</td>      <td>"));
client.print(timeClient.getFormattedTime()); 
//client.print(" "); client.print(monthDay); client.print("-"); client.print(currentMonth); client.print("-"); client.print(currentYear);


  client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">Humidity:&nbsp;</td>      <td>"));
client.print(humidity,1); client.print("%RH ("); client.print(dht.computeAbsoluteHumidity(temperature,humidity));client.print("g/m&sup3;)"); 


  client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">Temperature:&nbsp;</td>      <td>"));
client.print(temperature,1); client.print("&deg;C");

  client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">Dew point:&nbsp;</td>      <td>"));
//client.print(dewpoint,1);client.print("|"); 
client.print(dht.computeDewPoint(temperature,humidity),1); client.print("&deg;C");



  client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">&nbsp;</td>      <td>&nbsp;"));

    client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">tube:&nbsp;</td>      <td>"));
client.print(TUBE_NAME);

    client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">noise:&nbsp;</td>      <td>"));
client.print(String(GeigerTubeNoise)); 

    client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">counts:&nbsp;</td>      <td>"));
client.print(GeigerCounts);

  client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">&nbsp;</td>      <td>&nbsp;"));

//    client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><b>counts/s: </b>&nbsp;</td>      <td>"));
//client.print(GeigerCounts_1sec);

    client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><b>counts/10s: </b>&nbsp;</td>      <td>"));
client.print(GeigerCounts_10sec);

    client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><b>counts/10s avg: </b>&nbsp;</td>      <td>"));
client.print(Geiger10sAverage);

    client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><b>CPM: </b>&nbsp;</td>      <td>"));
client.print(GeigerCounts_60sec);

 //   client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><b>MAX: &nbsp;</td>      <td>"));
//client.print(F("CPS:</b>")); client.print(maxCPS);client.print(F(" <b>CP10S:</b>"));  client.print(maxCP10S);client.print(F("<b> CPM:</b>"));  client.print(maxCPM);client.print(F("<b> &#181;Sv/h:</b>"));  client.print(String(maxuSV/1000.0,3).c_str());
    
 client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\">&nbsp;</td>      <td>&nbsp;"));

 client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><b><b>&#181;Sv/h avg: </b>&nbsp;</td>      <td>"));
client.print(Geiger1mAverage / 1000.0, 4);

client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><b>&#181;Sv/h: </b>&nbsp;</td>      <td>"));
client.print(uSvh / 1000.0, 4);

client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><b>mR/day: </b>&nbsp;</td>      <td>"));
client.print(Roentgen1dAverage_cumulative, 3);

client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><b>GMC Map: </b>&nbsp;</td>      <td>"));
 client.print(GMCMap);

if ( GMCMap != "OK" ) {
  client.print(" [HTTP:");
  client.print(httpCode);
  client.print("| PAYLOAD:");
  client.print(payload);
  
  client.print("] ");
  };


 
  client.print(" (CPM:"); client.print(String(int(CPM1mAverage)).c_str()); client.print(" ACPM:"); client.print(String(CPM1mAverage / 1.0, 1).c_str()); client.print(" uSV:"); client.print(String(Geiger1mAverage / 1000.0, 3).c_str()); client.print(")<br>");

 client.print(F("</td>    </tr>    <tr>      <td style=\"text-align:right\"><br><button onClick='window.location.reload();'>Refresh</button></td>      <td>&nbsp;"));
    
  client.print(F("  </td>    </tr>  </tbody></table></div><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>"));
  
  
//  uptime: ")); client.print(Days); client.print("d:"); client.print(Hours); client.print("h:"); client.print(Minutes); client.print("m:"); client.print(Seconds); client.print("s");

//  client.print(F("<br>WiFi RSSI: "));  client.print(rssi); client.print("dBm ["); client.print(dBmtoPercentage(rssi)); client.println("%]");
//  client.print(F("<br>MQTT reconnect: "));  client.print(mqtt_reconnect);
 // client.print(F("<br><p>time: "));  client.print(timeClient.getFormattedTime()); client.print(" "); client.print(monthDay); client.print("-"); client.print(currentMonth); client.print("-"); client.print(currentYear);
 // client.print(F("<br>Humidity: "));  client.print(humidity,1);
//  client.print(F("%RH</p><br>Temperature: "));  client.print(temperature,1); client.print("&deg;C");
 // client.print(F("<br>Dew point: "));  client.print(dewpoint,1);client.print("&deg;C");
 // client.print(F("<br>Dew point lib: "));  client.print(dht.computeDewPoint(temperature,humidity,false),1);client.print("&deg;C");
 // client.print(F("<br><br>tube: "));  client.print(TUBE_NAME);client.print(F(" noise: "));  client.print(String(GeigerTubeNoise));  
 // client.print(F("<br>counts: "));  client.print(GeigerCounts);
//  client.print(F("<br><br><b>counts/s: </b>"));  client.print(GeigerCounts_1sec);
//  client.print(F("<br><b>counts/10s: </b>"));  client.print(GeigerCounts_10sec);
 // client.print(F("<br><b>counts/10s avg: </b>"));  client.print(Geiger10sAverage);
  //client.print(F("<br><b>CPM: </b>"));  client.print(GeigerCounts_60sec);
//  client.print(F("<br><b>max per day: CPS:</b>"));  client.print(maxCPS);client.print(F(" <b>CP10S:</b>"));  client.print(maxCP10S);client.print(F("<b> CPM:</b>"));  client.print(maxCPM);client.print(F("<b> &#181;Sv/h:</b>"));  client.print(String(maxuSV/1000.0,3).c_str());
//  client.print(F("<br><br><hr /><b>&#181;Sv/h avg: </b>"));  client.print(Geiger1mAverage / 1000.0, 4);
//  client.print(F("<br><b>&#181;Sv/h: </b>"));  client.print(uSvh / 1000.0, 4);
//  client.print(F("<br><b>mR/day: </b>"));  client.print(Roentgen1dAverage_cumulative, 3);
//  client.print(F("<br><hr /><br><b>GMC Map: </b>"));  client.print(GMCMap);
 // client.print(" (CPM:"); client.print(String(int(CPM1mAverage)).c_str()); client.print(" ACPM:"); client.print(String(CPM1mAverage / 1.0, 1).c_str()); client.print(" uSV:"); client.print(String(Geiger1mAverage / 1000.0, 3).c_str()); client.print(")<br>");

   client.print(F("<div style=\"position: absolute; bottom: 5px;\">"));
  for (x = 0; x < Roentgen1dAverage_number; x++) {
    client.print(x); client.print(":"); client.print(Roentgen1dAverage_array[x]); client.print(", ");
  }
    client.print(F("</div>"));
    






  
  // client.print(Roentgen1dAverage_position); client.print(FirstRunRoentgen);
 // client.print(flash.getCapacity());
//  client.print("<br><br><button onClick='window.location.reload();'>Refresh</button>");
  client.print("</body></html>");
}

int dBmtoPercentage(int dBm)
{
  int quality;
  if (dBm <= -100)  {
    quality = 0;
  }
  else if (dBm >= -50) {
    quality = 100;
  }
  else {
    quality = 2 * (dBm + 100);
  }
  return quality;
}//dBmtoPercentage

void reconnect() {
  // Loop until we're reconnected
  while (!mqttClient.connected()) {
 //       noInterrupts();
    Serial.print("Attempting MQTT connection...");
    if (mqttClient.connect("ESP8266Client", mqtt_user, mqtt_password)) {
      Serial.println("connected");
      mqtt_connected = true;
    } else {
      mqtt_connected = false;
      Serial.print("failed, rc=");
      mqtt_reconnect++;
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(500);
  //    interrupts();
    }
 //   interrupts();
  }
}

void eeprom_commit() {
  if (EEPROM.commit()) {
    Serial.println("EEPROM successfully committed!");
  } else {
    Serial.println("ERROR! EEPROM commit failed!");
    eeprom_err++;
  }
}

1 polubienie