Skip to content

Radiation Monitoring - MQTT Geiger Counter

Radiation Monitoring

jaden

Published September 04, 2023

Original author: Aaron CakeOriginal source (new tab)

Radiation Monitoring - MQTT Geiger Counter

Project description

안녕하세요!
이번에 소개할 프로젝트는 MQTT 프로토콜을 이용한 방사선 모니터링입니다.

이 프로젝트는 방사선을 모니터링하여 Home Assitant에 연결하는 것을 목표로 한 프로젝트입니다.
방사선 모니터링에 사용하는 센싱에는 가이거 계수기라는 장치를 사용했습니다.
이 가이거 계수기는 감마 및 베타 입자를 탐지할 때마다 MCU에 펄스를 보내게 됩니다.

Radiation Monitoring - MQTT Geiger Counter

Radiation Monitoring - MQTT Geiger Counter


가이거 계수기는 방사선을 측정하는 장치입니다. 그렇다면 방사선이란 무엇일까요? 생각해보면, 태양이나 전등같은 빛을 발산하는 것을 알고 있을 것입니다. 방사선도 비슷하게 자연 또는 인공적으로 발생하는 '빛'의 한 종류라고 생각하면 됩니다. 다만, 우리 눈에는 보이지 않는 특별한 빛입니다.
이제 가이거 계수기가 어떻게 이 '특별한 빛'을 감지하는지 살펴봅시다.

튜브 안의 가스: 가이거 계수기 안에는 특별한 가스가 들어있는 긴 튜브가 있습니다.

전극: 이 튜브 안에는 긴 금속 막대가 있는데, 이것을 '중앙 전극'이라고 합니다. 튜브의 외벽도 전극의 역할을 합니다.

전압: 중앙 전극에는 높은 전압이 걸려있습니다.

방사선(눈에 보이지 않는 '빛')이 튜브를 통과하면서 내부의 가스를 '활성화'시킵니다. 이 활성화된 가스는 전기를 잘 전달하게 되어, 중앙 전극과 튜브 외벽 사이에 작은 전기 흐름(또는 스파크)이 발생하게 됩니다. 이 작은 전기 흐름이 발생할 때마다, 가이거 계수기는 '딸깍' 소리와 함께 계수를 하는데, 이것이 방사선의 존재와 강도를 알려주는 신호가 됩니다.

간단히 말해, 가이거 계수기는 눈에 보이지 않는 '특별한 빛'을 튜브 안의 가스를 활성화시킴으로써 감지하고, 그 때마다 '딸깍' 소리로 알려주는 장치라고 생각하면 됩니다!

ChatGPT로 쓰여짐.

 

MCU로는 STM32F103C8T6이 실장된 Blue pill을 사용하였습니다.
 

Radiation Monitoring - MQTT Geiger Counter
Radiation Monitoring - MQTT Geiger Counter

저자는 15초 간격으로 입자 탐지 수를 모아 CPM (Counts Per Minute, 분당 계수) 값을 계산하고 이를 MQTT를 통해 Home Assistant로 전송합니다. 이 데이터는 Home Assistant에서 시각화되어 방사선 노출 레벨을 사용자에게 표시하게 됩니다.

Radiation Monitoring - MQTT Geiger Counter

실험적으로 구현했던 이 시스템은 세슘 137 샘플을 사용하여 테스트되었고, 센서 그래프에는 뚜렷한 피크가 나타났다고 합니다.

 

아래는 저자가 공개한 소스 코드입니다.

 


/*************************************************************************
 * Filename: rhGeiger.ino                                                *
 * Author:   Brian K. Gauger (based loosely on code by Alex Boguslavsky) *                                          *
 * Date:     11 Oct 2018                                                 *
 * Purpose:  Arduino code for http://rhelectronics.net v3.00 board,      *
 *           Serial Monitor version                                      *
 *                                                                       *
 * Arduino IDE version: 1.0.6                                            *
 * Executable Size: ~5.6 kbytes                                          *
 *                                                                       *
 * Copyright 2018 Brian K. Gauger                                        *
 *                                                                       *
 * Licensed under terms of Creative Commons Attribution-ShareAlike 4.0   *
 * International (CC BY-SA 4.0). You can do pretty much anything you     *
 * want with this code, provided you:                                    *
 *      a) Give me credit somewhere in the comments, and                 *
 *      b) Distribute your modified code under the same terms.           *
 *************************************************************************
 *                                                                       *
 * ===================================================================== *
 * Speaking of credit... MANY THANKS to Alex Boguslavsky for developing  *
 * the rhGeiger kit, and for the original code that inspired this!       *
 * ===================================================================== *

 *************************************************************************
 *
 * IMPORTANT NOTE:
 * ===============
 * The radiation levels reported by your Geiger Kit are APPROXIMATE only!
 * GM tubes vary greatly in terms of sensitivity to alpha, beta, and
 * gamma radiation. Some good info at:
 * https://sites.google.com/site/diygeigercounter/gm-tubes-supported
 *
 * If you need the CPM to microSievert conversion factor for GM tubes
 * OTHER than the SBM-20, this is a good place to begin looking.
 *
 * Remember, your unit is NOT calibrated to a standardized source.
 * So, take your readings with a large grain of salt substitute (a slightly
 * radioactive "check source"! A small plastic bag of KCl laid on top of my
 * SBM-20 yields about 6x background in my area. Your results will vary).
 */

 /////////////////////////////////////////////////////////
 // HARDWARE CONNECTIONS --                             //
 // A) Connect Geiger PCB INT output to Arduino pin 2.  //
 // B) Connect  Geiger PCB & Arduino grounds together.  //
 // C) Ensure C-INT (103, 0.01 uF) is soldered on the   //
 //    Geiger PCB.                                      //
 /////////////////////////////////////////////////////////

 // Averaging (logging) period in milliseconds, typically 15000-60000.   
#define AVG_PERIOD  15000
#define ONE_MINUTE  60000

// I'm using the SBM-20 Geiger-Muller (GM) tube with my kit. Conversion
// factors for other GM tubes may be found scattered throughout the Web.   

#define SBM_20

#ifdef SBM_20
#define CONV_FACTOR 0.0057   // SBM-20 counts to uSv/hr multiplier
#endif


#include 

//PubSubClient library for MQTT
#include 


//ethernet related includes
#include 
#include 							//ethernet2 for non-STM32

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
#if defined(WIZ550io_WITH_MACADDRESS) // Use assigned MAC address of WIZ550io
;
#else
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
#endif  



// Initialize the Ethernet client library

EthernetClient ethClient;

//MQTT

//MQTT sever address
#define MQTT_SERVER "192.168.107.11"  

void MQTTCallback(char* topic, byte* payload, unsigned int length);		//function prototype for MQTT callback

char const* MQTTClientName = "geiger";								//MQTT client name. Keep short, don't waste RAM
const char TelemetryTopic[] = "/geiger/tele/";					//Suffix added to board topic for telemetry heatbeat
const char CountTopic[] = "/geiger/cpm/";
const char DoseTopic[] = "/geiger/dose/";

PubSubClient MQTTClient(MQTT_SERVER, 1883, MQTTCallback, ethClient);	//pass Ethernet object to PubSubClient and create MQTT client



unsigned long counts; // # of raw GM Tube events
unsigned long cpm;				// Counts Per Minute (CPM)
unsigned int  mult;   // CPM = (counts in a given interval) * multiplier

char CountString[16];
char DoseString[16];


// Interrupt handler that counts raw events from Geiger Kit
void tube_impulse() {
	counts++;
}


void ConnectToMQTTBroker() {


	// Loop until connected to MQTT broker
	while (!MQTTClient.connected()) {
		Serial.println(F("MQTT: Attempting connection..."));

		//if connected, subscribe to the relay topics as BoardTopic/relay number
		if (MQTTClient.connect((char*)MQTTClientName)) {
			Serial.println(F("MQTT: Connected"));

		}
	}
}

void MQTTCallback(char* topic, byte* payload, unsigned int length) {
	//function called when MQTT message received

}


void setup()
{
	counts = 0;
	cpm = 0;

	// Initialize Serial communications
	Serial.begin(9600);

	Serial.println(F("Boot"));

	// start the Ethernet and obtain an address via DHCP
	Serial.println(F("Start: Ethernet..."));

	if (Ethernet.begin(mac) == 0) {
		Serial.println(F("FAIL: Ethernet: No DHCP."));

	}


	Serial.print(F("Success: Ethernet. DHCP IP: "));
	Serial.println(Ethernet.localIP());

	delay(3000);

	ConnectToMQTTBroker();							//connect to MQTT broker


	// Determine multiplier for your log period
	mult = ONE_MINUTE / AVG_PERIOD;

	pinMode(PA0, INPUT);                          // Set pin 2 up for GM tube event interrupts
	digitalWrite(PA0, HIGH);                      // Use internal pullup resistor
 


	//attachInterrupt(0, tube_impulse, FALLING);
	attachInterrupt(digitalPinToInterrupt(PA0), tube_impulse, FALLING);

	Serial.print(F("First reading in "));
	Serial.print(AVG_PERIOD / 1000);
	Serial.println(F(" sec..."));
	
}

void loop()
{

	//reconnect if connection is lost
	if (!MQTTClient.connected()) { ConnectToMQTTBroker(); }

	MQTTClient.loop();									//maintain the MQTT connection

	// Elapsed time stuff
	static unsigned long then;
	unsigned long now = millis();

	// Approx radiation level in microsieverts
	double uSv;

	switch (Ethernet.maintain()) {
	case 1:
		//renewed fail
		Serial.println(F("ERROR: DHCP Renewal Failure"));
		break;

	case 2:
		//renewed success
		Serial.println(F("SUCCESS: DHCP Renew"));
		//print your local IP address:
		Serial.print(F("IP Address: "));
		Serial.println(Ethernet.localIP());
		break;

	case 3:
		//rebind fail
		Serial.println(F("ERROR: Rebind failure"));
		break;

	case 4:
		//rebind success
		Serial.println(F("SUCCESS: Rebind"));
		//print your local IP address:
		Serial.print(F("IP Address: "));
		Serial.println(Ethernet.localIP());
		break;

	default:
		//nothing happened
		break;
	}



	if (now - then > AVG_PERIOD) {
		then = now;
		if (counts) {                     // i.e., if (counts != 0)
			cpm = counts * mult;
			uSv = cpm * CONV_FACTOR;
		}
		else {
			uSv = 0;
		}
		Serial.print(F("Counts: "));
		Serial.println(counts);
		Serial.print(F("CPM: "));
		Serial.println(cpm);
		Serial.print(F("uSv/hr: "));
		Serial.println(uSv, 4);          // Display 4 decimal places
		
		//convert both numbers to cstrings for MQTT publish
		itoa(cpm, CountString, 10);
		dtostrf(uSv, 5, 5, DoseString);


		counts = 0, cpm = 0;							//reset the counts fo rnext loop
	
		Serial.print(F("MQTT Publish:"));
		Serial.print(CountTopic);
		Serial.println(CountString);
		
		Serial.print(F("MQTT Publish:"));
		Serial.print(DoseTopic);
		Serial.println(DoseString);


		MQTTClient.publish(CountTopic, CountString);					
		MQTTClient.publish(DoseTopic, DoseString);
	}
	
}

 

Home Assistant에 정의된 MQTT sensor 

mqtt:
  sensor:
############Geiger Counter
    - name: "Background Radiation CPM"
      unit_of_measurement: 'CPM'
      state_topic: "/geiger/cpm/"
    - name: "Background Radiation Dose Rate"
      unit_of_measurement: 'uSv'
      state_topic: "/geiger/dose/"

 

 


hello!
This project is about radiation monitoring using MQTT protocol.

This project aims to monitor radiation and connect it to a home assistant.
For the sensing we used for radiation monitoring, we used a device called a Geiger counter.
This Geiger counter will send pulses to the MCU whenever it detects gamma and beta particles.

Radiation Monitoring - MQTT Geiger Counter

Radiation Monitoring - MQTT Geiger Counter


A Geiger counter is a device that measures radiation. So, what is radiation? Think of the light emitted by the sun or a light bulb. Radiation is similar, being a type of 'light' that is either naturally or artificially produced. However, it's a special kind of light that our eyes can't see.

Now, let's look at how the Geiger counter detects this 'special light'.

  1. Gas-filled Tube: Inside the Geiger counter, there's a long tube filled with a special gas.
  2. Electrode: Within this tube, there's a long metal rod, known as the 'central electrode'. The outer wall of the tube also acts as an electrode.
  3. Voltage: A high voltage is applied to the central electrode.

As radiation (the invisible 'light') passes through the tube, it 'excites' the gas inside. This excited gas becomes a good conductor of electricity, resulting in a small electrical current (or spark) between the central electrode and the tube's outer wall. Every time this small current occurs, the Geiger counter makes a 'click' sound and counts it. This count serves as a signal, indicating the presence and intensity of radiation.

In simple terms, a Geiger counter detects the invisible 'special light' by exciting the gas inside its tube, and then signals its presence with a 'click' sound!

Written by ChatGPT

 

For the MCU, we used a Blue pill with an STM32F103C8T6.
 

Radiation Monitoring - MQTT Geiger Counter
Radiation Monitoring - MQTT Geiger Counter

The author collects particle detections at 15-second intervals, calculates a Counts Per Minute (CPM) value, and sends it to Home Assistant via MQTT. This data will be visualized by Home Assistant to display the radiation exposure level to the user.

Radiation Monitoring - MQTT Geiger Counter

The experimental implementation of the system was tested using a cesium 137 sample, and reportedly showed a distinct peak in the sensor graph.

 

Below is the source code released by the author.

 


/*************************************************************************
 * Filename: rhGeiger.ino                                                *
 * Author:   Brian K. Gauger (based loosely on code by Alex Boguslavsky) *                                          *
 * Date:     11 Oct 2018                                                 *
 * Purpose:  Arduino code for http://rhelectronics.net v3.00 board,      *
 *           Serial Monitor version                                      *
 *                                                                       *
 * Arduino IDE version: 1.0.6                                            *
 * Executable Size: ~5.6 kbytes                                          *
 *                                                                       *
 * Copyright 2018 Brian K. Gauger                                        *
 *                                                                       *
 * Licensed under terms of Creative Commons Attribution-ShareAlike 4.0   *
 * International (CC BY-SA 4.0). You can do pretty much anything you     *
 * want with this code, provided you:                                    *
 *      a) Give me credit somewhere in the comments, and                 *
 *      b) Distribute your modified code under the same terms.           *
 *************************************************************************
 *                                                                       *
 * ===================================================================== *
 * Speaking of credit... MANY THANKS to Alex Boguslavsky for developing  *
 * the rhGeiger kit, and for the original code that inspired this!       *
 * ===================================================================== *

 *************************************************************************
 *
 * IMPORTANT NOTE:
 * ===============
 * The radiation levels reported by your Geiger Kit are APPROXIMATE only!
 * GM tubes vary greatly in terms of sensitivity to alpha, beta, and
 * gamma radiation. Some good info at:
 * https://sites.google.com/site/diygeigercounter/gm-tubes-supported
 *
 * If you need the CPM to microSievert conversion factor for GM tubes
 * OTHER than the SBM-20, this is a good place to begin looking.
 *
 * Remember, your unit is NOT calibrated to a standardized source.
 * So, take your readings with a large grain of salt substitute (a slightly
 * radioactive "check source"! A small plastic bag of KCl laid on top of my
 * SBM-20 yields about 6x background in my area. Your results will vary).
 */

 /////////////////////////////////////////////////////////
 // HARDWARE CONNECTIONS --                             //
 // A) Connect Geiger PCB INT output to Arduino pin 2.  //
 // B) Connect  Geiger PCB & Arduino grounds together.  //
 // C) Ensure C-INT (103, 0.01 uF) is soldered on the   //
 //    Geiger PCB.                                      //
 /////////////////////////////////////////////////////////

 // Averaging (logging) period in milliseconds, typically 15000-60000.   
#define AVG_PERIOD  15000
#define ONE_MINUTE  60000

// I'm using the SBM-20 Geiger-Muller (GM) tube with my kit. Conversion
// factors for other GM tubes may be found scattered throughout the Web.   

#define SBM_20

#ifdef SBM_20
#define CONV_FACTOR 0.0057   // SBM-20 counts to uSv/hr multiplier
#endif


#include 

//PubSubClient library for MQTT
#include 


//ethernet related includes
#include 
#include 							//ethernet2 for non-STM32

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
#if defined(WIZ550io_WITH_MACADDRESS) // Use assigned MAC address of WIZ550io
;
#else
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
#endif  



// Initialize the Ethernet client library

EthernetClient ethClient;

//MQTT

//MQTT sever address
#define MQTT_SERVER "192.168.107.11"  

void MQTTCallback(char* topic, byte* payload, unsigned int length);		//function prototype for MQTT callback

char const* MQTTClientName = "geiger";								//MQTT client name. Keep short, don't waste RAM
const char TelemetryTopic[] = "/geiger/tele/";					//Suffix added to board topic for telemetry heatbeat
const char CountTopic[] = "/geiger/cpm/";
const char DoseTopic[] = "/geiger/dose/";

PubSubClient MQTTClient(MQTT_SERVER, 1883, MQTTCallback, ethClient);	//pass Ethernet object to PubSubClient and create MQTT client



unsigned long counts; // # of raw GM Tube events
unsigned long cpm;				// Counts Per Minute (CPM)
unsigned int  mult;   // CPM = (counts in a given interval) * multiplier

char CountString[16];
char DoseString[16];


// Interrupt handler that counts raw events from Geiger Kit
void tube_impulse() {
	counts++;
}


void ConnectToMQTTBroker() {


	// Loop until connected to MQTT broker
	while (!MQTTClient.connected()) {
		Serial.println(F("MQTT: Attempting connection..."));

		//if connected, subscribe to the relay topics as BoardTopic/relay number
		if (MQTTClient.connect((char*)MQTTClientName)) {
			Serial.println(F("MQTT: Connected"));

		}
	}
}

void MQTTCallback(char* topic, byte* payload, unsigned int length) {
	//function called when MQTT message received

}


void setup()
{
	counts = 0;
	cpm = 0;

	// Initialize Serial communications
	Serial.begin(9600);

	Serial.println(F("Boot"));

	// start the Ethernet and obtain an address via DHCP
	Serial.println(F("Start: Ethernet..."));

	if (Ethernet.begin(mac) == 0) {
		Serial.println(F("FAIL: Ethernet: No DHCP."));

	}


	Serial.print(F("Success: Ethernet. DHCP IP: "));
	Serial.println(Ethernet.localIP());

	delay(3000);

	ConnectToMQTTBroker();							//connect to MQTT broker


	// Determine multiplier for your log period
	mult = ONE_MINUTE / AVG_PERIOD;

	pinMode(PA0, INPUT);                          // Set pin 2 up for GM tube event interrupts
	digitalWrite(PA0, HIGH);                      // Use internal pullup resistor
 


	//attachInterrupt(0, tube_impulse, FALLING);
	attachInterrupt(digitalPinToInterrupt(PA0), tube_impulse, FALLING);

	Serial.print(F("First reading in "));
	Serial.print(AVG_PERIOD / 1000);
	Serial.println(F(" sec..."));
	
}

void loop()
{

	//reconnect if connection is lost
	if (!MQTTClient.connected()) { ConnectToMQTTBroker(); }

	MQTTClient.loop();									//maintain the MQTT connection

	// Elapsed time stuff
	static unsigned long then;
	unsigned long now = millis();

	// Approx radiation level in microsieverts
	double uSv;

	switch (Ethernet.maintain()) {
	case 1:
		//renewed fail
		Serial.println(F("ERROR: DHCP Renewal Failure"));
		break;

	case 2:
		//renewed success
		Serial.println(F("SUCCESS: DHCP Renew"));
		//print your local IP address:
		Serial.print(F("IP Address: "));
		Serial.println(Ethernet.localIP());
		break;

	case 3:
		//rebind fail
		Serial.println(F("ERROR: Rebind failure"));
		break;

	case 4:
		//rebind success
		Serial.println(F("SUCCESS: Rebind"));
		//print your local IP address:
		Serial.print(F("IP Address: "));
		Serial.println(Ethernet.localIP());
		break;

	default:
		//nothing happened
		break;
	}



	if (now - then > AVG_PERIOD) {
		then = now;
		if (counts) {                     // i.e., if (counts != 0)
			cpm = counts * mult;
			uSv = cpm * CONV_FACTOR;
		}
		else {
			uSv = 0;
		}
		Serial.print(F("Counts: "));
		Serial.println(counts);
		Serial.print(F("CPM: "));
		Serial.println(cpm);
		Serial.print(F("uSv/hr: "));
		Serial.println(uSv, 4);          // Display 4 decimal places
		
		//convert both numbers to cstrings for MQTT publish
		itoa(cpm, CountString, 10);
		dtostrf(uSv, 5, 5, DoseString);


		counts = 0, cpm = 0;							//reset the counts fo rnext loop
	
		Serial.print(F("MQTT Publish:"));
		Serial.print(CountTopic);
		Serial.println(CountString);
		
		Serial.print(F("MQTT Publish:"));
		Serial.print(DoseTopic);
		Serial.println(DoseString);


		MQTTClient.publish(CountTopic, CountString);					
		MQTTClient.publish(DoseTopic, DoseString);
	}
	
}

 

MQTT sensor defined in Home Assistant

mqtt:
  sensor:
############Geiger Counter
    - name: "Background Radiation CPM"
      unit_of_measurement: 'CPM'
      state_topic: "/geiger/cpm/"
    - name: "Background Radiation Dose Rate"
      unit_of_measurement: 'uSv'
      state_topic: "/geiger/dose/"

 

Comments

Comments