ESP8266 割り込みパルスカウント (ガイガーカウンターの心臓部)

 先日は、割り込みを使ってLチカ やってみました。
 そのときに軽く触れましたが、Lチカしてる部分でカウンターを仕込めば、そのまんまガイガーカウンターになります。
 連載3回目でいきなりですが、ガイガーカウンター(モニタリングポスト)の心臓部に仕立てちゃいます。

#include "Ticker.h"
Ticker ticker;
Ticker led;

const unsigned char pinSignal = 0;
const unsigned char pinLED = 15;

const unsigned short l_sma = 60 * 60; // moving average(1hour)
const unsigned short m_sma = 60 * 20; // moving average(20minutes)
const unsigned short s_sma = 60 * 5;  // moving average(5minute)

volatile unsigned char cps[l_sma];    // max 255cps
volatile unsigned short idx_cps = 0;
volatile unsigned long countup = 0;
volatile unsigned long total_count = 0;

void setup() {
  // put your setup code here, to run once:

// for debug
  Serial.begin(115200);
  Serial.print("uptime");
  Serial.print(",");
  Serial.print("total_count");
  Serial.print(",");
  Serial.print("s_cpm"); 
  Serial.print(",");
  Serial.print("m_cpm"); 
  Serial.print(",");
  Serial.println("l_cpm"); 
  
  for(int i=0; i<l_sma; i++)  cps[i] = 0;

  pinMode(pinSignal, INPUT);
  pinMode(pinLED, OUTPUT);
  attachInterrupt(pinSignal, signal, FALLING);
  ticker.attach(1, tick);   // every 1 second
}

void loop() {
  // put your main code here, to run repeatedly:
}

void signal() {
  cps[idx_cps] ++;
  total_count ++;
  digitalWrite(pinLED, HIGH);
  led.attach(0.0001f, ledoff);
}

void ledoff() {
  led.detach();
  digitalWrite(pinLED, LOW);
}

void tick() {
  float l_cpm = 0.0f;
  float m_cpm = 0.0f;
  float s_cpm = 0.0f;

  idx_cps ++;
  if(idx_cps == l_sma)  {
    idx_cps = 0;
    countup ++;
  } 
  if(countup) l_cpm = cps[idx_cps];
  cps[idx_cps] = 0;

// calc cpm
  int cnt = 0;
  int idx = idx_cps;
  while(cnt < (!countup ? idx_cps : (l_sma - 1))) {
    idx --;
    if(idx < 0) idx = l_sma - 1;
    
                    l_cpm += cps[idx];
    if(cnt < m_sma) m_cpm += cps[idx];
    if(cnt < s_sma) s_cpm += cps[idx];

    cnt ++;
  }

  l_cpm *= (60.0f / (!countup                    ? idx_cps : l_sma));
  m_cpm *= (60.0f / (!countup && idx_cps < m_sma ? idx_cps : m_sma));
  s_cpm *= (60.0f / (!countup && idx_cps < s_sma ? idx_cps : s_sma));

// for debug
  Serial.print(countup * l_sma + idx_cps);
  Serial.print(",");
  Serial.print(total_count);
  Serial.print(",");
  Serial.print(s_cpm); 
  Serial.print(",");
  Serial.print(m_cpm); 
  Serial.print(",");
  Serial.println(l_cpm); 
}

 サンプルと言ったって半端なものではなく、3種類の移動平均処理を同時に計算しています。
 あとは使用する管の特性に応じた cpm→μSV/h の変換式を追加してやれば、ただちにガイガーカウンターになります。
(慣れてくると cpm のままでも不自由しなくなりますが)


 詳しくはソースを読んでもらうとして、Tick() の中の //calc cpm 以降がキモですな。
 3600秒分の配列化された cps[] から平均化時間ごとに抜き出しているだけですけど。


 上はタクトスイッチのボタンを数えていますが、汎用ガイガー検波ユニット みたいなのを繋いでカウントしたらいいですね。
 あとは、I2C液晶を繋いで測定値を表示させるもよし、TCPUDPで測定値をサーバーに送信させるもよし、と。


 なにも考えず 3600秒分(3.6KB) な配列を定義して使っちゃってますが、ESP8266 は RAM が 96KB くらいあるそうで、ぶっちゃけ、mbed より多いんですわ、これ。
 Arduino 使うと、その関係で幾分メモリー食うと思いますが、Arduino じゃなくて Native GCC でプログラム書けば、理論的には mbed より複雑なことが出来そうです。


(追記)2016/01/31
 ハードウェア部を含めて ESP8266 ガイガーカウンターの製作 という記事を続きに書きました。
 複数管にも対応しています。(その関係でソフトも若干かわってます)