[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto"
  },
  {
    "path": "Geiger_Counter/Geiger_Counter.ino",
    "content": " /*\n   Geiger.ino\n\n   This code interacts with the Alibaba RadiationD-v1.1 (CAJOE) Geiger counter board\n   and reports on Display readings in CPM (Counts Per Minute).\n   To the Thingspeak CPH (Counts Per Hour) are reported\n   Connect the output of the Geiger counter to pin inputPin.\n\n   Install Thingspulse SSD1306 Library IFTTTWebhook and ThingSpeak\n   Please notice you need to udate the https cerificatet signere certificate within IFTTTWebhook - doues not work with certificate manager.\n   IFTTTWebhook does not work with parameter .…… i asume as results from api changes.\n   Examle of my deice https://thingspeak.com/channels/1754536#\n\n   Author Hans Carlos Hofmann\n   Based on Andreas Spiess\n   Based on initial work of Mark A. Heckler (@MkHeck, mark.heckler@gmail.com)\n   License: MIT License\n   Please use freely with attribution. Thank you!\n*/\n\n#define Version \"V1.3.1\"\n#define CONS\n#define WIFI\n#define IFTT\n#ifdef CONS\n#define PRINT_DEBUG_MESSAGES\n#endif\n\n#ifdef WIFI\n#include <WiFi.h>\n#include <WiFiClientSecure.h>\n#ifdef IFTT\n#include \"IFTTTWebhook.h\"\n#endif\n#include <ThingSpeak.h>\n#endif\n#include <SSD1306.h>\n#include <esp_task_wdt.h>\n\n// #include <credentials.h>                 // or define mySSID and myPASSWORD and THINGSPEAK_API_KEY\n\n#define WIFI_TIMEOUT_DEF 30\n#define PERIOD_LOG 15                //Logging period \n#define PERIOD_THINKSPEAK 3600        // in seconds, >60\n#define WDT_TIMEOUT 10\n\n#ifndef CREDENTIALS\n// WLAN\n#ifdef WIFI\n#define mySSID \"xxx\"\n#define myPASSWORD \"xxx\"\n\n//IFTT\n#ifdef IFTT\n#define IFTTT_KEY \"xxx\"\n#endif\n\n// Thingspeak\n#define SECRET_CH_ID 00000000      // replace 0000000 with your channel number\n#define SECRET_WRITE_APIKEY \"xxx\"   // replace XYZ with your channel write API Key\n\n#endif\n\n// IFTTT\n#ifdef IFTT\n#define EVENT_NAME \"Radioactivity\" // Name of your event name, set when you are creating the applet\n#endif\n#endif\n\nIPAddress ip;\n\n#ifdef WIFI\nWiFiClient client;\n\nWiFiClientSecure secure_client;\n#endif\n\nSSD1306  display(0x3c, 5, 4);\n\nconst int inputPin = 26;\n\nint counts = 0;  // Tube events\nint counts2 = 0;\nint cpm = 0;                                             // CPM\nunsigned long lastCountTime;                            // Time measurement\nunsigned long lastEntryThingspeak;\nunsigned long startCountTime;                            // Time measurement\nunsigned long startEntryThingspeak;\n\n#ifdef WIFI\nunsigned long myChannelNumber = SECRET_CH_ID;\nconst char * myWriteAPIKey = SECRET_WRITE_APIKEY;\n#endif\n\n\nvoid IRAM_ATTR ISR_impulse() { // Captures count of events from Geiger counter board\n      counts++;\n      counts2++;\n}\n\n\nvoid displayInit() {\n  display.init();\n  display.flipScreenVertically();\n  display.setFont(ArialMT_Plain_24);\n}\n\nvoid displayInt(int dispInt, int x, int y) {\n  display.setColor(WHITE);\n  display.setTextAlignment(TEXT_ALIGN_CENTER);\n  display.drawString(x, y, String(dispInt));\n  display.setFont(ArialMT_Plain_24);\n  display.display();\n}\n\nvoid displayString(String dispString, int x, int y) {\n  display.setColor(WHITE);\n  display.setTextAlignment(TEXT_ALIGN_CENTER);\n  display.drawString(x, y, dispString);\n  display.setFont(ArialMT_Plain_24);\n  display.display();\n}\n\n\n/****reset***/\nvoid software_Reset() // Restarts program from beginning but does not reset the peripherals and registers\n{\n#ifdef CONS\n  Serial.println(\"resetting by software\");\n#endif\n  displayString(\"Myreset\", 64, 15);\n  delay(1000);\n  esp_restart();\n}\n\n\nvoid IFTTT(int postValue) {\n#ifdef WIFI\n#ifdef IFTT\n  IFTTTWebhook webhook(IFTTT_KEY, EVENT_NAME);\n  if (!webhook.trigger(String(postValue).c_str())) {\n#ifdef CONS\n    Serial.println(\"Successfully sent to IFTTT\");\n  } else\n  {\n    Serial.println(\"IFTTT failed!\");\n#endif\n  }\n#endif\n#endif\n}\n\nvoid postThingspeak( int value) {\n  // Write to ThingSpeak. There are up to 8 fields in a channel, allowing you to store up to 8 different\n  // pieces of information in a channel.  Here, we write to field 1.\n#ifdef WIFI\n  int x = ThingSpeak.writeField(myChannelNumber, 1, value, myWriteAPIKey);\n#ifdef CONS\n  if (x == 200) {\n    Serial.println(\"Channel update successful\");\n  }\n  else {\n    Serial.println(\"Problem updating channel. HTTP error code \" + String(x));\n  }\n#endif\n#endif\n}\n\nvoid printStack()\n{\n#ifdef CONS\n  char *SpStart = NULL;\n  char *StackPtrAtStart = (char *)&SpStart;\n  UBaseType_t watermarkStart = uxTaskGetStackHighWaterMark(NULL);\n  char *StackPtrEnd = StackPtrAtStart - watermarkStart;\n  Serial.printf(\"=== Stack info === \");\n  Serial.printf(\"Free Stack is:  %d \\r\\n\", (uint32_t)StackPtrAtStart - (uint32_t)StackPtrEnd);\n#endif\n}\n\nvoid setup() {\n  esp_task_wdt_init(WDT_TIMEOUT, true); //enable panic so ESP32 restarts\n  esp_task_wdt_add(NULL); //add current thread to WDT watch\n  \n#ifdef CONS\n  Serial.begin(115200);\n  Serial.print(\"This is \") ; Serial.println(Version) ;\n#endif \n\n  if (PERIOD_LOG > PERIOD_THINKSPEAK) {\n#ifdef CONS\n    Serial.println(\"PERIOD_THINKSPEAK has to be bigger than PERIODE_LOG\");\n#endif\n    while (1);\n  }\n  displayInit();\n#ifdef WIFI\n  ThingSpeak.begin(client);  // Initialize ThingSpeak\n#endif\n  displayString(\"Welcome\", 64, 0);\n  displayString(Version, 64, 30);\n  printStack();\n#ifdef WIFI\n#ifdef CONS\n  Serial.println(\"Connecting to Wi-Fi\");\n#endif \n\n  WiFi.begin(mySSID, myPASSWORD);\n\n  int wifi_loops = 0;\n  int wifi_timeout = WIFI_TIMEOUT_DEF;\n  while (WiFi.status() != WL_CONNECTED) {\n    wifi_loops++;\n#ifdef CONS\n    Serial.print(\".\");\n#endif\n    delay(500);\n    if (wifi_loops > wifi_timeout) software_Reset();\n  }\n#ifdef CONS\n  Serial.println();\n  Serial.println(\"Wi-Fi Connected\");\n#endif\n#endif\n  display.clear();\n  displayString(\"Measuring\", 64, 15);\n  pinMode(inputPin, INPUT);                            // Set pin for capturing Tube events\n#ifdef CONS\n  Serial.println(\"Defined Input Pin\");\n#endif\n  attachInterrupt(digitalPinToInterrupt(inputPin), ISR_impulse, FALLING);     // Define interrupt on falling edge\n  Serial.println(\"Irq installed\");\n\n  startEntryThingspeak = lastEntryThingspeak = millis();\n  startCountTime = lastCountTime = millis();\n#ifdef CONS\n  Serial.println(\"Initialized\");\n#endif  \n}\n\nint active = 0 ;\n\nvoid loop() {\n  esp_task_wdt_reset();\n#ifdef WIFI\n  if (WiFi.status() != WL_CONNECTED) software_Reset();\n#endif\n\n  if (millis() - lastCountTime > (PERIOD_LOG * 1000)) {\n#ifdef CONS\n    Serial.print(\"Counts: \"); Serial.println(counts);\n#endif\n    cpm = (60000 * counts) / (millis() - startCountTime) ;\n    counts = 0 ;\n    startCountTime = millis() ;\n    lastCountTime += PERIOD_LOG * 1000;\n    display.clear();\n    displayString(\"Radioactivity\", 64, 0);\n    displayInt(cpm, 64, 30);\n    if (cpm >= 200) {\n      if (active) {\n        active = 0 ;\n        display.clear();\n        displayString(\"¡¡ALARM!!\", 64, 0);\n        displayInt(cpm, 64, 30);\n#ifdef IFTT\n        IFTTT(cpm);\n#endif\n      } ;\n    }\n    else if (cpm < 100)\n    {\n      active = 1 ;\n    } ;\n#ifdef CONS\n    Serial.print(\"cpm: \"); Serial.println(cpm);\n#endif\n    printStack();\n  }\n\n  if (millis() - lastEntryThingspeak > (PERIOD_THINKSPEAK * 1000)) {\n#ifdef CONS\n    Serial.print(\"Counts2: \"); Serial.println(counts2);\n#endif\n    int averageCPH = (int)(((float)3600000 * (float)counts2) / (float)(millis() - startEntryThingspeak));\n#ifdef CONS\n    Serial.print(\"Average cph: \"); Serial.println(averageCPH);\n#endif\n    postThingspeak(averageCPH);\n    lastEntryThingspeak += PERIOD_THINKSPEAK * 1000;\n    startEntryThingspeak = millis();\n    counts2=0;\n  } ;\n  delay(50);\n}\n"
  },
  {
    "path": "README.md",
    "content": "# Geiger Counter (RadiationD-v1.1 CAJOE)\n\n[![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![GitHub stars](https://img.shields.io/github/stars/SensorsIot/Geiger-Counter-RadiationD-v1.1-CAJOE-)](https://github.com/SensorsIot/Geiger-Counter-RadiationD-v1.1-CAJOE-/stargazers)\n[![YouTube](https://img.shields.io/badge/YouTube-Video-red?logo=youtube)](https://youtu.be/K28Az3-gV7E)\n\n![ESP32](https://img.shields.io/badge/ESP32-Supported-green?logo=espressif)\n![Arduino](https://img.shields.io/badge/Arduino-Compatible-00979D?logo=arduino)\n![Radiation](https://img.shields.io/badge/CPM-Counts%20Per%20Minute-orange)\n\nInterface with the RadiationD-v1.1 (CAJOE) Geiger counter board. Displays readings on OLED, sends data to ThingSpeak, and triggers IFTTT alerts.\n\n📺 **Video Tutorial:** https://youtu.be/K28Az3-gV7E\n\n## 🔌 Wiring\n\n| Function | GPIO |\n|----------|------|\n| Geiger Counter Output | GPIO 26 |\n| OLED SDA | GPIO 5 |\n| OLED SCL | GPIO 4 |\n\n## 📦 Dependencies\n\nInstall using the Arduino Library Manager:\n\n| Library | Description |\n|---------|-------------|\n| [ThingPulse SSD1306](https://github.com/ThingPulse/esp8266-oled-ssd1306) | OLED display driver |\n| [IFTTTWebhook](https://github.com/romkey/IFTTTWebhook) | IFTTT notifications |\n| [ThingSpeak](https://github.com/mathworks/thingspeak-arduino) | ThingSpeak API |\n\n## ⚙️ Configuration\n\nEdit the credentials in `Geiger_Counter.ino`:\n\n```cpp\n// WiFi\n#define mySSID \"your-wifi-name\"\n#define myPASSWORD \"your-wifi-password\"\n\n// IFTTT\n#define IFTTT_KEY \"your-ifttt-key\"\n\n// ThingSpeak\n#define SECRET_CH_ID 0000000           // Your channel number\n#define SECRET_WRITE_APIKEY \"xxx\"       // Your API key\n```\n\n### ⏱️ Timing Settings\n\n```cpp\n#define PERIOD_LOG 15          // Display update interval (seconds)\n#define PERIOD_THINKSPEAK 3600 // ThingSpeak upload interval (seconds)\n```\n\n## 📊 Output\n\n- **Display:** CPM (Counts Per Minute)\n- **ThingSpeak:** CPH (Counts Per Hour)\n- **IFTTT Alert:** Triggered when CPM ≥ 200\n\n"
  },
  {
    "path": "simpletest",
    "content": "/* \n   edited by genewitch to actually provide clicks per minute on the serial port\n   in a sane fashion. the original file i forked from just set CPM to 105 and \n   didn't really work anyhow, as the count was reset every 20 seconds.\n   There's an issue with my understanding of the way variables work, so please\n   feel free to fix my cludge with the clock1 and start variables. when they \n   were in the loop the \"minutes\" never changed.\n\n   trying to create PR\n*/\n\n/*\n   Geiger.ino\n\n   This code interacts with the Alibaba RadiationD-v1.1 (CAJOE) Geiger counter board\n   and reports readings in CPM (Counts Per Minute).\n\n   Author: Andreas Spiess\n\n   Based on initial work of Mark A. Heckler (@MkHeck, mark.heckler@gmail.com)\n\n   License: MIT License\n\n   Please use freely with attribution. Thank you!\n*/\n\nvolatile unsigned long counts = 0;                       // Tube events\nunsigned long cpm = 0;                                   // CPM\nunsigned long previousMillis;                            // Time measurement\nconst int inputPin = 7;                                  // geiger is on this pin?\nunsigned int thirds = 0;\nunsigned long minutes = 1;\nunsigned long start = 0;\n\n#define LOG_PERIOD 20000 //Logging period in milliseconds\n#define MINUTE_PERIOD 60000\n\nvoid ISR_impulse() { // Captures count of events from Geiger counter board\n  counts++;\n}\n\nvoid setup() {\n  // put your setup code here, to run once:\n  Serial.begin(9600);\n  delay(1000);\n  Serial.println(\"Booting...\");\n  Serial.println(\"Measuring\");\n  pinMode(inputPin, INPUT);                                                // Set pin for capturing Tube events\n  interrupts();                                                            // Enable interrupts\n  attachInterrupt(digitalPinToInterrupt(inputPin), ISR_impulse, FALLING); // Define interrupt on falling edge\n  unsigned long clock1 = millis();\n  start = clock1;\n}\n\nvoid loop() {\n  // put your main code here, to run repeatedly:\n  unsigned long currentMillis = millis();\n  \n  \n  \n  if (currentMillis - previousMillis > LOG_PERIOD) {\n    \n    previousMillis = currentMillis;\n    // Serial.print(\"previousMillis: \");\n    // Serial.println(String(previousMillis));\n    minutes = (previousMillis - start) / MINUTE_PERIOD;\n    if (minutes < 1) {\n      minutes = 1;\n  }\n    // Serial.print(\"minutes: \");\n    // Serial.println(String(minutes));\n\n    //cpm = counts * MINUTE_PERIOD / LOG_PERIOD; this is just counts times 3 so:\n\n    cpm = counts / minutes;\n    Serial.print(\"Total clicks since start: \");    \n    Serial.println(String(counts));\n    Serial.print(\"Rolling CPM: \");\n    Serial.println(String(cpm));\n    \n//    if ( thirds > 2) {\n//      counts = 0;\n//      thirds = 0;  \n//    }\n    \n    \n  }\n}\n  \n  \n  \n"
  }
]