sea side she side

写真と山、資格が好きなアラフォーエンジニアのブログ

Pachube w/ Arduino

      2018/08/07

ここ1ヶ月Pachubeへのアップデートに四苦八苦していました。が、ERxPachubeというライブラリをarms22さんのつぶやきで発見し、さっくりとテストコードを動かすことができました。

その後べた書きなスケッチで進めていたのですが、前々回の通りCPMの計算方法で四苦八苦。とりあえずの形ながらも移動平均を求めるようにできるまで結構かかりました。

ここで私の師匠でもあるTaKuYaに軽くチェックしてもらったところ、オブジェクト指向化しましょうとのこと。その教えに従いクラス化しようとしたところこれまた四苦八苦。

一番はまったのはattachInterrupt。クラス化するに当たり、インスタンス生成時に初期化として割込のPIN番号を渡してattachInterruputを設定使用としたのですがエラー、エラー、エラー…。Google Codeだったり、githubでattachInterruptとclassをキーワードに探したのですが同じようにしている人は少なく困りに困っていました。

英語サイトを含めあれだけ探したのに、ふと旧Arduino:forumを覗いてみると、「How to use attachInterrupt with a class member」というドンぴしゃなスレッドがありました。

クラスのメンバ関数として直接呼び出せないということはわかりました。しかし代替方法と思わしき2例の違いや意味、使い方がわかりません…。

できねーとつぶやいたらarms22さんが親切にも「そしたらformのサンプルどおりグローバル変数にクラスのインスタンスのポインタ渡して、attachInterruptに渡した関数からポインタを介してメンバ関数を呼べばいい」と教えてくれました。

私の知識と経験のなさのせいで意味を完全には理解はできませんでしたが、

class Foo{
  public:
    void bar();
};

Foo foo;

void setup(){
  attachInterrupt(0,fooBar,RISING);
}

void loop(){}

void fooBar(){
  foo.bar();
}

のほうでその通りすればよいことがarms22さんのおかげでわかりました。

しかし既存のスケッチにあてはめてやってみるものの全然駄目。数日あれやこれやと試しましたがうまくいきません。

一日おいて、同じ内容を小さな単位で一から書き直していくと無事動かすことができました。結局のところ原因はわかりませんでしたが…。

本来であればファイルを分け、インクルードガードを入れ、継承を使い、もっと人に優しいように作るのがよいのですがとりあえずということで。
# これじゃライブラリにもなりません。あくまでクラスの勉強用ということで。

#include <SPI.h>
#include <Wire.h>
#include <Ethernet.h>
#include <ERxPachube.h>
#include <Rtc8564AttachInterrupt.h>


/*----------------------------------------------
 RtcInterruptクラス
 ----------------------------------------------*/
class RtcInterrupt {
public:
  RtcInterrupt();
  void Countup();
  unsigned int Get();
  void Set(unsigned int);
private:
  volatile unsigned int sec_;
};

RtcInterrupt::RtcInterrupt() {
  sec_ = 0;
}

void RtcInterrupt::Countup(void) {
  ++sec_;
}

unsigned int RtcInterrupt::Get(void) {
  return sec_;
}

void RtcInterrupt::Set(unsigned int s) {
  sec_ = s;
}


/*----------------------------------------------
 GeigerCounterクラス
 ----------------------------------------------*/
class GeigerCounter {
public:
  GeigerCounter();
  void IncCps();
  void SetCps(unsigned int);
  unsigned int GetCps();
  void CulculateCpm(unsigned int);
  unsigned int GetCpm();
  void CulculateCpmMav(unsigned int);
  unsigned int GetCpmMav(void);
private:
  volatile unsigned int cps_;
  volatile unsigned int cpm_;
  volatile unsigned int cpmmav_;
};

GeigerCounter::GeigerCounter() {
  cps_ = 0;
  cpm_ = 0;
}

void GeigerCounter::IncCps(void) {
  ++cps_;
}

void GeigerCounter::SetCps(unsigned int s) {
  cps_ = s;
}

unsigned int GeigerCounter::GetCps(void) {
  return cps_;
}

void GeigerCounter::CulculateCpm(unsigned int s) {
  static volatile unsigned int cpsa_[60] = {0};
  
  cpm_ -= cpsa_[s];
  cpsa_[s] = cps_;
  cpm_ += cpsa_[s];
  
  
//for debug
for(unsigned int i=0; i<60; ++i) {
  Serial.print(cpsa_[i]);
  Serial.print(" ");
}
Serial.println("");
for(unsigned int j=0; j<s; ++j) {
  Serial.print("  ");
}
Serial.println("~");
  
  
}

unsigned int GeigerCounter::GetCpm(void) {
  return cpm_;
}

void GeigerCounter::CulculateCpmMav(unsigned int m) {
  static volatile unsigned int cpmsum_;
  static volatile unsigned int cpma_[5] = {0};
  
  cpmsum_ -= cpma_[m];
  cpma_[m] = cpm_;
  cpmsum_ += cpma_[m];
  cpmmav_ = cpmsum_ / 5;
}

unsigned int GeigerCounter::GetCpmMav(void) {
  return cpmmav_;
}


/*----------------------------------------------
 湿度センサークラス
 ----------------------------------------------*/
class HumiditySensor {
public:
  HumiditySensor();
  float Get();
  void CulculateValue(unsigned int);
private:
  volatile float value_;
};

HumiditySensor::HumiditySensor() {
  value_ = 0;
}

float HumiditySensor::Get(void) {
  return value_;
}

void HumiditySensor::CulculateValue(unsigned int p) {
  value_ = 0;
  for (unsigned int i = 0; i < 10; ++i) {
    value_ += analogRead(p);
  }
  
  value_ /= 10;
  value_ = ((5.0 / 1024.0) * value_ ) * 100;
}


/*----------------------------------------------
 温度センサークラス
 ----------------------------------------------*/
class TemperatureSensor {
public:
  TemperatureSensor();
  float Get();
  void CulculateValue(unsigned int);
private:
  volatile float value_;
};

TemperatureSensor::TemperatureSensor() {
  value_ = 0;
}

float TemperatureSensor::Get(void) {
  return value_;
}

void TemperatureSensor::CulculateValue(unsigned int p) {
  value_ = 0;
  for (unsigned int i = 0; i < 10; ++i) {
    value_ += analogRead(p);
  }
  
  value_ /= 10;
  value_ = (((5.0 * value_) / 1024) / 5) * 100;
}


/*----------------------------------------------
 initialize
 ----------------------------------------------*/

// インスタンス生成
RtcInterrupt SecTimer;
GeigerCounter SBM20;
HumiditySensor CHSUGS;
TemperatureSensor LM35DZ;

// Ethernetシールド設定
byte mac[] = { 
  0x90, 0xA2, 0xDA, 0x00, 0x00, 0x00 }; 
byte ip[] = { 
  192, 168, 0, 100 };

// Pachube API key and feed id
#define PACHUBE_API_KEY	"APIキーを記入"
#define PACHUBE_FEED_ID	FeedNoを記入

ERxPachubeDataOut dataout(PACHUBE_API_KEY, PACHUBE_FEED_ID);
void PrintDataStream(const ERxPachube& pachube);
void PrintString(const String& message);

// RTCタイマ開始日時設定
#define RTC_SEC  0x00  // 秒
#define RTC_MIN  0x00  // 分
#define RTC_HOUR 0x22  // 時
#define RTC_DAY  0x24  // 日
#define RTC_WEEK 0x05  // 曜日(00:日 〜 06:土)
#define RTC_MON  0x06  // 月
#define RTC_YEAR 0x11  // 西暦

byte date_time[7] = {
  RTC_SEC
    ,RTC_MIN
    ,RTC_HOUR
    ,RTC_DAY
    ,RTC_WEEK
    ,RTC_MON
    ,RTC_YEAR
};

// 電源リセット確認フラグ
boolean init_flg = false;
// 計測間隔(RTC割込間隔)
#define RTC_INTERRUPT_TERM 1
// 定周期タイマ間隔設定単位 0:秒 1:分
#define RTC_INTERRUPT_MODE 0

// 外部割込ピン
#define RTC_INTERRUPT_PIN 2
#define GC_INTERRUPT_PIN 3


/*----------------------------------------------
 * 関数: setup
 * 概要: 事前処理
 * 引数:
 * 戻値:
 * ---------------------------------------------*/
void setup(void) {
  Serial.begin(9600);
  Serial.println("Weather Station Board - Arduino");
  Serial.println("sea side she side");
  Serial.println("https://www.a10i.jp/");
  Serial.println("");
  Serial.println("");

  Rtc.initDatetime(date_time);

  // 日付時刻初期化
  Rtc.beginWithoutIsValid();

  Rtc.begin();

  if (!Rtc.isInterrupt()) {
    Rtc.syncInterrupt(RTC_INTERRUPT_MODE, RTC_INTERRUPT_TERM);
  }

  // RTC割込設定
  pinMode(RTC_INTERRUPT_PIN, INPUT);
  digitalWrite(RTC_INTERRUPT_PIN, HIGH);
  attachInterrupt(0, RTC_Interrupt, FALLING);

  // GeigerCounter割込設定
  pinMode(GC_INTERRUPT_PIN, INPUT);
  attachInterrupt(1, GC_Interrupt, FALLING);

  // Ethernet・Pachube初期化
  Ethernet.begin(mac, ip);
  dataout.addData(0);
  dataout.addData(1);
  dataout.addData(2);
}


/*----------------------------------------------
 * 関数: loop
 * 概要: メイン処理
 * 引数:
 * 戻値:
 * ---------------------------------------------*/
void loop(void) {
  
}


/*----------------------------------------------
 * 関数: RTC_Interrupt
 * 概要: RTC割込処理
 * 引数:
 * 戻値:
 * ---------------------------------------------*/
void RTC_Interrupt(void) {
  static volatile unsigned int sec;
  
  if(sec > 299) {
    sec = 0;
  }
  
  SBM20.CulculateCpm(sec % 60);
  SBM20.SetCps(0);
  
  Serial.print("CPM: ");
  Serial.println(SBM20.GetCpm());
  
  if(sec % 60 == 0) {
    // cpm
    SBM20.CulculateCpmMav(sec / 60);
    Serial.print("CPM Moving Average: ");
    Serial.println(SBM20.GetCpmMav());
    
    // 各センサー
    CHSUGS.CulculateValue(3);
    Serial.print(CHSUGS.Get());
    Serial.println(" %");
    
    LM35DZ.CulculateValue(2);
    Serial.print(LM35DZ.Get());
    Serial.println(" C");
    
    // 各データをpachubeへ
    outputPachube();
  }
  
  ++sec;
}

/*----------------------------------------------
 * 関数: GC_Interrupt
 * 概要: GeigerCounter割込処理
 * 引数:
 * 戻値:
 * ---------------------------------------------*/
void GC_Interrupt(void) {
  SBM20.IncCps();
}


/*----------------------------------------------
 * 関数: outputPachube
 * 概要: Pachubeへのアップロード
 * 引数:
 * 戻値:
 * ---------------------------------------------*/
void outputPachube(void) {
    Serial.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
    dataout.updateData(0, (int)SBM20.GetCpmMav());
    dataout.updateData(1, CHSUGS.Get());
    dataout.updateData(2, LM35DZ.Get());
    
    int status = dataout.updatePachube();
    
    Serial.print("sync status code <OK == 200> => ");
    Serial.println(status);

    PrintDataStream(dataout);
}


/*----------------------------------------------
 * 関数: PrintDataStream
 * 概要: 
 * 引数: const ERxPachube& pachube
 * 戻値:
 * ---------------------------------------------*/
void PrintDataStream(const ERxPachube& pachube) {
    unsigned int count = pachube.countDatastreams();
    Serial.print("data count=> ");
    Serial.println(count);
    
    Serial.println("<id>,<value>");
    for(unsigned int i = 0; i < count; i++) {
        Serial.print(pachube.getIdByIndex(i));
        Serial.print(",");
        PrintString(pachube.getValueByIndex(i));
        Serial.println();
    }
}


/*----------------------------------------------
 * 関数: PrintDataStream
 * 概要: 
 * 引数: const String& message
 * 戻値:
 * ---------------------------------------------*/
void PrintString(const String& message) {
    String tmpStr = message;
    int bufferSize = tmpStr.length() +1;
    char pBuffer[bufferSize];
    tmpStr.toCharArray(pBuffer, bufferSize);
    
    Serial.print(pBuffer); 
}

クラス化以外にもはまったことがもう一つあります。attachInterrupt 1(Digital 3)の割込は動くのにatacchInterrupt 0(Digital 2)だけ動かないことでした。

事の始まりはガイガーカウンターを始めるより前、ただ単純にen_daisukeさんのRTC割込ライブラリを試してみたときでした。RTCがおかしいのか、ライブラリの使い方がおかしいのか、ライブラリ自体がおかしいのかすごく悩み、そのときはあきらめました。

今回1秒毎の割込として使いたいため、もう一度組んだら動いたのです(前々回の記事)。よし、と思って色々やってると動かなくなりました。

 ・オシロでRTCのパルスを確認:問題なさそう
 ・RTCの代わりにGM管をさす:問題なさそう

ここまで考えていたときに、何をしたのか思い出しました。

 そうだ、最小構成のArduino 328Pから純正Arduino Unoに変更した!

Ethernetシールドを使うためにUno入れ替えたのでした。ただ、自作で動いて純正で動かない。…逆ならわかるんだけど。

ここでRTC割込ライブラリがUnoが対応しているのかen_daisukeさんにtwitterで確認。Unoは持ってないので確認してないとのこと。症状を話しながら切り分けしているとき気がつきました。

 ・RTCだけattachInterrupt 1の割込は動くのにatacchInterrupt 0は動かない

RTCのパルスは5V->0V->5Vのパルスです。GM管は0V->5V->0Vでは動くことから、いったん74HC04(NOT回路)を挟んでみました。なんと言うことでしょう、動くではありませんか。

このことからプルアップがうまくいってないことに気づきGoogle様にお伺いしたところ、

 許容外の電流が流れ内部のプルアップ用トランジスタのが壊れる

という現象があることを知りました。おそらくガイガーカウンター以前、色々試しているうちに知らず知らず突っ込んでしまったのでしょう。情けない。

ATMega 328PにUnoのboot焼いてDIPの交換したところ無事動きましたとさ。

arms22さん、en_daisukeさん、ありがとうございました!!

次は他のセンサーを入れつつ、基板とケースに入れることを考えていきます。

 

 - Make , , , , , , ,