The software used to control the scintillator detectors is a two layers
system: the firmware inside the Arduino 2560 controls the hardware and
reads the data; the user interface in the PC let the user interact with
the hardware with a set of commands sent to the microcontroller. It is
written in VBA for Excel, it sends the commands to Arduino and
read the results via Strokereader, an activeX module whose reference is
given below. I'm using Excel because it is easy to develop this kind of
instrument software, it is also very flexible for modifications and
access to the results. Moreover, radioactivity measurement is not a fast
process ( at least with my samples) so there is few activity on the transfer line and a slow baud
rate can even be used. Strokereader is a low cost program easy to
use.
Details of the Excel commands panel. The different buttons restart the
interface, send the commands to the Arduino and start or Stop the
measurement. If needed, the data are printed directly into the sheet (see
below). The small table on the top of the figure describes the
parameters (in black), the parameters values (in blue) and the Arduino
response to confirm the reception (in green). The description of the
commands is displayed on the figure below.
Various commands sent to the Arduino by the
Excel program
or the Arduino serial monitor for testing purposes.
Stop_Time: parameter = value: set the time limit for
counting, example Stop_Time:3600: Discri_1: parameter = voltage1: set the reference voltage
of discriminator 1, example Discri_1:0.3: Discri_2: parameter = voltage2: set the reference voltage
of discriminator 2, example Discri_2:1: File_Name: parameter = FileName: set the filename for SD
card, example File_Name: sample1: Titre: parameter = samplename: set the sample name,
example Titre: pechblende: SD_Card: parameter = ON: or OFF: use SD card for
printout or not Printer: parameter = ON: or OFF: use Excel or
serial monitor for printout or not Counter: parameter = Plastic:NaI: CdWO4: LYSO: Two
Detectors: set the type of detector. Used to format printout. Start: no parameter Stop: no parameter Reset: no parameter
Above is the data table (left part of the Excel sheet) for the
radioactivity results. It can easily be copied elsewhere
for storage and further calculations.
I have
reproduced below the Strokereader routines for illustration only. See
the strokereader web site for reference.
Public
Function Send_Command_Parameter(Parametre) As Boolean
StrokeReader1.Send Parametre + ":"
If StrokeReader1.Error Then
MsgBox StrokeReader1.ErrorDescription
Send_Command_Parameter = False
Else
Send_Command_Parameter = True
End If
End Function
Public Function Read_Parameter(Temps) As String
Dim Response
Response = ""
Intervalle = 2
Temps = Temps / Intervalle
For i = 1 To Temps
Sleep Intervalle
Response = Response + StrokeReader1.Read(Text)
If Right(Response, 1) = Chr(10) Then
Exit For
End If
Next i
Intervalle = Intervalle * (i - 1)
Range("Read_Time").Value = Intervalle
Response = Replace(Response, Chr(13), "") 'Arduino sends <CR><LF>.
Remove <CR> if present
Response = Replace(Response, Chr(10), "")
Read_Parameter = Response
End Function
Public Function Send_Command_Wait_Response(Commande As String, HandShake
As Boolean) As String
Dim Response
Response = ""
Intervalle = 2
Send_Command = ""
StrokeReader1.Send Commande
If StrokeReader1.Error Then
MsgBox StrokeReader1.ErrorDescription
Exit Function
End If
For i = 1 To 1000
Sleep Intervalle
Response = Response + StrokeReader1.Read(Text)
If Right(Response, 1) = Chr(10) Then
Exit For
End If
Next i
Intervalle = Intervalle * (i - 1)
Range("Read_Time").Value = Intervalle
Response = Replace(Response, Chr(13), "") 'Arduino sends <CR><LF>.
Remove <CR> if present
Response = Replace(Response, Chr(10), "")
Range("Commande").Value = Response
Commande = Replace(Commande, ":", "")
If Response <> Commande Then
MsgBox "Erreur Commande"
Else
Send_Command = Response
End If
If HandShake Then
StrokeReader1.Send "Xoff:" + Chr(13) + Chr(10)
End If
End Function
Private Sub StrokeReader1_CommEvent(ByVal Evt As
StrokeReaderLib.Event, ByVal data As Variant)
Dim Reponse As String
Dim Fields, i As Integer, Nb_Fields As Integer
If Counter_Started And Range("Print").Value = "ON" And EVT_DATA Then
Reponse = Read_Parameter(1000)
If Reponse = "Counter_Stopped" Then
StrokeReader1.Connected = False
Range("Status").Value = "Instrument Closed"
Range(Range("Param_Begin"), Range("Param_End")).ClearContents
Counter_Started = False
Exit Sub
End If
Fields = Split(Reponse, ";")
Nb_Fields = UBound(Fields)
If Nb_Fields > 8 Then
Nb_Fields = 8
End If
For i = 1 To Nb_Fields
Range("Buffer").Cells(ICount, i).Value = Fields(i - 1)
If i = 1 Then
Range("Time").Value = Range("Buffer").Cells(ICount, i).Value
End If
Next i
ICount = ICount + 1
Application.ScreenUpdating = True
End If
End Sub
Example of the LCD display.
Program
of the Arduino microcontroller reproduced for illustration only. The
loop routine is reading the serial interface to see if a command is
ready. When the counter is activated, the program send data to
LCD, to SD Card and Excel.
// Micro SD card interface
#include <SPI.h>
#include <SD.h>
#define SD_CS 53
String FileName = ""; // Chip select for micro SD card
File myFile;
//------------------------------------------------------------------------------------------------------------------
// Display interface
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
//Dac interface
#include <Adafruit_MCP4725.h>
Adafruit_MCP4725 Discriminator_1;
Adafruit_MCP4725 Discriminator_2;
int Dac_Value;
float Dac_Voltage;
float Discri_1_Max = 4.938;
float Discri_2_Max = 4.896;
//------------------------------------------------------------------------------------------------------------------
int Secondes; // Time in seconds for scaler
boolean Read_Gamma_Detector = false; // test if counter is started
boolean Printer_Status = true; // Set ON or OFF the printout on serial monitor or Excel program
boolean SD_Card = false; // test if SD card is to be used for printout
String Ligne = ""; // The result line to be printed
int Scaler ; // delay between successive printout
int Scaler_Count = 0; // used to start a printout
String Titre = "Gamma FtLab counter file."; // Sample name
boolean Time_Flag = false; // Flag if stop time parameter is set
int Stop_Time; // counter to test if Stop Time is reached
volatile long interrupt_Counter; // variable incremented by the first interrupt, used by the analog pulses output
long Pulse_Counter; // Store the pulses to be printed after Scaler time
long Pulse_Rate; // interrupt_Counter is loaded into this variable after 60 seconds
volatile long interrupt_Counter_2; // same three variable for interrupts 2 and 3
long Pulse_Counter_2;
long Pulse_Rate_2;
volatile long interrupt_Counter_3;
long Pulse_Counter_3;
long Pulse_Rate_3;
byte interrupt_Pin = 3; // define the 3 interrupts pins
byte interrupt_Pin_2 = 19;
byte interrupt_Pin_3 = 18;
String Counter_Type = "Plastic"; // counter type to be used, default is plastic
unsigned long milli_Begin; // Start and stop timer variables
unsigned long milli_End;
String Discri1 = ""; //Discriminator voltage for printout
String Discri2 = "";
//------------------------------------------------------------------------------------------------------------------
void software_Reset()
{
asm volatile (" jmp 0");
}
void setup()
{
//Arduino Initialize
Serial.begin(9600);
Serial.print("SD card ");
if (!SD.begin(SD_CS)) //Test the presence of SD card
{
Serial.println("initialization failed!");
}
else
{
Serial.println("initialization OK.");
}
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Scaler = 5;
lcd.init(); // initialize the lcd
lcd.backlight();
lcd.noAutoscroll();
lcd.setCursor(0,0);
lcd.print("Gamma counter");
pinMode(interrupt_Pin,INPUT); // Set interrupts pins
pinMode(interrupt_Pin_2,INPUT);
pinMode(interrupt_Pin_3,INPUT);
Discriminator_1.begin(0x60); // Set reference voltage to the discriminators
Discriminator_1.setVoltage(0, false);
Discriminator_2.begin(0x61);
Discriminator_2.setVoltage(0, false);
}
// 3 interrupt routines for the output counters
void Pulse_Detected()
{
interrupt_Counter ++;
}
void Pulse_Detected_2()
{
interrupt_Counter_2 ++;
}
void Pulse_Detected_3()
{
interrupt_Counter_3 ++;
}
void loop()
{
String Command = "";
boolean wait_read;
String parameter;
Serial.flush();
if (Serial.available() > 0)
{
Command = Serial.readStringUntil(':'); // Use of colon (:) as a separator to indicate an end of string
if (Command == "Start") // Start all counters
{
Scaler_Count = 0;
Secondes = 0;
Pulse_Rate = 0;
Pulse_Rate_2 = 0;
Pulse_Rate_3 = 0;
interrupt_Counter = 0;
interrupt_Counter_2 = 0;
Pulse_Counter = 0;
Pulse_Counter_2 = 0;
Pulse_Counter_3 = 0;
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print(" Time: ");
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print("Discri 1 Discri 2");
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print(" ");
if (SD_Card)
{
if (FileName == "")
{
Serial.println("No file Name!");
SD_Card = false;
}
else
{
if ( SD.exists(FileName))
{
Serial.println("File exist!");
SD_Card = false;
}
else
{
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open(FileName, FILE_WRITE);
if (myFile)
{
myFile.print("Sample: "+Titre + "; Stop Time = ");
myFile.print(Stop_Time);
myFile.println(";Discri1 = " + Discri1 +";Discri2 = "+Discri2);
myFile.println("Time(secondes) ;Discrim. Low;Discrim. High;Scaler count");
}
}
}
}
if ( Counter_Type == "Plastic" || Counter_Type == "NaI" || Counter_Type == "LYSO")
{
attachInterrupt(digitalPinToInterrupt(interrupt_Pin), Pulse_Detected, RISING);
attachInterrupt(digitalPinToInterrupt(interrupt_Pin_2), Pulse_Detected_2, FALLING);
}
if ( Counter_Type == "CdWO4" )
{
attachInterrupt(digitalPinToInterrupt(interrupt_Pin_3), Pulse_Detected_3, FALLING);
}
if ( Counter_Type == "Two Detectors")
{
attachInterrupt(digitalPinToInterrupt(interrupt_Pin), Pulse_Detected, RISING);
attachInterrupt(digitalPinToInterrupt(interrupt_Pin_2), Pulse_Detected_2, FALLING);
attachInterrupt(digitalPinToInterrupt(interrupt_Pin_3), Pulse_Detected_3, FALLING);
}
interrupt_Counter_2 = 0;
interrupt_Counter = 0;
interrupt_Counter_3 = 0;
milli_Begin = millis();
Secondes = 0;
Read_Gamma_Detector = true;
Serial.println("Counter Started");
delay(500);
if ( Printer_Status )
{
Serial.print(Titre);
Serial.println(";");
delay(500);
if ( Counter_Type == "Plastic" || Counter_Type == "NaI" || Counter_Type == "LYSO")
Serial.println("Time(Seconds);Discrim. Low;Discrim. High;");
if ( Counter_Type == "CdWO4" )
Serial.println("Time(Seconds);Discrim.CdWO4;");
if ( Counter_Type == "Two Detectors")
Serial.println("Time(Seconds);Discrim. Low;Discrim. High;Discrim.CdWO4;");
}
}
if (Command == "Stop") // Manual stop of all counters
{
Read_Gamma_Detector = false;
// close the file:
myFile.println("Manual stop");
myFile.close();
detachInterrupt(digitalPinToInterrupt(interrupt_Pin));
detachInterrupt(digitalPinToInterrupt(interrupt_Pin_2));
detachInterrupt(digitalPinToInterrupt(interrupt_Pin_3));
Serial.println("File closed. Gamma Counter Stopped.");
//Serial.println("OK");
}
if (Command == "Reset") // reset microcontroller
{
software_Reset();
}
if (Command == "Scaler") // set delay time between each printout
{
wait_read = true;
while (wait_read)
{
if (Serial.available() >0)
{
wait_read = false;
parameter = Serial.readStringUntil(':');
//Serial.println(parameter);
if (parameter.toInt() > 0)
{
Scaler = parameter.toInt();
Serial.print("Scaler = ");
Serial.println(Scaler);
}
else
{
Serial.println("Parameter Error!");
}
}
}
}
if (Command == "Counter") // Define the counter type used
{
wait_read = true;
while (wait_read)
{
if (Serial.available() >0)
{
wait_read = false;
parameter = Serial.readStringUntil(':');
//Serial.println(parameter);
if (parameter == "Plastic" || parameter == "NaI" || parameter == "CdWO4" || parameter == "LYSO"|| parameter == "Two Detectors")
{
Counter_Type = parameter;
Serial.print("Detector = ");
Serial.println(Counter_Type);
}
else
{
Serial.println("Parameter Error!");
}
}
}
}
if (Command == "Stop_Time") // Total counting time
{
wait_read = true;
while (wait_read)
{
if (Serial.available() >0)
{
parameter = Serial.readStringUntil(':');
wait_read = false;
if (parameter.toInt() > 0)
{
Stop_Time = parameter.toInt();
Time_Flag = true;
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print("Max Time: ");
lcd.setCursor(11,0);
lcd.print(Stop_Time);
Serial.print("Stop Time = ");
Serial.println(Stop_Time);
}
else
{
Serial.println("Parameter Error!");
}
}
}
}
if (Command == "Discri_1") // Set discriminator 1 Voltage
{
wait_read = true;
while (wait_read)
{
if (Serial.available() >0)
{
parameter = Serial.readStringUntil(':');
Discri1 = parameter;
wait_read = false;
Dac_Voltage = parameter.toFloat();
if ((Dac_Voltage >= 0) && (Dac_Voltage < Discri_1_Max))
{
Serial.print("Discri 1 voltage = ");
Serial.println(Dac_Voltage,3);
Dac_Voltage = Dac_Voltage / Discri_1_Max * 4095 ;
Dac_Value = (int) Dac_Voltage;
Discriminator_1.setVoltage(Dac_Value, false);
}
else
{
Serial.println("Parameter Error!");
}
}
}
}
if (Command == "Discri_2") // Set discriminator 2 Voltage
{
wait_read = true;
while (wait_read)
{
if (Serial.available() >0)
{
parameter = Serial.readStringUntil(':');
Discri2 = parameter;
wait_read = false;
Dac_Voltage = parameter.toFloat();
if ((Dac_Voltage >= 0) && (Dac_Voltage < Discri_2_Max))
{
Serial.print("Discri 2 voltage = ");
Serial.println(Dac_Voltage,3);
Dac_Voltage = Dac_Voltage / Discri_2_Max * 4095 ;
Dac_Value = (int) Dac_Voltage;
Discriminator_2.setVoltage(Dac_Value, false);
}
else
{
Serial.println("Parameter Error!");
}
}
}
}
if (Command == "File_Name") // Set filename for SD card
{
wait_read = true;
//Serial.println(Command);
while (wait_read)
{
if (Serial.available() >0)
{
FileName = Serial.readStringUntil(':');
FileName = FileName + ".csv";
wait_read = false;
if ( SD.exists(FileName) )
{
Serial.println("File exist!");
}
else
Serial.println("File Name " + FileName + " OK");
}
}
}
if (Command == "Titre") // General purpose info, most of the time used for the rock or mineral name.
{
wait_read = true;
//Serial.println(Command);
while (wait_read)
{
if (Serial.available() >0)
{
Titre = Serial.readStringUntil(':');
wait_read = false;
myFile.println(Titre);
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print(Titre.substring(0, 19));
Serial.print("Titre = ");
Serial.println(Titre);
}
}
}
if (Command == "Printer") // Set printout ON or OFF
{
wait_read = true;
//Serial.println(Command);
while (wait_read)
{
if (Serial.available() >0)
{
parameter = Serial.readStringUntil(':');
wait_read = false;
if ( parameter == "ON" )
{
Serial.println("Printer ON");
Printer_Status = true;
}
else
{
Serial.println("Printer Off");
Printer_Status = false;
//Serial.println("OK");
}
}
}
}
}
if (Command == "SD_Card") // Set SD card ON or OFF
{
wait_read = true;
while (wait_read)
{
if (Serial.available() >0)
{
parameter = Serial.readStringUntil(':');
wait_read = false;
if ( parameter == "ON" )
{
Serial.println("SD_Card ON");
SD_Card = true;
}
else
{
Serial.println("SD_Card Off");
SD_Card = false;
//Serial.println("OK");
}
}
}
}
// Executed after command "Start" has been sent, read the data from all counters and send results to Excel or SD card.
if (Read_Gamma_Detector)
{
milli_End = millis();
if ((milli_End - milli_Begin) >= Scaler * 1000) // Generate the results after the Scaler time
{
// Gestion pulse counter
milli_Begin = milli_End;
Pulse_Rate = interrupt_Counter;
Pulse_Rate_2 = interrupt_Counter_2;
Pulse_Rate_3 = interrupt_Counter_3;
interrupt_Counter =0;
interrupt_Counter_2 =0;
interrupt_Counter_3 =0;
Pulse_Counter = Pulse_Counter + Pulse_Rate;
Pulse_Counter_2 = Pulse_Counter_2 + Pulse_Rate_2;
Pulse_Counter_3 = Pulse_Counter_3 + Pulse_Rate_3;
Secondes = Secondes + Scaler;
// Data sent to small LCD display
lcd.setCursor(8,0);
lcd.print(" ");
lcd.setCursor(8,0);
lcd.print(Secondes);
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,2);
lcd.print(Pulse_Rate);
if ( Counter_Type == "Plastic" || Counter_Type == "NaI" || Counter_Type == "LYSO")
{
lcd.setCursor(10,2);
lcd.print(" ");
lcd.setCursor(10,2);
lcd.print(Pulse_Rate_2);
}
if ( Counter_Type == "Two Detectors" || Counter_Type == "CdWO4" )
{
lcd.setCursor(10,2);
lcd.print(" ");
lcd.setCursor(10,2);
lcd.print(Pulse_Rate_3);
}
lcd.setCursor(0,3);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print(Pulse_Counter);
if ( Counter_Type == "Plastic" || Counter_Type == "NaI" || Counter_Type == "LYSO")
{
lcd.setCursor(10,3);
lcd.print(" ");
lcd.setCursor(10,3);
lcd.print(Pulse_Counter_2);
}
if ( Counter_Type == "Two Detectors" || Counter_Type == "CdWO4" )
{
lcd.setCursor(10,3);
lcd.print(" ");
lcd.setCursor(10,3);
lcd.print(Pulse_Counter_3);
}
// Print to PC, Excel or Arduino monitor
if ( Printer_Status )
{
Serial.print(Secondes);
Serial.print(" ; ");
if ( Counter_Type == "Plastic" || Counter_Type == "NaI" || Counter_Type == "LYSO")
{
Serial.print(Pulse_Rate);
Serial.print(" ; ");
Serial.print(Pulse_Rate_2);
Serial.println(" ; ");
}
if ( Counter_Type == "CdWO4" )
{
Serial.print(Pulse_Rate_3);
Serial.println(" ; ");
}
if ( Counter_Type == "Two Detectors" )
{
Serial.print(Pulse_Rate);
Serial.print(" ; ");
Serial.print(Pulse_Rate_2);
Serial.print(" ; ");
Serial.print(Pulse_Rate_3);
Serial.println(" ; ");
}
}
// Data sent to SD card if activated
if (SD_Card)
{
if ( Counter_Type == "Plastic" || Counter_Type == "NaI" || Counter_Type == "LYSO")
Ligne = String(Secondes)+ " ; " + String(Pulse_Rate)+ " ; " + String(Pulse_Rate_2);
if ( Counter_Type == "CdWO4" )
Ligne = String(Secondes)+ " ; " + String(Pulse_Rate_3);
if ( Counter_Type == "Two Detectors" )
Ligne = String(Secondes)+ " ; " + String(Pulse_Rate)+ " ; " + String(Pulse_Rate_2) + " ; " + String(Pulse_Rate_3);
if (myFile)
{
myFile.println(Ligne);
myFile.flush();
}
}
// Stops all counters after "Sop_Time"
if (Secondes >= Stop_Time)
{
detachInterrupt(digitalPinToInterrupt(interrupt_Pin));
detachInterrupt(digitalPinToInterrupt(interrupt_Pin_2));
detachInterrupt(digitalPinToInterrupt(interrupt_Pin_3));
Read_Gamma_Detector = false;
// close the file:
myFile.close();
delay(500);
Serial.println("Counter_Stopped");
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print("Counter Stopped");
}
}
}
}