W kolejnym, 4-tym odcinku cyklu dotyczącego budowy liczników Geigera-Müllera (G-M), do projektu wykonanego w części 3, dodamy moduł zliczający impulsy i konwertujący tę liczbę na wartość dawki promieniowania pochłoniętego. Układ zrealizujemy na bazie Arduino Nano (Fig. 1). Jako urządzenie wyjściowe zastosujemy popularny wyświetlacz ciekłokrystaliczny z telefonów Nokia 5110. Układ w czasie rzeczywistym będzie obliczał przybliżoną liczbę impulsów na minutę (CPM), a następnie będzie ją przeliczał na wartość dawki promieniowania wyrażoną w μS/h.
Formuła, która przelicza CPM na wartość dawki promieniowania wyrażoną w sievertach zależy głównie od zastosowanej tuby Geigera: jej rozmiaru, kształtu, materiału, wrażliwości, czasu martwego, rodzaju mierzonej cząsteczki i innych. Zwykle współczynniki konwersji można uzyskać na podstawie wykresów zamieszczanych w notach katalogowych. Ogólny wzór umożliwiający konwersję wartości CPM na μS/h ma postać:
CPM × współczynnik konwersji = μSv/h (Wz. 1)
Wartości współczynników konwersji dla typowych tub G-M przedstawia Tab. 1.
| typ lampy G-M | współczynnik konwersji |
|---|---|
| SBM-20 | 0,006315 |
| LDN-712 | 0,005940 |
| SI29BG | 0,010000 |
| SBM19 | 0,001500 |
| STS-5 | 0,006666 |
| SI22G | 0,001714 |
| SI3BG: | 0,631578 |
| SBM21 | 0,048000 |
| SBT9 | 0,010900 |
| SI1G | 0,006000 |
Układ detekcji promieniowania jonizującego, tak jak poprzednio będzie oparty o tubę G-M STS-5 i przetwornicę impulsową podwyższającą napięcie z wejściowego (5-12V) do 400V (DC1; Fig. 2).
Zamiast zasilacza płytek prototypowych (5V) wykorzystywanego w częściach 2 i 3, zastosujemy małą impulsową przetwornicę podwyższającą napięcie z dostarczonego przez jedno ogniwo Li-ionowe 18650 około 4V na napięcie 5V (DC2; Fig. 3).
Dodatkowo, w celu umożliwienia ładowania akumulatora, dodano układ ładujący ugniwa Li-jonowe (Fig. 4). Umożliwia on szybkie naładowanie ogniwa z wykorzystaniem dowolnej ładowarki wyposażonej w wyjście usb.
Zbudujmy układ wg schematu wykonawczego z Fig. 5. Moduły wraz z elementami elektronicznymi rozmieszczono na dwóch płytkach prototypowych. Schemat wykorzytuje trzy magistrale zasilające. Patrząc od góry występują na nich napięcia: ok. 4,2V; 5V i ok. 400V.
Tuba G-M, poprzez rezystor R1 (6,8MΩ 0,25W) będzie zasilana napięciem 400V generowanym w przetwornicy DC1. Impuls wyładowania lawinowego poprzez kondensator C1 (15pF) będzie przekazywany do układu wzmacniającego. Zastosowano wzmacniacz zbudowany w oparciu o dwa tranzystory BC-547 pracujące w układzie Darlingtona. W celu przyspieszenia przełączania układu (zwłaszcz przy przechodzeniu do stanu zatkania) pomiędzy bazę i emiter tranzystora T2 podłączono rezystor R3 (3,3kΩ). Na wyjściu wzmacniacza zastosowano zieloną diodę świecącą (LED1), która będzie reagowała na analogowe sygnały z tuby. Do wyjścia wzmacniacza podłączono ją poprzez rezystor R8 (150Ω). Sygnał jest dalej przekazywany do układu pomiarowego opartego o mikrokontroler Arduino Nano. Sygnał poprowadzono do pinu cyfrowego D8. Do układu mikrokontrolera podłączono trzy urządzenia wyjściowe: buzzer (SG1), wyświetlacz ciekłokrystaliczny Nokia 5110 oraz czerwoną diodę świecącą (LED2), której zadaniem będzie sygnalizowanie alarmów. Buzzer poprzez mikroprzełącznik oraz rezystor ograniczający prąd R10 (100Ω) podłączono do pinu cyfrowego D9. Wyświetlacz ciekłokrystaliczny poprzez rezystory ograniczające prąd R3-R7 (10kΩ) podłączono do pinów cyfrowych Arduino D3-D7. Układ wyświetlacza, zgodnie z zaleceniami producenta zasilany jest napięciem 3,3V pochodzącym z układu Arduino. Czerwoną diodę świecącą (LED2) oprzez rezystor R9 (130Ω) podłączono do pinu cyfrowego D11. W celu kontroli stanu naładowania akumulatora, napięcie z jego zacisków podłączono do pinu analogowego A0 układu Arduino.
Układ mikrokontrolera dokonuje ciągłej kontroli stanu pinu cyfrowego D8, stanowiącego wejście sygnału pomiarowego. Gdy Arduino zarejestruje impuls, obliczany jest czas jaki upłynął od poprzedniego impulsu. Obliczona różnica stanowi podstawę do obliczenia częstotliwości pojawienia się impulsów w okresie jednej minuty (CPM). Pojawienie się cząsteczki promieniowania jonizującego jest sygnalizowane akustycznie przez buzzer oraz optycznie za pomocą diody LED2. Obliczona wartość CPM jest następnie przeliczana na szacunkową wartość dawki promieniwania wyrażoną w μSv/h (Wz. 1). Dwa alarmy: 1000 i 5000 μSv
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#define GeigerCounter 8 // geiger counter pin
#define Buzzer 9 // buzzer pin
#define LED_Alarm 11 // LED_Alarm pin
#define numReadings 30 // raise this number to increase data smoothing (mean CPM value)
const float factor = 0.006666; // define constans factor CPM to uS/h
const int alarm1 = 850; // 5,70 ?Sv/h (max day dose calculated as a part of the max permissible annual radiation dose in USA (50 000 ?Sv)
const int alarm2 = 1700;
int noCounts = 0; // Variable: no of counts
const unsigned char myBitmap [] PROGMEM = {
// logo, 80x46px
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff,
0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xc1, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xf0, 0x1f,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00,
0x7f, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xe0, 0x03, 0xff, 0xff,
0xff, 0xe0, 0x3b, 0xfc, 0xe1, 0xf0, 0x03, 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x1b, 0xfc, 0xe1, 0xc0,
0x63, 0xff, 0x80, 0x3f, 0xff, 0x87, 0x13, 0x80, 0x37, 0x83, 0xe3, 0x80, 0x8e, 0x1f, 0xff, 0x0f,
0xe3, 0x00, 0x37, 0x8f, 0x03, 0x80, 0x0f, 0x1f, 0xff, 0x1f, 0xe3, 0xf8, 0x3f, 0x8f, 0x03, 0xfc,
0x0e, 0x1f, 0xff, 0x1c, 0x23, 0xf8, 0x3f, 0x8d, 0xe3, 0xfc, 0x40, 0x3f, 0xff, 0x18, 0x23, 0xf8,
0x3f, 0x8f, 0xe3, 0xfc, 0x40, 0x7f, 0xff, 0x1c, 0x23, 0x00, 0x3b, 0x8d, 0xe3, 0x80, 0x40, 0x7f,
0xff, 0x0f, 0x23, 0x00, 0x3f, 0x8e, 0xe3, 0x80, 0x6c, 0x3f, 0xff, 0x87, 0x23, 0x80, 0x37, 0xfb,
0x63, 0xc0, 0x6e, 0x3f, 0xff, 0xc0, 0x63, 0xfc, 0xf2, 0x3c, 0x63, 0xfe, 0x6f, 0x1f, 0xff, 0xe0,
0x03, 0xfc, 0xe2, 0x0e, 0x43, 0xfc, 0x6f, 0x0f, 0xff, 0xff, 0x80, 0x00, 0x18, 0x01, 0x80, 0x00,
0x1f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x18, 0x01, 0x80, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfc, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x03,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf3, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf8, 0x01, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xe0, 0x78, 0xe0, 0xef, 0xe0, 0x70, 0x38, 0x8f,
0xff, 0x80, 0xc0, 0x38, 0xe8, 0xff, 0xe0, 0x60, 0x18, 0x0f, 0xff, 0x1e, 0x86, 0x38, 0xf8, 0xe3,
0xf1, 0xe7, 0x98, 0x0f, 0xff, 0x1f, 0x8f, 0x18, 0xf8, 0xe3, 0xf1, 0xc0, 0x18, 0xff, 0xff, 0x1f,
0x8f, 0x18, 0xd8, 0xe3, 0xb1, 0xc0, 0x18, 0xff, 0xff, 0x1f, 0x8f, 0x18, 0xd8, 0xe3, 0xb1, 0xc7,
0xf8, 0xff, 0xff, 0x0e, 0xc6, 0x38, 0xb8, 0xe3, 0x91, 0xe3, 0xd8, 0xff, 0xff, 0x80, 0xc0, 0x38,
0x78, 0xe3, 0x90, 0x60, 0x18, 0xff, 0xff, 0xc1, 0xe0, 0xfc, 0xd8, 0xe3, 0x88, 0x78, 0x1c, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00,
0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfc, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x07, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xff, 0xff
};
long cancelTime = 0; // time after which, when radiation is detected, the buzzer is switched off
long lastEventTime = 0; // variable used to calculate the time between radiation detections
int CPM = 0; // variable: counts per 1 minute
boolean canceled = true; // logical variable for activation of the buzzer
float uSperH; // dose of radiation in uS/h
float uSperHmax = 0.0; // max value of the dose of radiation in uS/h
int readings[numReadings]; // the readings from the analog input
int index = 0; // the index of the current reading
int total = 0; // the running total
int meanCPM = 0; // final average of the probe reading
// DNokia 5110 display pins:
// pin 7 - Serial clock out (SCLK)
// pin 6 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 3 - LCD reset (RST)
//Adafruit_PCD8544 display = Adafruit_PCD8544(SCLK, DIN, D/C, CS, RST);
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);
void setup() {
Serial.begin(9600);
pinMode(GeigerCounter, INPUT);
pinMode(Buzzer, OUTPUT);
pinMode(LED_Alarm, OUTPUT);
pinMode(A0, INPUT);
display.begin(); // start display
display.setContrast(40); // contrast settings
display.clearDisplay(); // clear display buffer
display.drawBitmap(2, 1, myBitmap, 80, 46, BLACK); // draw bitmap myBitmap
display.display(); // display bitmap
delay(3000); // stop for 3 sec.
display.clearDisplay(); // clear display buffer
display.drawRect(0, 0, 83, 47, BLACK); // draw a rectangle from (x1,y1) to (x2,y2)
display.setTextSize(1); // font size settings
display.setTextColor(BLACK); // font color settings
display.setCursor(26, 5); // set cursor on position X, y
display.print("Geiger"); // print txt
display.setCursor(23, 15); // set cursor on position X, y
display.print("counter"); // print txt
display.setCursor(3, 25); // set cursor on position X, y
display.print("Tomasz Bartus"); // print txt
display.setCursor(32, 35); // set cursor on position X, y
display.print("2018"); // print txt
display.display(); // display buffer
delay(1000); // stop for 2 sec.
tone(Buzzer, 500);
delay(200);
tone(Buzzer, 1000);
delay(200);
tone(Buzzer, 1500);
delay(200);
noTone(Buzzer);
display.clearDisplay(); // clear the screen and buffer
for (int i = 0; i < numReadings; i++) {
readings[i] = 0; // initialize all the readings to 0
}
}
void loop() {
if (digitalRead(GeigerCounter)) {
long eventTime = millis();
analogWrite(Buzzer, 127);
canceled = false;
cancelTime = eventTime + 10;
long elapsed = eventTime - lastEventTime;
int CPM = 60000 / elapsed;
uSperH = CPM * factor; // convertion of the CPM value to the radiation dose value
if (uSperH > uSperHmax) { // if the current dose measurement is greather than the max dose measurement
uSperHmax = uSperH; // assign the current dose measurement as the max measurement
}
lastEventTime = eventTime;
delay(100);
noCounts++; // increment no of counts
total -= readings[index]; // subtract the last reading
readings[index] = CPM; // read from the sensor
total += readings[index]; // add the reading to the total
index = (index + 1); // advance to the next index
if (index >= numReadings) // if we're at the end of the array...
index = 0; // ...wrap around to the beginning
meanCPM = total / numReadings; // calculate the average
// float power = analogRead(A0) / 1024.00 * 5; // power in volts
float power = (analogRead(A0) * 5) / 1023.00; // power in volts
display.clearDisplay();
display.setCursor(2, 0); // set cursor on position x, y
display.print(power); // print power voltage
display.setCursor(26, 0); // set cursor on position x, y
display.print("V"); // print txt
display.setCursor(2, 9); // set cursor on position x, y
display.print("CPM: "); // print txt
display.print(CPM); // print the CPM value
display.setCursor(2, 17); // set cursor on position x, y
display.print((char)229); // print the mi char
display.print("S/h: "); // print txt
display.print(uSperH); // print the dose value
display.setCursor(2, 25); // set cursor on position x, y
display.print("counts: "); // print txt
display.print(noCounts); // print the no of counts value
display.setCursor(2, 33); // set cursor on position x, y
display.print("max: "); // print txt
display.print(uSperHmax); // print the max dose value
display.setCursor(57, 33); // set cursor on position x, y
display.print((char)229); // print the mi char
display.print("S/h"); // print txt
display.setCursor(2, 41); // set cursor on position x, y
display.print("mean CPM: ");
if (noCounts < numReadings) {
display.print("...");
} else {
display.print(meanCPM); // print the average CPM value (from the numReadings table of readings)
}
display.display(); // display buffer
if (CPM >= alarm1) {
digitalWrite(LED_Alarm, HIGH); // turn the LED on
tone(Buzzer, 500);
delay(300);
tone(Buzzer, 1500);
delay(300);
tone(Buzzer, 500);
delay(300);
tone(Buzzer, 1500);
delay(300);
tone(Buzzer, 500);
delay(300);
tone(Buzzer, 1500);
delay(300);
noTone(Buzzer);
digitalWrite(LED_Alarm, LOW); // turn the LED on
}
} else if (! canceled && millis() > cancelTime) {
analogWrite(Buzzer, 0);
cancelTime = millis();
canceled = true;
}
}
Na wstępie deklarowane są biblioteki obsługujące komunikację układu Arduino z wyświetlaczem Nokia 5110 oraz obsługujące ten wyświetlacz. Następnie deklarowane są zmienne definiujące numery pinów, do których podłączono tubę G-M oraz urządzenia wyjściowe - buzzer i czerwoną diodę LED służącą do optycznej sygnalizacji alarmu w przypadku przekroczenia dopuszczalnego poziomu promieniowania. Warto zwrócić uwagę, że buzzer podłączono do pinu 9 umożliwiającego modulację szerokości impulsu czyli PWM.
Deklarowana jest też zmienna numReadings definiująca liczbę elementów tablicy readings, w której będą deponowane kolejne pomierzone wartości CPM. Gromadzone dane będą potrzebne do wyznaczania przeciętnej liczby CPM.
W kolejnych wierszach definiowane są:
Definiowana jest także zmienna noCounts przechowująca całkowitą zarejestrowaną liczbę cząstek promieniowania.
W kolejnej linii skryptu deklarowana tablica przechowująca bitmapę, która będzie wyświetlana na ekranie wyświetlacza w chwili włączenia urządzenia. Można ją automatycznie wygenerować w odpowiednich programach (np. LCDAssistant lub też za pomocą aplikacji dostępnych online), na podstawie przygotowanych wcześniej plików bmp o wielkości obrazka nie przekraczającej liczby punktów wyświetlacza Nokia 5110, czyli 84 ∗ 48 pikseli. W praktyce dobrze jest zrobić taki obrazek nieco mniejszy np. 80 ∗ 46 pikseli.
Pod tablicą bitmapy deklarowane są zmienne pomocnicze wykorzystywane w skrypcie:
cancelTime - definiujący czas, po którym, po wykryciu promieniowania przez rurkę G-M, wyłączany jest buzzer,lastEventTime - czas ostatniej rejestracji cząstki promieniowania,
CPM - początkową wartość zliczeń cząstek na minutę (CPM),canceled - zmienna logiczna służąca aktywacji buzzera w momencie wykrycia cząstki promieniowania jonizującego, jej wartość początkowa ustawiana jest na "true" - czyli wyłączony,uSperH - zmienna przechowująca wartość dawki promieniowania jonizującego wyrażoną w μS/h,uSperHmax - wartość początkowa maksymalnej rejestrowanej dawki promieniowania.
Po deklaracji wyżej wymienionyh zmiennych, tworzona jest numReadings - elementowa tablica readings, przechowująca czasy rejestracji cząstek promieniowania jonizującego.
Następnie deklarowane są jeszcze trzy zmienne:
index - zmienna przechowująca indeks z tablicy readings aktualnie rejestrowanej cząski,total - zmienna wyrażająca sumaryczną wartość wszystkich pomiarów CPM przechowywanych w tablicy readings,meanCPM - wartość początkowa średniej wartości CPM.W kolejnej linii skryptu deklarowane są piny, do których podłączono wyświetlacz Nokia 5110.
Po tych wstępnych deklaracjach następuje część setup, w której
W kolejnych liniach skryptu:
Następnie na ekran wyświetlacza wysyłany jest obraz zadeklarowanej tablicy z bitmapą. Będzie on wyświetlany przez 3000ms czyli 3 sekundy.
Po jego wyświetleniu i 1-sekundowej pauzie, buzer wygeneruje kolejno dźwięki o częstotliwościach kolejno: 500, 1000 i 1500 Hz.
W ostatnich liniach części setup wszystkie elementy tablicy readings zostaną wypełnione wartościami "0".
W pętli loop, na wstępie sprawdzana jest aktywność na pinie cyfrowym 8, do którego podłączona jest tuba G-M. Jeśli mikrokontroler wykryje wyładowanie lawinowe, do zmiennej eventTime zapisywany jest czas pojawienia się cząstki promieniowania jonizującego.
canceled zostaje zmieniona z "true" na "false".Teraz następuje część skryptu odpowiedzialna za oszacowanie poziomu liczby cząstek promieniowania na minutę (CPM) oraz jej konwersję na wartość dawki pochłoniętej.
Jeżeli aktualny pomiar jest wyższy od najwyższego dotychczas zarejestrowanego, jego wartość jest podmieniana wartością aktualnego pomiaru.
lastEventTime zostaje podmieniona wartością obecnego wyładowania.noCounts oznaczająca liczbę zarejestrowanych cząstek zostaje inkrementowana o "1".total odejmowana jest stara wartość czasu rejestracji cząstki promieniowania o indeksie bieżącego pomiaru,readings o indeksie index.readings dodawana jest bieżąca watość CPM,index określająca element tablicy readings jest powiększana o "1".Jeżeli bieżąca wartość indeksu jest większa bądź równa liczbie elementów tablicy numReadings,
numReadings pomiarów.
analogRead(A0) - power [V]
1023.00 - 5 [V]
power = (analogRead(A0) * 5) / 1023.00
Po wykonaniu niezbędnych obliczeń następuje część skryptu odpowiedzialna za wyświetlanie danych na wyświetlaczu. Na wstępie czyszczony jest bufor wyświetlacza, po czym wyświetlane są następujące parametry:
noCounts,numReadings pomiarów. Jak widać ta zmienna jest wyświetlana dopiero po zarejestrowaniu w tablicy numReadings pomiarów.
W końcowej części skryptu włączono obsługę miękkiego alarmu alarm1. Jeżeli bieżąca wartość CPM jest większa od 850 zliczeń na minutę, sytuacja ta jest sygnalizowana akustycznie i za pomocą czerwonej diody LED.
Skrypt zamyka kontynuacja instrukcji warunkowej z początku skryptu. Jeżeli zmienna logiczna canceled, po zarejestrowaniu cząstki promieniowania jonizującego, ma wartość "false" - czyli przeciwną do ustawionej na początku skryptu wartości początkowej, oraz czas, który upłynął od jej zarejestrowania jest większy od 10ms, to:
cancelTime przypisywana jest wartość aktualnego czasu orazcanceled ponownie zmieniana jest na "true".Układ przechodzi w tryb oczekiwania na rejestrację kolejnej cząstki promieniowania.
(na podst. Libelium Team, 2011)
No dobrze, mamy urządzenie, potrafimy w przybliżeniu określić dawkę przyjętego promieniowania. Pora na ocenę, czy otrzymywane dawki są bezpieczne. Niebezpieczeństwo związane z ekspozycją na źródło promieniowania jonizującego zależy głównie od trzech czynników:
Możliwość pomiaru dawek promieniowania powyżej 0,1μSv/h może dostarczyć nam cennych informacji o anormalnych źródłach skażeń promieniotwórczych. Dzieląc maksymalną roczną dawkę promieniowania przez liczbę godzin w roku otrzymamy maksymalną dawkę promieniowania na godzinę.
(50 000 μSv) / (24 × 365) = 5,70 μSv/h
Dla porównania, średnia godzinna dawka promieniowania na jaką ludzie byli narażenii podczas katastrofy elektrowni jądrowej w Fukushimie 21 marca wynosiła około 7,47μSv/h. Niektóre inne poziomy odniesienia (tylko wartość nominalna, a nie czas ekspozycji!):