Three Mile Island, Chernobyl, Fukushima... These names remind us that the promise of nuclear power and the atom unleashed can turn to peril and environmental disaster in a matter of minutes. The threat of radioactive dirty bombs or outright global thermonuclear war further multiples the peril factor of nuclear power and our use of the atom.
In the past, miners brought canaries into mines. The canaries would show the presence of toxic gases in mines long before they could effect the miners. Hence the term, "canary in a coal mine." The gamma canary is a small, solar powered field deployable radiation sensing device. Much like the miners' canaries, it can act as an early warming sensor for the presence of ionizing radiation.
The gamma canary not only senses the presence of ionizing radiation, but it also transmits its data over LoRa to an WiFI/LoRa gateway base station. Distance and shiedling are essential elements of radiation protection and the long range transmission capability provided by LoRa provides the distance component!
Using the PubNub real time data network, we can share this data all around the world, even with SMS and Twitter! Yes, this electronic canary really tweets!
The gamma canary's enclosure and design is unique because we wanted to focus not just on the land based, portable application of radiation monitoring, but also sea or water borne monitoring. Due to the location of the Fukushima reactors, there was significant land, air and sea based contamination. In fact, some research groups have used the sea based contamination to explore their ocean current models!
Canadian scientists track Pacific Ocean currents... using Fukushima radiation.
In a littoral or land deployment, during the acute phases of a reactor incident, multiple gamma canaries could be dropped in location, sending data back to a command center allowing first responders, government officials and clean up personnel and citizens to monitor contamination levels from a safe distance. They can use the data returned on their real time dashboard to plan their response, marshal resources and direct evacuation at a safe distance from the event. Experts and citizens from around the globe can also monitor the situation real time on their own dashboard given a PubNub subscription key!
For those living near a nuclear reactor or spent fuel disposal site, the gamma canary can act as your own personnel safety device in case of a breakdown of communications between reactor officials, municipalities and you.
Of course, the standard warning applies: This is a prototype, proof-of-principle device. It has neither been been tested, nor licensedfor thesemission critical, human health, personalsafety,etc. purposes. It should not be used for such purposes.But as makers, we can dream up,create,experimentwith and demonstrate what might be!
The overall design if the gamma canary:
The gamma canary is a field deployable environmental sensor for gamma radiation. It has geolocation with GPS and communicates from the field to the Internet via LoRa and a LoRa/WiFi gateway to communicate realtime data on PubNub powered real time dashboard.
ElectronicsIn our design, we took advantage of 3 features of the MAX32620FTHR board. The form factor is pin-to-pin compatible with the adafruit feather layout. This allowed us to select from a wide range of components and break out boards, aka feather wings, already available. We utilized the PMOD connector to provide us with a reliable and durable connection point for our radiation sensor. And finally, we utilized the onboard MAX17055 fuel gauge IC to provide a real time assessment of the battery state of charge, which is essential for monitoring the health of field deployed units!
Main Payload CircuitThis is basic overview of how we put the two electronic components of our project together, the main electronics/sensor payload and the LoRa/WiFi gateway.
We soldered stacking headers to the MAX32620FTHR and other feather wings. These make is quite easy to interact with the boards and they really can be stacked, one on top of another! There are several ways to solder these headers, the picture below show a way we think works best for us.
The MAX32620FTHR like all boards with a adafruit feather form factor make it very easy to transform create and test a circuit, while making a deployable prototype at the same time. In many cases, with the use of stacking headers and a really neat component called a FeatherWing Tripler Mini Kit, you can skip the breadboard stage altogether!
The tripler carries the pin out assignments of the feather board laterally 3 board widths long and with the use of stacking headers, you can build your projects not only horizontally but vertically as well! The connections made by plugging into the headers are reliable and remain in place with field deployment.
The PMOD connectors available on the MAX32620FTHR provide another source of reliable and strong points of connection for components. For prototyping individual pin connections, the stacking header sockets are great, but they are not as reliable when moving to a field testing scenario. The PMOD connector comes into play here. With some special connector cables, we easily made a secure and reliable connection to our radiation sensor
The final step is to attach a power supply. Our main store of of power is a single 3.7v, 1200 mAh LiPo battery with a solar panel and adafruit USB / DC / Solar Lithium Ion/Polymer charger for charging.
There are quite a bit of exciting electronics components used in the gamma canary. We chose to focus on 3 of these, the MAX17055 fuel gauge IC, the "Pocket Geiger", the LoRa FeatherWing, and yp highlight some of the things we found very interesting about them below.
Battery Characterization with the MAX17055 Fuel Gauge ICMuch like the fuel gauge on a car's dashboard, a battery fuel gauge provides information regarding the amount of fuel or charge left in your battery. One of the important measures that the MAX17055 provides is the State of Charge(SOC). This is ratio of charge remaining in the battery over total capacity and represented as a percent, factors such as battery age, and health amongst many other can affect this.
Battery characterization is the process by which parameters such as SOC of a battery are determined. This process can be quite complex and battery specific. Maxim Integrated has developed a patented algorithm and implemented an IC to simpilfy this process and allow batteries to be characterized "in the field."
This is what the MAX17055 does and it is very easy to use and provides very useful information for embedded, IoT and field deployed devices. Without the benefit of a plug in a house, field deployed devices can quickly run out of power. Even with solar power charging, what will happen during prolonged overcast periods or during the night?
Maxim describes a fuel gauge as such:
A battery fuel gauge, also known as a battery gas gauge, determines battery state-of-charge (SOC) and state-of-health and predicts how much longer, under specific operating conditions, the battery can continue to provide power. Inadequate battery life and unreliable battery SOC reporting are some of the most common complaints about any battery-powered device. Highly accurate battery fuel gauges are needed to avoid surprise shutdowns and are critical for a positive user experience.
The patented Maxim algorithim is called ModelGauge m5:
The ModelGauge and ModelGauge m3 algorithms require data obtained from a factory-based battery characterization process. ModelGauge m5, however, allows field configuration using a software interface that allows the user to provide basic battery (pack) information.
So makers and other DIY types can take advantage of this for their projects without the benefit of a full fledged electrical engineering department and testing lab!
More excellent info on this can be found at:
For our project we use the information derived from the MAX17005 is two ways:
- provide a continual, live SOC updates on our real time dash board
- a low battery alarm system to send a mayday tweet and SMS
For our first run through with this unique technology we used the MAX17055 driver and example for onboard MAX17055 Fuel Gauge driver from Maxim Integrated, to get the data from the MAX17055. This is all done over I2C!
The code will wait for 3 minutes after startup to prevent an inadvertent battery low warning. This is because initially, before the modelgauge algorithm has time to characterize the battery, it may issue 0% SOC reading. We don't want to issue spurious alarms, so we set a timer to wait for 3 minutes. After 3 minutes, the timer expires and allows the emergency read to occur, if this time has expired AND we are not already in the battery emergency state. Why? Since we will be issuing SMS and tweets as mayday signals, we don't want to abuse twitter and ClickSend by sending many useless messages, especially in the testing phase.
...
Timer batteryEmergencyPause; // avoid sending alert right after starting unit up!
bool startBatteryEmergency;
bool inBatteryEmergencyState;
#define EMERBEGIN 180000 // 3 minutes in millissecond after setup runshghhg
...
Here in the "setup()" section of our code, we set our state variables and start the Timer to prevent spurious battery low emergency warnings.
...
int main()
{
...
startBatteryEmergency = false;
inBatteryEmergencyState = false;
batteryEmergencyPause.start();
...
while(1) {
...
// special 1 time timer to begin battery monitoring for emergency lo battery warning
if( batteryEmergencyPause.read_ms() >= EMERBEGIN )
{
batteryEmergencyPause.stop();
startBatteryEmergency = true;
}
The state variables are used within the regular battery monitoring code to determine if the emergency mayday signal should be sent to the gateway:
void readFuelGauge()
{
max17055.v_cell(&batteryVoltage);
printf("%6.3fV ", batteryVoltage);
...
if (startBatteryEmergency and !inBatteryEmergencyState)
{
pc.printf("emergency battery monitoring set\n");
if (stateOfCharge < 51.0)
{
inBatteryEmergencyState = true;
batteryLowTransmit();
}
}
printf(" SOC:%d\\% ",stateOfCharge);
...
The batteryLowTransmit() function is called, extends the intervals of data transmission to save power and then sends the mayday/battery low power warning.
void batteryLowTransmit()
{
/** broadcast warning low battery/increased transmisson intervals **/
// extend transmission intervals to conserve power
LOCATION_INTERVAL = 150.0;
CPM_INTERVAL = 600.0;
FUEL_INTERVAL = 900.0;
// Transimet Battery/Fuel Gauge data: voltage, current, temp
LoRa.beginPacket();
// Packet Header
LoRa.printf("%c",LOCALADDRESSID); LoRa.printf("**");
LoRa.printf("%c",LOWBATTERY); LoRa.printf("**");
LoRa.printf("%d",stateOfCharge);LoRa.printf("**");
LoRa.endPacket();
pc.printf("LoRa Low Battery Packet Sent\n");
}
The message is parsed on the Python side of things and 3 SMS and 3 tweets are sent out as a "mayday"
def parseBatteryLow(msg):
# this is an "emergency" situation just msg out the problem
for x in range(0,3):
msgLine0 = str(x)
msgLine1 = ' BAT LOW! '
msgLine2 = 'LAST LAT: {:04.5f}, LON: {:04.5f}\n'.format(currentLocation[0],currentLocation[1])
emergencyMsg = msgLine0 + msgLine1 + msgLine2
sms = { "to" : "1
tweetMsg = { "tweet" : emergencyMsg }
pubnub.publish().channel('gamma-canary-tweet').message(tweetMsg).async(publish_callback)
pubnub.publish().channel('cpm-text-alert').message(sms).async(publish_callback)
We will cover the details on how we enabled tweeter and ClickSend sms using PubNub below, but for now take a look at what this looks like on your phone and the gamma canaries' twitter feed:
In the future we will not only extend the intervals of transmission, but also disable the GPS and wake it up periodically with the RTC due to the limitations on the length of time intervals for Timers in Mbed OS.
Radiation DetectionMost every one is familiar with Geiger counters. These involve the use of a glass tube filled with inert gas separating two high voltages terminals. An ionizing particle flies through the chamber, charges the inert gas allowing it to conduct an electrical current which is then transferred into a reading, or "click." Geiger counters in kit form are available, such as, Geiger Counter Kit - Radiation Sensor, but the circuit requires a glass tube, high voltage and are relatively expensive. These factors make this less than ideal for a maker, field deployable project.
So we turned to the Pocket Geiger Radiation Sensor - Type 5 distributed by Sparkfun. The kit form is meant to be put together and warn on your person and attach to your cell phone for personnel radiation detection.
It is a bit of a misnomer to call it a "Pocket Geiger", as it is based on a solid state photo sensor rather than a geiger-mueller tube, but it is very light weight, durable and can be easily interfaced with microcontroller interrupt pins! It is ideal for our maker project field deployment situation where, size, weight, durability and cost matter. There is a significant amount of documentation in Japanese, English and Dutch to learn from!
A photodiode is a semiconductor device that converts light into an electrical current. The current is generated when photons are absorbed in the photodiode.
To keep it very simple, gamma rays are high energy photons and when they strike the silicon, they cause a current to be generated and amplified and sent as a interrupt to the microcontroller. This is due to something involving the photoelectric effect discovered by some Einstein guy...
As seen above, we used the PMOD connector on the MAX32620FTHR and a PMOD cable to make a secure and reliable connection between the sensor and our board. Because the sensor is sensitive to vibration, there is also a noise pin. Both the signal pin and noise pin are hooked up to interrupt capable pins. The interrupts count the signals received at each of these pins of a 1 minute time period and subtract noise from signal to derive a CPM or count per minute reading. In order to evaluate the sensor and our adaptation of the code to run it, we tested it against a known source. Both our testing against a known radioactive source and the code for working with interrupts are described below!
Wiring the LoRa FeatherWing:The Adafruit LoRa FeatherWing is designed to be compatible with multiple platforms and when there might be pin conflicts with other components and wings. The picture below shows how to wire jumpers up to the LoRa feather wing to be compatible with this project. While this picture shows the LoRa/WiFi gateway component of this project, the wiring is the same for the gamma canary payload portion as well.
A. The the MAX32620FTHR and requires no wiring.
B. Is the adafruit Feather wing. The legend to the left indicates how to wire, the IRQ pin is connected by the blue wire to PIN D, CS by the yellow wire to B and RST to PIN D by the white wire.
C. The bare LoRa feather wing so you can see the origin and insertion points of the jumper wires.
MAX32620-RPi3 LoRa/WiFi GatewayIn order to make this project a true IoT project, we need a way to get our field based LoRa transmissions from the gamma canary to the internet. For this we created a gateway that will take our LoRa data packets and send them via WFi to the internet.
We already know how to Tx & Rx LoRa with the MAX32620FTHR and we have a few Raspberry Pi boards around that we have used to transmit data to the internet. Would there be a simple way to combine these two?
The answer of course is, yes! The simplest way we found to do this was to connect the 2 over UART Serial to form our gateway. This way the MAX32620FTHR/LoRa part of the circuit can Tx and Rx LoRa data packets to LoRa enabled nodes in the field and we can take advantage of the power of the RPi3 and Python to connect to the Internet!
The serial port on the Rpi3, there is one accessible, and is easily accessed with the Python serial library, after some configuration issues are worked out:
Configuring The GPIO Serial Port On Raspbian Jessie and Stretch Including Pi 3
Click the link to read about it! The gateway code for both the MAX32620FTHR and RPi3 sides is described below. Remember to cross the Rx/Tx from the MAX32620FTHR side and the RPi3 side!
Project EnclosureFor a project like this, the enclosure needs to be more than a "box" to house the electronics. It has to be durable, water resistant and of course look good!
Our first enclosure to perform some rudimentary field testing(see below) was simply an adafruit box, which we save for such purposes!
I designed this enclosure to be able to hold all the electronics in the smallest package possible, while still being able to float, and prevent water from spilling in. This enclosure was designed with most of its weight low and centralized, much like a buoy in the ocean, to help it stay upright. Additionally, I added spaces on the sides of the enclosure, much like a ballast tank on a submarine, however these were not meant to fill with water. This would help keep the enclosure above the waterline, so it did not sink to the bottom.
The first few revisions of enclosure came with some problems. Most were small measurement issues, like increasing the length of the radiation sensor cover, or creating more depth for the antenna to fit in, however there were some bigger issues like keeping the cover pieces down and making the GPS fit better. These changes came in revision two of the whole enclosure where I added in two more places for the cover to screw in, and a slot for the GPS to rest snuggly in place.
I intend to pursue this project further, and make many more revisions to the enclosure. I want to start off by making the bottom electronics package hydrodynamic. That part is square-ish right now, and doesn't flow nicely in water. Also, I would like to further investigate if there is a better way to print the pieces, maybe simplify it down into two pieces instead of three, making the package easier to put together. Another possible change I could make is adding a sail to the enclosure to make it steerable. This could help the enclosure move to different places in water, instead of sitting in the same place waiting for the waves to push it.
All enclosure components of the gamma canary were printed on a lulzbot mini with 3mm yellow HIPS filament.
Putting It All TogetherAll the files and revisions of the enclosure to date on our our projects github repo, lachendeKatze/canary/ensclosure, and can be seen in the attachments section below.
SoftwareThere are four major software components that make up gamma canary:
- gammaCanary01, running on the canary embedded MAX32620FTHR
- lora-callback-rx, running on the LoRa/WiFi gateway MAX32620FTHR
- gateway.py, script running on the LoRa/WiFi gateway RPi3
- gammaCanaryDashboard.html, the CSS/HTML/Javascript real-time dashboard
All our code is available at the projects github repository. The MAXFTHR32620 is also available on the Mbed OS repo.
We'll highlight some of the interesting features of each software component below.
The MAX32620FTHR can be programmed in several different environments. We started this project before we realized there was a BSP for the arduino IDE. So all the MAX32620FTHR code is written with the online Mbed OS compiler:
Arm Mbed OS is a free, open-source embedded operating system designed specifically for the "things" in the Internet of Things.
In the end, this turned out to be a great "choice", as we got a chance to learn a great new programming environment and OS for embedded projects!
All our previous embedded microcontroller code has been written with the Arduino IDE both local and online. The Mbed OS complier is comparably easy to use, fully featured. No problem adapting to this environment at all, as you can see from the left hand column! Mbed OS has all the essential libraries necessary to work with the MAXFTHR32620 just like the Arduino IDE has for its supported boards.
One difference is you will need a serial terminal program in order to see output from the MAX32620FTHR serial output for things such as debugging purposes. We used the program CoolTerm for this. The Mbed OS/Complier documentation also recommends CoolTerm:
Tip: We recommend using CoolTerm, as it works the same on Windows, Linux and OS X. Here is a handy video on how to use CoolTerm to connect to your board and view the printf()
statements. For more information, see our serial communication tutorials.
The gamma canary hardware payload is run by an embedded "script". In order to interface with our adafruit GPS feather wing, LoRa transceiver feather wing and MAX17055 fuel gauge, we took advantage of the many libraries already available on repositories in Mbed ecosystem!
The LoRa library is a port of Sandeep Mistry's arduino-LoRa code. We were very excited to find a port of his code, to the Mbed OS environment. There are other LoRa libraries to check out and use, some with more features, etc. However, we used Sandeep's BLE code for several Arduino101 projects, and it has been our experience that his code is elegant, well supported, easy to use and just works! This LoRa library is the same!
The LoRa transceiver used in our project is supported by his code over the SPI interface. In order to use Mohammed Fadhlika's port, we just had to change SPI pin assignments.
Original LoRa.h:
#ifndef LORA_H
#define LORA_H
#include "mbed.h"
#define LORA_DEFAULT_SS_PIN PA_4
#define LORA_DEFAULT_RESET_PIN PA_2
#define LORA_DEFAULT_DIO0_PIN PA_3
Ported for MAX32620FTHR:
#ifndef LORA_H
#define LORA_H
#include "mbed.h"
#define LORA_DEFAULT_SS_PIN P5_4
#define LORA_DEFAULT_RESET_PIN P5_5
#define LORA_DEFAULT_DIO0_PIN P3_3
In the LoRa.cpp, the SPI object to be used by the LoRa transceiver is declared using the Mbed OS standard SPI library call, which requires specifying the pins for your board:
SPI spi(MOSI_PIN, MSIO_PIN, SCLK_PIN); // mosi, miso, sclk
Original LoRa.cpp:
SPI _spi(PA_7, PB_4, PA_5);
Ported for MAX32620FTHR:
SPI _spi(P5_1, P5_2, P5_0);
That's it, we are now ready to use the library!
LoRa will allow us to transmit and receive messages and Sandeep's github repo has some easy to learn from examples. The library takes advantage of the printf() function to transmit messages, so formatting is easy. We just have to decide on a format for those messages. LoRa does not specify what your transmission mean.
So we will format our messages as such:
NODEID**DATAPACKETTYPE**DATA_0**DATA_1**...DATA_N**
The first time items establish the node source id and the type of data being sent. This is followed by N data values. The "**" will help separating or tokenizing the data packet on the python LoRa/WiFI gateway side of our LoRa network.
We start by assigning some codes to establish identity of the transceiver nodes and the type of data packet being sent:
/************************************************************************/
/***************************** LoRa TxRx ********************************/
/************************************************************************/
#define GATEWAYID 0x30 // LoRa Gateway ID
#define LOCALADDRESSID 0x31 // this gamma canary ID
#define GPSDATA 0x40
#define CPMDATA 0x41
#define FUELDATA 0x42
After this, we set up timing parameters and functions for transmitting, more on this below. For now, lets stay on message! ;)
In our main() function, but before our while(1) infinite loop, we perform our LoRa setup and initialization:
/** 1. LoRa TxRx initialization **/
if (!LoRa.begin(915E6)) {pc.printf("LoRa not found!");}
else { pc.printf("LoRa started...\n");}
. . .
transmissionTicker.attach(&transmitLoRaTicker,1.0);
And here's how we transmit some data, in this case the GPS location data:
/** LoRa TxRx Functions **/
void transmitLocation()
{
// transmite GPS/Location data, lat/lon
LoRa.beginPacket();
// Packet Header
LoRa.printf("%c",LOCALADDRESSID); LoRa.printf("**");
LoRa.printf("%c",GPSDATA); LoRa.printf("**");
// Packet Contents
LoRa.printf("%f",currentLatitude);LoRa.printf("**");
LoRa.printf("%f",currentLongitude);LoRa.printf("**");
LoRa.endPacket();
pc.printf("LoRa GPS Packet Sent\n");
}
Using the printf() to send our data package makes LoRa transmission just like printing a debug message/string to the serial port!
Our LoRa/WiFi Gateway is composed of two software components, lora-callback-rx, running on the LoRa/WiFi gateway MAX32620FTHR and gateway.py, script running on the LoRa/WiFi gateway RPi3.
The lora-callback.rx is simple, it receives the data packet from the canary and sends over a serial connection to the RPI3! We again use the LoRa library and take advantage of the Mbed OS UARTSerial library, specifing the RxTx pins from UART2 with 9600 baud rate on the MAXFTHR32620.
#include "mbed.h"
#include "LoRa.h"
#include "UARTSerial.h"
...
Serial _serial(P3_1, P3_0,9600);
A callback function that is executed when a LoRa message is received is then specified.
void onReceive(int packetSize)
{
idle = false;
char msg[45];
pc.printf("PACKET RECEIVED, SIZE:%d \n", packetSize);
...
for (int i = 0; i<packetSize; i++)
{
if ( i < 45)
{
msg[i] = (char)LoRa._getc();
}
receiving = !receiving;
}
if (msg[0] == '1')
{
// indicates this message comes from the canary!
pc.printf("received from 0\n");
_serial.printf("%s\n", msg);
}
else {
pc.printf("dont know who this was from\n");
}
...
We take in the data packet, parse it into an array of characters of size 45. We protect our array by ensuring we only fill the array to a size of 45:
if ( i < 45)
{
msg[i] = (char)LoRa._getc();
}
Garbage data, if received, might blow this array up, and crash this end of the gateway. With this approach, even if garbage data is sent in excess of 45 chars, the gateway stays up and we take advantage of the powerful python try/except clauses to take care of garbage data.
The pc.printf() functions are for debugging and monitoring if messages being sent to the gateway. The _serial.printf("%s\n", msg); statement sends the message array to the RPi3 and then onto the internet! Simple, right?
Let's talk about interrupts, timers and tickers first, then move on to the rest of the software. Each radioactive element has a characteristic rate of decay known as it's half-life, however an individual decay event is unpredictable. As gamma rays randomly strike the sensor, they will generate an signal event in an unpredictable manner. This basically sums up the reason interrupts were invented!
Many if not all of the GPIO pins on the MAX32620FTHR are able to handle interrupts, we just have to enable the pin to do so. In this case we will need 2 pins, one for signal and the other for noise and we will also need to do something when an interrupt occurs.
So we create 2 Interrupt Service Routines(ISR). The most fundamental rule of ISRs is to do as little as possible inside the ISR. An interrupt brings everything on a processors to a halt. The regular program logic comes to a halt. And you can see, there is a lot going on in this program overall. LoRa transmission, GPS reads, timers, tickers...etc. So we don't want to interrupt things for long.
In this case we just advance a counter for signals and noise. For testing purposes, we also flip the LED1, but this is just for debugging purposes and can be removed for deployment.
InterruptIn signalPin(P1_0);
InterruptIn noisePin(P1_1);
// interrupt service routines(ISR) which count are called whenever signal or noise pins are triggered
void signalISR(){ signalCounter++; led1 = !led1;}
void noiseISR(){ noiseCounter++; }
P1_0 and P1_1 are GPIO pins on the PORT1 PMOD connecter, which is where our radiation sensor is connected, and these pin are now set to receive interrupts. In our main function, in the setup section before the while(1) loop, we connect the ISRs to the interrupt pins. They will trip on a rising value/
signalPin.rise(&signalISR); // attach ISR function to the rising edge
noisePin.rise(&noiseISR); // attach ISR function to the rising edge
And that's it, when a signal or noise pulse comes in, it triggers an interrupt which just advances a counter. But how does this turn into a Count Per Minute or CPM? That's where timers come in. First we create a Timer object which trip every 60 seconds, or 1 minute(Counts Per Minute) and create some variables to store our counts. Further one, in our setup area we start the cpmTimer object.
#define CPM_REFRESH 60 // 60 seconds in a minute
Timer cpmTimer; // CPM data collection, determine total current counts per minute
int currentSignalCount, currentNoiseCount, previousCPM, currentCPM;
int previousTotalSignalCount, previousTotalNoiseCount;
....
int main()
{
...
signalPin.rise(&signalISR); // attach ISR function to the rising edge
noisePin.rise(&noiseISR); // attach ISR function to the rising edge
cpmTimer.start();
In the loop portion, we then evaluate the cpmTimer. The timer is evaluated at the speed of the while(1) loop, which for the MAX32620FTHR96MHz Arm Cortex-M4 Microcontroller at the heart of this board, must be very rapid. The cpmTimer is executing in the time frame of seconds! Talk about tortoise and the hare!
if (cpmTimer.read() >= CPM_REFRESH)
{
cpmTimer.reset();
calculateCPM();
}
If we exceed 60 seconds, again remember the underlying processor speed in 96 MHz, the timer is reset and then we then hand off to the calculateCPM() function:
void calculateCPM()
{
currentSignalCount = signalCounter - previousTotalSignalCount;
previousTotalSignalCount = signalCounter;
currentNoiseCount = noiseCounter - previousTotalNoiseCount;
previousTotalNoiseCount = noiseCounter;
previousCPM = currentCPM;
currentCPM = currentSignalCount - currentNoiseCount;
// pc.printf("signal: %d, noise: %d, ", currentSignalCount, currentNoiseCount);
// pc.printf("Current CPM: %d\n", currentCPM);
}
There is also a Ticker, from the documentation:
Tickers and timers are another way of creating a time interval. These methods are somewhat better than busy wait because they allow other code to run while you are waiting. It is even possible, though nontrivial, to sleep during the wait period.
Use the Ticker interface to set up a recurring interrupt; it calls a function repeatedly and at a specified rate.
You can create any number of Ticker objects, allowing multiple outstanding interrupts at the same time. The function can be a static function, a member function of a particular object or a Callback object.
and from the MbedOS Course Notes:
An advantage of the ticker object is that we don’t need to read the time, so we can execute other code while the ticker is running in the background and calling the attached function as necessary.
I think the main advantage to Ticker over Timer is speed. We wanted to work with as much of Mbed OS as we could so we utilized both, but I think Ticker could have been used as a sole solution to timing both the data collection and LoRa transmission events. In case we used Ticker for LoRa communication timing. The gamma canary will transmit its data packets over LoRa at specified intervals. These are rather short and power inefficient. However, in the prototyping phase, we want to be able to see lots of transmissions for debugging purposes!
// data transmission/report intervals
#define LOCATION_INTERVAL 30.0 // GPS location every 120 secs
#define CPM_INTERVAL 60.0 // CPM every 60 secs
#define FUEL_INTERVAL 90.0 // Fuel Gauge every 90 secs
Directly after this, we define our Ticker and the function it will call.
volatile int locationCountDown, cpmCountDown, fuelCountDown;
Ticker transmissionTicker;
void transmitLoRaTicker(void)
{
if (locationCountDown > 0) locationCountDown--;
if (cpmCountDown > 0) cpmCountDown--;
if (fuelCountDown >0) fuelCountDown--;
}
The int variables here are declared volatile because they are declared and accessed in the program before being initialized. I believe this might cause an issue with the complier eliminating them due too complier optimizations. The volatile keyword prevents this. The function called by our Ticker will be executed every 1 second, due to this statement in our setup code:
transmissionTicker.attach(&transmitLoRaTicker,1.0);
So every 1 second, each of these variables will be decremented by one, unless of course they are already 0. This allows us to establish what you might consider a "look up" table of differently timed events. Think of it this way. We have, in this case, 3 different people scheduled to perform a different activity at a different interval. They all are watching the same stopwatch, and when the clock hits their assigned time, they know what to do. This could be expanded to any number of intervals and activities, and the timing could be even more precise... within certain limits and restrictions of course! I learned this technique from watching a series of free, online embedded programing lectures by a professor from Cornell, Bruce Land. I highly recommend his lectures and YouTube channel!
In the infinite while(1) loop of our code:
/** LoRa Data Transmision **/
if (locationCountDown == 0)
{
locationCountDown = LOCATION_INTERVAL;
transmitLocation();
}
if (cpmCountDown == 0)
{
cpmCountDown = CPM_INTERVAL;
transmitCPM();
}
if (fuelCountDown == 0)
{
fuelCountDown = FUEL_INTERVAL;
transmitFuel();
}
As the Ticker merrily counts down in the background, again every second without generally disturbing the flow of our program, this values are checked every run through the while(1) loop. When the countdown for any of them reaches zero, the counter is reset and the transmit function is called! Very nice, thanks Prof. Land!
Testing & DeploymentAs we stepped through this project, we performed testing along the way to validate our work.
With the main gamma canary electronics payload and LoRa/WiFi gateway built, we wanted to perform some basic tasting ensure that the radiation sensor, GPS, and LoRa were all working together, hardware and software, on our bench top and in the field.
Its 'easy' to test LoRa and GPS the signals are either transmitted or not. The location data matches your location or it doesn't. But how do you test a radiation sensor? It is quite difficult to simulate a reactor meltdown and if there was a nuclear war, well, we would probably have other things to deal with. It was easy to see that we we getting an average of 3 CPM, with a range of 1 to 8 CPM on the bench. It was also easy to tap and jiggle the sensor around to generate noise and ensure this was ignored, but was the CPM reading real?
We decided to get a true radioactive source! Many homes have sources of radiation readily available in their home smoke detectors, americium. However, my wife and the mother of our design team leader, while very forgiving of our Maker space, is not so forgiving when it comes to the dismantling of essential home safety devices for mad maker experiments!
So we decided to buy some Uranium ore!
Yes, you too can buy uranium ore in small quantities! In the US, individuals can buy small quantities of certain radioactive materials that are liscense exempt. We found uranium ore sold by Images Scientific Instruments for US $39.95:
Shipping Information:
We are always in compliance with Section 13 from part 40 of the NRC Nuclear Regulatory Commission rules and regulations and Postal Service regulations specified in 49 CFR 173.421 for activity limits of low level radioactive materials. Item will be shipped in accordance with Postal Service activity limits specified in Publication 52.
Radioactive minerals are for educational and scientific use only.
Sale of uranium ore is in compliance with all U.S. Federal regulations (Part 40) and NY State Regulations (10 NYCRR Part 16 and 12 NYCRR Part 38) as defined under Appendix A exemptions.
UR-01 - $39.95 Uranium Ore (500 CPM - 1, 000 CPM)
UR-02 - $74.95 Uranium Ore (1000 CPM - 2, 000 CPM)
UR-03 - $120.00 Uranium Ore (High - Over 2, 000 CPM)
But please do your research, and find out the rules for your state/country, etc. We are not responsible if men in black swarm your house with helicopters and SWAT teams.Also follow all the safety recommendations sent with your sample!
For the purposes of demonstration, we added a statement in the radiation detection code ISR to blink the MAX32620FTHR on-board LED1 with a signal pulse.
Here's the result:
With the CPM going from a steady 2-3 CPM to >300 CPM after ore exposure, I think you also can also conclude our sensor and code work!
Field Testing, on FootIn order the verify LoRa transmission at a distance and GPS tracking, we took the gamma canary for a walk. We started a screen recording of the dashboard and then went for a walk, recording some video along the way. It correlated nicely with the geolocation on our screen capture!
On our first walks, we noticed that, unlike most of our bench top LoRa transmissions, we would receive occasional garbage messages which initially caused the LoRa/WiFI gateway to crash. We corrected this both on the MAX3260FTHR embedded side of the code and in the Rpi3 python side of the code in geolocation section below. I can tell you it was quite frustrating to come home on the first few walks to see no data and a frozen blip on the map!
Field Testing the Canary, on a Bike:Field Testing, Water!Electronics and water just do not mix, but one environment where we want this work is in the water. Our first test was a short exposure to moderate rainfall. All went well except for some leaking around the LoRa antenna port, which was to be expected as we did not seal it.
For the second round of testing, we sealed up the seams of gamma canary with a hot glue gun and let it float in our fish tank.
All seemed to be going well, except we noticed that it stopped transmitting data altogether. We quickly extracted it, and to our horror we could feel the water jostling around in the enclosure! On opening, the electronics payload was soaked!
Disaster with only 10 days to go until the contest deadline! So we did the old iPhone trick and put everything in rice for a few hours.
Et viola!, miracle of miracles, it all worked!
Float Test 2, It Floats and Stays Dry!So for our 3rd round of testing, we purchased some FlexSeal glue and applied it to the seams, both inside and outside, of the enclosure. We also applied a layer pf FlexSeal spray to the underside, just in case!
After 24 hours, we refloated it in the tank, and...it worked! Thank you FlexSeal!
No data transmission cut outs and we are back to breathing again!
The next 3 sections describe adding geolocation, twitter and SMS to our project using PubNub. PubNub provides a real time data stream/messaging network. They provide over API/SDKs in over 70 programming languages that allow you to publish json messages and subscribe to channels publishing messages on their network. Messages on the PubNub network are sent across the network from point to point in 1/4 sec, there is a very generous free use tier and hundreds of blog posts, tutorials and webinars with lots of demo code to learn from.
PubNub blocks/functions provide a way to easily and quickly incorporate value added services to your project. As an example, from the time we signed up for our free tier ClickSend account to our first successful dashboard generated SMS was about 20 minutes! Its not as much about instant gratification, as about achieving meaningful results rapidly, debugging quickly and improving your projects in an enjoyable manner!
The bottom line on the ease of use of PubNub is this, it will take you 5X to 10X the as long to read the sections below as to get one of these functions up and running! All makers should look into using PubNub to enhance their projects! Check out some amazing projects using PubNub here on hackster.io PubNub channel.
Geolocation with PubNub & MapboxOne of the key features of our project is transforming the GPS data sent from the gamma canary into a mapped location on the real time dashboard. This feature is easily created when you use PubNub. The eon project is a part of PubNub, and in addition to the line, bar and gauge data displays you see, it provides a very easy way of providing graphical geolocation data through Mapbox. In order to do this you will need a Mapbox account. There is a free pricing tier, which is incredibly generous.
The GPS data is collected from the adafruit GPS feather wing in the gamma canary electronics payload. For this stage of the project, we will only be transmitting longitude and latitude. This data packet is then sent to the python script, to be 'parsed' by gateway.py:
def parseGPS(gpsMsg,prevGPS,currGPS):
print "parse gps: " + str(gpsMsg) # debug stmt
prevGPS[0] = currGPS[0]
prevGPS[1] = currGPS[1]
try:
currGPS[0] = float(gpsMsg[2]) # longitude
currGPS[1] = float(gpsMsg[3]) # latitude
print "GPS: " + currGPS[0]+ "," + currGPS[1]
except Exception as e:
print "error parsing GPS data: "
print e
In reality we are not parsing the longitude and latitude here as much as we are extracting this data the packet sent over serial to to RPi. The important part here is that we use the try/except mechanism to prevent a crash of the script in case we receive garbage in the LoRa packet.
After getting the geolocation data, we put the data into json format for publshing twith PubNub. currentLocation is specifying before this as a List(array) of the current long/lat data 'parsed from the LoRa GPOS data packet.
# Data Parameters
# GPS/Location Data
currentLocation = [0.000000, 0.000000] # [longitude,latitude]
...
# format messages for PubNub publishing
latlngPN = {'latlng':currentLocation}
The message is then published to the PubNub on the myMap channel. Anyone that has the subscription key to this channel and subscribes to it, will then see a geolocation message to this channel whenever one is published. In fact PubNub guarantees it to be every 1/4 second!
pubnub.publish().channel('myMap').message([latlngPN]).async(publish_callback)
The realtime dash board, gammaCanaryDashboard.html is one such subscriber! We place a map object on our page with the following HTML:
<div class="grid-item">
<p><font size="35"> Geographic Location </font></p>
<div id="map"></div>
<p> </p>
</div>
Then in the Javascript portion of our page, we connect this map object with the Mapbox API data sent from PubNub.
var map = eon.map({
pubnub: pn,
id: 'map',
mbToken: 'your mapbox token here',
mbId: 'mapbox.streets',
center: [40.500000,-74.600000],
channels: [channel1],
connect: connect,
marker: function (latlng, data) {
var marker = new L.Marker(latlng, {
icon: L.icon({
iconUrl: 'radsymbol.png',
iconSize: [32, 32]
})
});
return marker;
}
});
PubNub has the eon project integrated with Mapbox and knows how to interpret properly formatted long/lat data! Really, it is as simple as publishing a properly formatted json message on the channel! You can even selct a marker or markers of your choice. Just make sure the javascript knows where to find them. In our case, we just put then in the home directory location of.html file.
We have barely scratched the surface of what Mapbox has to offer. I recommend looking at some of the demos and reading some of the blog posts below, and then dream up some great ways of using PubNub, eon and mapabox with your maker project!
REALTIME DASHBOARDS Chart, Map, and Graph Your Realtime Data.
Tag: Maps and Geolocation Blog Posts
The Gamma Canary Tweets!Using PubNub function blocks, it is incredibly easy to bring social media, such as Twitter, into your project. Social media functionality will allow the gamma canary to not only provide emergency information to an individual but could help inform and protect whole communities. Many municipalities, police departments and other law enforcement agencies use Twitter to send alerts so their is good precedent here.
PubNub not only provides a publish/subscribe network, there are also many ways to connect to useful and interesting services with PubNub. We utilized mapbox as a service within our webpage as described above in order to provide an exciting and useful feature to the gamma canary.
PubNub functions live and execute on the PubNub servers. They can be programmed to modify messages and transmit them as you wish from a "central" area. For a commercial product, this allows the developer to provide value added services to customer/device/whatever generated messages. Additionally, developers don't need to share their service keys and identifiers. These are stored and accessed in the PubNub vaults. This is especially appealing to hobbyists and Makers. Do you really want to accidentally share your twitter keys to the rest the world on github?
In order to add twitter functionalty to your project, you need to first create a twitter app. This is a simple process and begins at https://apps.twitter.com/. You will need a twitter account for this. I Once this is created, your twitter app keys will be used in the PubNub function.
I am assuming you have learned a bit about PubNub already, if this is the case, the following won't seem too arcane. If you don't already know about PubNub, you should find out about it!
The twitter function/block will be enabled and created under one of your existing key sets. Of course I enabled this under my pub/sub key set for this project. These relate back to the python code, as before:
# establish PubNub communication object
pnconfig = PNConfiguration()
pnconfig.subscribe_key = 'your sub key here'
pnconfig.publish_key = 'your pub key here'
pnconfig.ssl = False
pubnub = PubNub(pnconfig)
The twitter functionality is integrated with the javascript on the CSS/HTML/JavaScript real time dashboard. There is some pretty extensive CSS to style the button which was automatically generated with an online button generator. The button is placed and linked to the button with the following HTML:
<div class="grid-item">
<p> </p>
<p><font size="35"> Tweet! </font></p>
<button class="myButton"><img src="blueBird.png" onclick="tweetData()"></button>
<p> </p>
</div>
The image, "blueBird.png", should reside in the same directory of the HTML page and will look like this:
It is connected to the tweetData() function which completes the CSS/HTML/JavaScript triad!
function tweetData()
{
console.log("Request tweet CPM & Location Data");
pn.publish({
channel:'canaryTweet',
message:
{
"request":"tweet"
}
});
}
This function simply uses the PubNub object, pn, to publish a message to the canaryTweet canal. This message does not tweet, it simply sends a message via PubNub. The Python script portion of the LoRa/WiFI gateway has subscribed to this channel, and when it receives a message, it collects the data and then publishes a message to the PubNub twitter block set up to tweet the message!
From the python script, this code sets up a listener for the 'canaryTweet' channel.
# subscribe to our pubnub channel
pubnub.subscribe().channels('canaryTweet').execute()
And this code sets up a callback which will execute when messages are received on subscribed channel. We differentiate between a request for a tweet vs an sms request. Or origin version only had tweeting, so it was simplest to send this message on the same canaryTweet channel and use the python script to decide what to do based on the content of the message.
class myPubNubListener(SubscribeCallback):
def status(self, pubnub, status):
print("status changed: %s" % status)
def message(self, pubnub, message):
print("new message: %s" % message.message)
print(str(message.message))
print(message.message['request'])
if (message.message['request'] == 'tweet'):
publishGammaTweet()
elif (message.message['request'] == 'sms'):
publishSMSMessage(message.message['number'])
#print("phone number: " + message.message['number'])
def presence(self, pubnub, presence):
pass
myPubNubListener = myPubNubListener()
pubnub.add_listener(myPubNubListener)
When the callback listener receives a "tweet" request message, it executes the message function. The print statement is just for debugging purposes. publishGammaTweet() is executed upon receipt of a message:
def publishGammaTweet():
print("gamma tweet request publish function")
tweetLine1 = '*!*!* THIS IS ONLY A TEST *!*!*\n'
tweetLine2 = 'The gamma canary Alert System\n'
tweetLine3 = 'CPM: {:d} at lat {:04.5f}, lon {:04.5f}\n'.format$
tweetLine4 = 'Local Time(EST): ' + time.asctime( time.localtime$
tweet = tweetLine1 + tweetLine2 + tweetLine3 + tweetLine4
msg = { "tweet" : tweet }
print(msg)
pubnub.publish().channel('gamma-canary-tweet').message(msg).asy$
We don't need to do any parsing of the passed in message, because we only receive messages from one channel and only execute the same patterned response to this message. Your could, however, implement some quite complex functionality here by parsing the passed in message in this function.
This is what it looks like in action:
The reason we designed it this way it because it was easier to access all the data we tweet from the Python LoRa/Wifi gateway code than from the javascript!
SMS: Hello, the Gamma Canary Is Calling!SMS is the third major function we added to the gamma canary real time dashboard and simply the easiest to do using the ClickSend block.
First review the account details and obtain your credentials from ClickSend. Go your PubNub account, and select the ClickSend Block. Then choose the project & keyset you would like to associate with this BLOCK. Enter your API key and Username into the PubNub BLOCK page key vault. You are ready to go!
You can use the test function on the lower left side of the PunNub BOCK page. The message should be formatted as such:
{
"to":"+1231231234",
"body":"message here"
}
Again, as with our tweets, we publish our SMS from the gateway.py to PubNub. We extract the phone number, then pass it into the fucntion, collect the geolocation and CPM data, then create an message to SMS. This is all then published to the cpm-text-alert channel which will execute the ClickSend function/BLOCK for us!
def publishSMSMessage(phoneNumber):
smsLine1 = 'ALERT! '
smsLine2 = 'CPM: {:d} LAT: {:04.5f}, LON: {:04.5f}\n'.format(int(cpm[1]),currentLocation[0],currentLocation[1])
smsMsg = smsLine1 + smsLine2
sms = { "to" : phoneNumber, "body" : smsMsg }
print(sms)
pubnub.publish().channel('cpm-text-alert').message(sms).async(publish_callback)
You can learn a lot more about PubNub functions/BLOCKS in this brand new tutorial, A Crash Course in PubNub Functions, and more specifically about PubNub and twitter, Post Tweets to your Twitter account.
Technical Aside: RadioactivityWorking on this project, motivated us to not only learn more about the technologies involved, but also motivated to learn more about the "problem space" It was interesting read about the hero's of Physics from the 20th century and how they came to explore and understand the underlying basis of matter itself. So I took the opportunity to ask my team mate( and son) to answer some questions and here it what he came up with:
- What is radioactive decay?
Radioactive decay is when an unstable atom, whose nuclei cannot hold itself together, begins to lose energy. It loses energy to get itself back to the ideal ratio of protons to neutrons, making it stable again. This energy is emitted in the form of radiation.
- What is nuclear fission?
Nuclear fission is the splitting of large atoms, usually by shooting neutrons at an unstable atom. This breaks the large atom into smaller atoms. When this happens, mass is lost, and is converted into energy. This is how nuclear power plants work.
- What are Alpha, Beta, and gamma rays?
When an atom is going through the process of radioactive decay and releases radiation, the three most well known types are alpha, beta and gamma radiation. Alpha decay is where two protons and two neutrons are given off, and have a positive charge. Beta decay is where an electron is given off. Gamma rays are the strongest of the bunch, and are given off in the form of a ray (unlike the others) when the protons and neutrons in a nuclei begin to shuffle.
- What are cloud chambers?
A cloud chamber is a way to bring some of these ideas together in a visual way. With a cloud chamber, you are able to see the trails that these particles leave behind, after being emitted. This works because the cloud inside the chamber, when disturbed, creates this visual streak and from this you can identify the type of particle.
One of the neat side projects we did as a part of the gamma canary was to make a simple cloud chamber at home for this project. This let us visualize the process of radioactive decay in a very direct and tangible way(see below). What else are you going to do when you have some spare uranium ore around the house anyway?
DIY cloud chamber instructions we used are here and here. All the items we used can be found at stores locally and in the US there are many sources of dry ice, such as Air Gas Dry Ice, for everyday consumers like us!
References for the above:
Future DirectionsOne of our inspirations for this project and where we want to take it the Wave Glider by Liquid Robotics:
Going Where No Human Should: Wave Gliders Meet Hawaii’s Kilauea Lava Flow
We have some small and large future goals for this project:
- We would like to calibrate our sensors from CPM to something more biomedically meaningful, such as Sieverts and improve the internal GPS and LoRa antenna connections. We found the SMA connectors to be a little bit too easy to disconnect in the field.
- We would also like to incorporate more advanced power management using the on board MAX77650 Ultra-Low Power PMIC and changing ur ability to completely disable the GPS and re-enable with the help of on board RTC.
- One problem we ran into was with battery characterization was determining the temperature of the battery. This is a key input variable into the modelgauge algorithm. When we tried to set the registers through the driver, we were only able to disable the default die temperature sensor! We went to the hackster.io contest forum and got this info back from Greg:
If you check the schematic you should notice that R15 is marked DNI (Do Not Install). This component is not populated so that AIN_3 is available for other uses, while you can still read the AMUX output from the MAX77650 through the AIN channel on the MAX17055. You can short across R15 to connect AIN_3 to AIN on the MAX17055. Remember that you will also need to add a pullup to the battery voltage to form a resistor divider with the thermistor to ground. Unfortunately, there was not much room to include a test point to access the THRM pin of the MAX17055.
- We would like to modify the gamma canary both in size, shape and scope to be more like the wave glider. In size, to increase the size to add more solar panels and add a form of propulsion. In shape, as Phil V. discussed above, to make it a more hydrodynamically friendly structure and in scope to increase the number and types of sensors on board.
- We can go far from our house, but not too far. Future versions will incorporate cellular data transmission and field LoRa boosters to jump of leap frog the signal to a WiFi/Internet gateway. This will allow us to test the canary on actual bodies of water, none of which are in LoRa distance our home.
- We would like to incorporate the gamma canary with the citizen science project Safecast. While building our device and researching other topics on radiation we found project Safecast and now we have completed this stage and our contest entry, we want to connect with this effort, both ourselves and our gamma canary. We feel it is very compatible with their device, the bGeigie nano and the API they use to collect and share data!
Thank you for reading through our project we truly enjoy all aspects of this project so far, even the soaking! And this is only the beginning!
Comments