Pushing an Arduino to the limit.

Gamma spectacular, theremino adapters, non-sound card based, etc...
Post Reply
User avatar
Steven Sesselmann
Posts: 693
Joined: 27 Apr 2015, 11:40
Location: Sydney

Pushing an Arduino to the limit.

Post by Steven Sesselmann » 14 Oct 2019, 21:31

I recently bought myself a box full of $5 Arduino Uno's (as you do).

For those of you who don't know what an Arduino is, it's a low cost programmable microprocessor, the original Arduino modules cost around $20 but the Chinese clones are a lot cheaper. The software to program the arduino is free and has been downloaded more than 13 million times. Here is a picture of the ones I bought on eBay, and they work just fine.
arduino-uno.JPG (162.44 KiB) Viewed 357 times
I started off making a simple geiger counter and after brief success I set my ambitions higher, and wondered if it would be possible to make some kind of spectrometer from a $5 Arduino. The main component of my project is the Schmitt trigger, this is a circuit with a fast opamp that triggers a logic pulse when the analogue voltage from a PMT detector reaches a threshold. The length of the logic pulse varies with the height of the analogue pulse.

I sell the Schmitt trigger here: https://www.gammaspectacular.com/blue/g ... tt-trigger

The only thing that may need changing are the load resistors, very high for neutron detectors and around 1M for PMT's
schmitt-trigger.JPG (152.5 KiB) Viewed 357 times
For an extra few dollars I got some 8 x 32 pixel LED displays off eBay like these:
https://www.ebay.com/sch/i.html?_from=R ... 6&_sacat=0

And some simple 4 digit LED number displays.

The basic principle of the code is to use the interrupt function on the Arduino which are pins 3 and 3, using digital interrupt you can very accurately record the time in microseconds when a pin goes high or low, The maximum resolution on the Arduino is 4 µs. but over longer periods the error is not cumulative, so at best it is out by 2 µs.

Writing the basic spectrometer code was pretty simple, making it display on the 8x32 pixel display took me a bit longer. All up it was a three day job, but I was learning.

To make my job a little easier I 3D printed a smart enclosure for the hardware and fitted a piece of coloured acrylic which I had laying around. I also added a button for resetting the variables quickly.
arduino spectrometer.jpg
Arduino Spectrometer
arduino spectrometer.jpg (96.5 KiB) Viewed 357 times

To fully demonstrate how this thing works I shot a 2 minute video and uploaded it to YouTube here: https://youtu.be/qcwWh61sAhw

I have a few ideas on how to develop the Arduino into useful products, but it wont be anything like what you see here, this was purely a fun R&D project.

If anyone is interested in the code I am happy to post it below.

Steven Sesselmann | Sydney | Australia | gammaspectacular.com | groundpotential.org | beejewel.com.au |

User avatar
Posts: 95
Joined: 23 Sep 2016, 04:25
Location: Germany

Re: Pushing an Arduino to its limit.

Post by Svilen » 15 Oct 2019, 00:35

Very nice project, Steven. I also experiment for several days now with Arduino, working on an environmental radiation monitor with two GM tubes and a data logger, maybe I share it later too, in case successful :)
I would be interested to see the code you used.

User avatar
Steven Sesselmann
Posts: 693
Joined: 27 Apr 2015, 11:40
Location: Sydney

Re: Pushing an Arduino to its limit.

Post by Steven Sesselmann » 15 Oct 2019, 07:54


Here is my code as it currently stands, it's working quite well, but consider it a work in progress.

Arduino MCA by Gammaspectacular (v1.0) October 2019

Code: Select all

 Multichannel analyser for arduino written by Steven Sesselmann with help from David Housley

 The analogue pulse from a scintillation detector is coupled through a GS-SCMITT trigger and outputs 
 a square wave pulse with length proportional to the log of the pulse height.
 The 3.3V square wave length is a known function of the analogue pulse height.
 The actual pulse height can be calculated by the formula pheight = (plength)/e^(plength/tconstant)
 The program outputs a histogram in 100 channels to a MAX72XX matrix display with 32x8 pixels.

#include <MD_MAXPanel.h>          // Library for matrix display
#include <TM1637.h>               // Library for 7 segment displays

          TM1637 tm(8, 9);        // CLK-Pin 8  DIO-Pin 9 Pinout for TM1637(a)
          TM1637 tn(6, 7);        // CLK-Pin 6  DIO-Pin 7 Pinout for TM1637(b)

const MD_MAX72XX::moduleType_t HARDWARE_TYPE = MD_MAX72XX::FC16_HW; //Hardware definition
const uint8_t X_DEVICES = 4;                                        //Number of 8x8 segments in x direction
const uint8_t Y_DEVICES = 1;                                        // number of segments in y direction
const uint8_t CLK_PIN = 13;   // Pin connections for MAX72xx
const uint8_t DATA_PIN = 11;  // Pin connections for MAX72xx
const uint8_t CS_PIN = 10;    // Pin connections for MAX72xx


const byte ledPin = 13;           // pin assigned to led on board
const byte Pin2 = 2;              // pin assigned to rising pulse (connect3d to pin 3)
const byte Pin3 = 3;              // pin assigned to falling pulse (connected to pin 2)
const byte Pin4 = 4;              // pin assigned for reset

volatile byte state = LOW;        // pin state for positive pulse

//--------------------- SET UP CONSTANTS HERE --------------------------------------

const float e = 0.5;             // This is the plength to pheight exponential              
const int firstbin = 20;         // Ignore pulses below this firstbin threshold
const int binsize = 8;           // sets bin size (min 4 for Arduino uno)
const int bins = 80;             // sets the number of channels in array

//---------------------DEFINE VARIABLES HERE------------------------------------

float pheight = 0.00;             // define variable
float maxvalue = 0.00;            // define variable

int channel[bins];                // define number of channels
int ratio = 0;                    // define variable
int usec = 0;                     // define variable
int laptime = 0;                       // define variable lap time
int seconds = 0;                  // define variable
int tcounts = 0;                  // define variable
int plength = 0;                  // define variable
int i = 0;
int m = 0;

unsigned long b = 0;              // define variable for beginning of pulse rising
unsigned long f = 0;              // define variable for end of pulse falling


void setup() {
  Serial.begin (57600);            // set speed of serial communication
  tm.init();                       // initialise tm display
  tm.setBrightness(5);             // set brightness tm display

  tn.init();                       // initialise tn display 
  tn.setBrightness(5);             // set brightness tn display
  mp.begin();                     // initialise mp display
  mp.setIntensity(2);             // set brightness mp display
  pinMode(ledPin, OUTPUT);        // set function of pin 13
  pinMode(Pin2, INPUT_PULLUP);    // set function of pin 2
  pinMode(Pin3, INPUT);           // set function of pin 3
  pinMode(Pin4, INPUT);           // set function of pin 4
  attachInterrupt(digitalPinToInterrupt(Pin2), blink1, RISING);  // interrupt all funktions for blink1
  attachInterrupt(digitalPinToInterrupt(Pin3), blink2, FALLING); // interrupt all funktions for blink2


void blink1() {                                 // subroutine 1
  b = micros();                                 // start time in microseconds

void blink2() {                                 // subroutine 2
  f = micros();                                 // finish time in microseconds
  plength = (f-b);                              // calculate pulse length
  if (plength < 256) {
    pheight = (plength * exp(e)) ;   
  tcounts = tcounts +1 ;                        // make total counts

  // for loop sorts by pulseheight and increases the count in the matching channel
  for ( i = 0; i <= bins; i = i+1){             
      if ( pheight > (firstbin+(binsize *i)) && pheight <= (firstbin+(binsize * (i + 1)))){
         (channel[i] = (channel[i]+1));

 void loop() {
  delay(100);                     // wait 100 millisecons
  mp.clear();                     // clear mp display
  for ( i = 0; i <= bins ; i = i+1){          // get max value in array

  if (channel[i] >= maxvalue){ maxvalue = channel[i]; } else {}
  ratio = ((channel[i]/maxvalue)*8) -1 ;      // calculat ratio between counts and max value then convert to 8 bit
  mp.setPoint(i-m,ratio -7, true);            // print spectrum and fill in dots below
  mp.setPoint(i-m,ratio -6, true);
  mp.setPoint(i-m,ratio -5, true);
  mp.setPoint(i-m,ratio -4, true);
  mp.setPoint(i-m,ratio -3, true);
  mp.setPoint(i-m,ratio -2, true);
  mp.setPoint(i-m,ratio -1, true);
  mp.setPoint(i-m,ratio -0, true);

  if (millis() > seconds * 1000) { seconds++; } else {}     // second counter
  tm.dispNumber(tcounts/(seconds-laptime));                 // display counts per second
  tn.dispNumber(seconds-laptime);                           // display seconds since last reset

  m = (m+1)%(bins-firstbin);                                // moves display one pixel to the left

  int val = digitalRead(Pin4);                              // reset  button triggers subroutine
  if (val == 1) {                                           // to clear variables and reset spectrum
    laptime = seconds;
    tcounts = 0;
    pheight = 0;
    maxvalue = 0;

    for ( i = 0; i <= bins ; i = i+1){
      channel[i] = 0;
    } else {}

Steven Sesselmann | Sydney | Australia | gammaspectacular.com | groundpotential.org | beejewel.com.au |

Posts: 44
Joined: 13 May 2019, 08:09

Re: Pushing an Arduino to its limit.

Post by gwgw » 15 Oct 2019, 08:44

A coincidence counter would be an interesting project with more than one tube I guess. It would be challenging - for me the hardware part would be challenging for sure as I know little about HV supplies for GM tubes and any signal conditioning and amplification (if required). But software side would be complicated too. Arduino's interrupts are not reentrant, interrupts are disabled on the MCU before your ISR is called and reenabled after it finishes. So coincident events would very likely be lost as the MCU has already disabled interrupts when the first event occurs. Of course, inside your interrupt handler you can explicitly enable interrupts - perhaps that may happen fast enough so that the second coincident event triggers the interrupt. Perhaps not. But even if it does, writing reentrant interrupt handlers is a very tricky business.
Milen Rangelov

Conor Whyte
Posts: 65
Joined: 28 Apr 2019, 15:06

Re: Pushing an Arduino to the limit.

Post by Conor Whyte » 20 Nov 2019, 16:33

I'd love to try executing this code on a Teensy 3.6 or 4 to see if this program could be further adapted to an LCD screen. --- Also I'd like to try outputting the data onto a WG12864B display.
Getting curious. Thank you for posting the code. If I get this experiment to work on the teensy 3.6, I will post here about it.

User avatar
Posts: 81
Joined: 16 Jan 2017, 07:06

Re: Pushing an Arduino to the limit.

Post by sgt_bear » 08 Dec 2019, 06:48

If you need a more "boosted" arduino, take a look at the new models, they come with ARM Processor which is much more powerfull (48Mhz instead of 16)
- Jonathan from Switzerland

Posts: 44
Joined: 13 May 2019, 08:09

Re: Pushing an Arduino to the limit.

Post by gwgw » 08 Dec 2019, 07:45

I'd recommend ESP32, it's very cheap (you can have it for less than $10), it happily runs arduino sketches plus it's much more powerful and it has wifi/bluetooth and a lightweight tcp/ip stack. And it's surprisingly....well, working without issues. Much cheaper than the Uno yet much more powerful.
Milen Rangelov

Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests