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!):