前回までで、AEイベントを収集するセンサーから信号を取り出し、Arduinoで複数チャンネルのAD変換してイベントをとらえる事前検討が完了しました。ここでは、長期ロギングに向けて最終的な統合をしていきます。

長期ロギング用の実装を事前準備していたのですが、実際にロギングを開始してみるといくつもの新たな課題を確認しました。それらの課題はつぎのとおりです。
- Flashへのイベントデータの記録ができない。
- EEPROMなどのデータ回収でavrdude.exeを使うとリセットが発生する
- シリアルモニタを使うとON/OFFでリセットが発生する。
- 閾値を超えていないイベントが記録される。
- ロギング用プログラムを起動した直後にイベントが記録される。
- 隣のチャンネルの信号の影響を受けているように見える。
これらの課題について、以下のように検討して、対処していきました。
Flashへのイベントデータの記録ができない。
できるだけ多くのイベントデータを記録できるようにEEPROMではなく、Flashに記録する方針でしたが、動作検証するとこのArduinoは、細工しないとFlashに記録できないことが判明しました。 次の件の課題もあり、Flashへの記録はあきらめて、EEPROMを目いっぱい活用することにしました。イベントデータは1ブロック8byteに圧縮する仕様としました。
EEPROMなどのデータ回収でavrdude.exeを使うとリセットが発生する
avrdude.exeを使うには、一度リセットしなければならないことが判明しました。検討の結果、データ回収、などのすべての運用に必要な機能を、イベントロギングと並行で処理できるように実装することにしました。
シリアルモニタを使うとON/OFFでリセットが発生する。
シリアルモニタは、ON/OFFで、リセット信号を送信する仕様になっているようだ。シリアルモニタでデータ回収することも検討していたが、データを吸い上げる実装もロギングツール内に実装することにしました。
閾値を超えていないイベントが記録される。
これはいくつかバグや後述の項目の問題が関わっていました。
まず、最初の実装では、閾値判定したデータそのものがイベントのそのチャンネルでの最大値に反映されておらず、さらに閾値を越えた後に配列データの0クリアを行う構造になっており、閾値越え検出から本計測開始までに余計な時間がかかっていました。そこで、サンプリングの高速化と閾値越えから本計測開始までの時間を最小化できるように、簡易オシロで採用したリングバッファ構造を採用しました。
ロギング用プログラムを起動した直後にイベントが記録される。
リセット直後(ほぼ同時に)に閾値を越えていないイベントが複数回検出された。毎回検出されるわけではないが、記録量引きの少ないEEPROMを無駄に消費するので、これの対策を検討した。これが前段の件と関連して発生していた。どうもAD変換のレジスタにリセット直前の電位が残っていることがあるようだ。そこで、リセット直後の約1秒間はイベント情報をEEPROMに書き込まない仕様に変更しました。
隣のチャンネルの信号の影響を受けているように見える。
隣のセンサーに影響が出ないように衝撃を与えた時に隣のチャンネルの信号の影響を受けているように見えた。当初、AD変換で直前のチャネルの電位の影響を受けて信号を拾っているのではないかと調整を試したが改善しなかった。 顧みると実験室で検証したときよりより多くの隣のチャンネルの影響を受けているようだった。試しに実験室内での計測したチャンネル切り替えで隣のチャンネルの影響を受けていなかったプログラムに戻して検証すると確かに、実験室の時より隣のチャンネルの影響が大きいことを確認した。この結果から、AD変換チャンネル切り替えの問題ではなく、物理的な信号ラインの クロストークが発生していると判断した。 フィールドへ配置する際に配線を束ねており、実験室に比べて電磁的クロストークが発生しやすい状況になったと推測されるそこで、信号ライン同士をできるだけ物理的に離すように調整した。 それでもある程度影響が残るので、可能な範囲で波形観測もできるように、リングバッファのほかに波形保存用のウインドウバッファを使う仕様に拡張しました。RAMではあるが、最後の2回については保存できるようにしました。
最終ロギング用 Arduinoスケッチ
#include <EEPROM.h>
// 2026/01/08 update
//
const int NUM_CHANS = 4;
const int W_NUM = 2; // ウインドウ数
const int DISPLAY_POINTS = 50; // 画面に表示したい総行数
const int PRE_TRIGGER_SAMPLES = 5; // そのうちの20%(過去分)
const int POST_TRIGGER_SAMPLES = DISPLAY_POINTS - PRE_TRIGGER_SAMPLES; // 残りの80%(未来分)
const unsigned long RECORD_WINDOW_MS = 1000;
const int EEPROM_MAX = 1024;
const int LIMITSC = 500; // シリアルチェックのサンプルカウント間隔
const int BUFFER_SIZE = 1100; // バッファサイズ (1.1KB)
const int WBUFFER_SIZE = W_NUM * NUM_CHANS * DISPLAY_POINTS; // ウインドバッファサイズ (0.4KB) 2回x4chx50点
const int THRESHOLD = 5; // 閾値 (8bit: 0-255) ※約2.5V
int currentAddr = 0;
bool isMonitoring = false;
uint8_t maxValues[NUM_CHANS];
unsigned long windowStartTime;
unsigned long windowTime[W_NUM];
int samplecount = 0;
int sw = 0; //ウインド書き込み位置
byte buffer[BUFFER_SIZE];
byte wbuffer[WBUFFER_SIZE];
int head = 0; // バッファ書き込み位置
void setup() {
Serial.begin(115200);
ADCSRA &= ~(bit(ADPS2) | bit(ADPS1) | bit(ADPS0));
ADCSRA |= bit(ADPS2);
findNextAddr();
// 起動記録 (01: Reset)
saveEventLog(0x01, 0);
Serial.println(F("SYSTEM_READY"));
}
void loop() {
// 1. シリアル通信処理 (バイナリ同期プロトコル対応)
if (samplecount > LIMITSC) {
handleSerial();
samplecount = 0;
} else { ++samplecount; }
// --- 1. 定常サンプリング ---
int val = analogRead(A0 + (head % NUM_CHANS));
buffer[head] = (byte)(val >> 2);
// --- 2. トリガー判定 ---
if ( buffer[head] > THRESHOLD) {
capture(head);
// 送信が終わったら head をリセットして、次のトリガーに備える
}
head = (head + 1) % BUFFER_SIZE;
/*
// 2. 監視ロジック
unsigned long now = millis();
if (!isMonitoring) {
for (int ch = 0; ch < NUM_CHANS; ch++) {
uint8_t val = analogRead(A0 + ch ) >> 2;
if (val > THRESHOLDS[ch]) {
isMonitoring = true;
windowStartTime = now;
maxValues[ch] = val;
break;
}
}
} else {
for (int ch = 0; ch < NUM_CHANS; ch++) {
uint8_t val = analogRead(A0 + ch ) >> 2;
if (val > maxValues[ch]) maxValues[ch] = val;
}
if (now - windowStartTime >= RECORD_WINDOW_MS) {
saveLog03(now);
for (int i = 0; i < NUM_CHANS; i++) maxValues[i] = 0;
isMonitoring = false;
}
}
*/
}
//ウインドウバッファへのコピーとイベント登録
void capture(int tIndex) {
unsigned long now = millis();
// 1. 未来分をサンプリング
for (int i = 0; i < (POST_TRIGGER_SAMPLES * NUM_CHANS); i++) {
head = (head + 1) % BUFFER_SIZE;
int v = analogRead(A0 + (head % NUM_CHANS));
buffer[head] = (byte)(v >> 2);
}
// 2. ウイドウコピー開始位置の計算
int startOffset = (tIndex - (PRE_TRIGGER_SAMPLES * NUM_CHANS) + BUFFER_SIZE * 2) % BUFFER_SIZE;
startOffset = (startOffset / NUM_CHANS) * NUM_CHANS;
windowTime[sw] = now;
// 3. ウイドウコピー
for (int i = 0; i < NUM_CHANS; i++) maxValues[i] = 0;
for (int i = 0; i < DISPLAY_POINTS; i++) {
for (int ch = 0; ch < NUM_CHANS; ch++) {
int idx = (startOffset + (i * NUM_CHANS) + ch) % BUFFER_SIZE;
int wi = (sw * NUM_CHANS * DISPLAY_POINTS + (i * NUM_CHANS) + ch) % WBUFFER_SIZE;
wbuffer[wi] = buffer[idx];
if (buffer[idx] > maxValues[ch]) maxValues[ch] = buffer[idx];
}
//
}
saveLog03(now);
sw = (sw + 1) % W_NUM;
}
// --- EEPROM記録関数 ---
void findNextAddr() {
currentAddr = 0;
while (currentAddr <= EEPROM_MAX - 8) {
uint8_t tag = EEPROM.read(currentAddr);
if (tag != 0x01 && tag != 0x02 && tag != 0x03) break;
currentAddr += 8;
}
}
// 種別01(Reset), 02(Sync)用
// Syncの場合は data に UnixTime を入れる
void saveEventLog(uint8_t type, uint32_t data) {
if (currentAddr > EEPROM_MAX - 8) return;
EEPROM.update(currentAddr + 0, type);
// データ4バイト分 (UnixTimeなど)
EEPROM.update(currentAddr + 1, (uint8_t)(data & 0xFF));
EEPROM.update(currentAddr + 2, (uint8_t)((data >> 8) & 0xFF));
EEPROM.update(currentAddr + 3, (uint8_t)((data >> 16) & 0xFF));
EEPROM.update(currentAddr + 4, (uint8_t)((data >> 24) & 0xFF));
// millis上位3バイト
unsigned long ms = millis();
EEPROM.update(currentAddr + 5, (uint8_t)(ms >> 24));
EEPROM.update(currentAddr + 6, (uint8_t)(ms >> 16));
EEPROM.update(currentAddr + 7, (uint8_t)(ms >> 8));
currentAddr += 8;
}
// 種別03(Measurement)用
void saveLog03(unsigned long timestamp) {
if ((currentAddr > EEPROM_MAX - 8) || millis() < 1025 ) return;
EEPROM.update(currentAddr + 0, 0x03);
for(int i=0; i<NUM_CHANS; i++) EEPROM.update(currentAddr + 1 + i, maxValues[i]);
EEPROM.update(currentAddr + 5, (uint8_t)(timestamp >> 24));
EEPROM.update(currentAddr + 6, (uint8_t)(timestamp >> 16));
EEPROM.update(currentAddr + 7, (uint8_t)(timestamp >> 8));
currentAddr += 8;
}
void handleSerial() {
if (Serial.available() > 0) {
uint8_t firstByte = Serial.peek();
// バイナリ同期パケット (0x02) の処理
if (firstByte == 0x02) {
uint8_t buf[6];
if (Serial.readBytes(buf, 6) == 6) {
uint8_t checksum = (buf[1] + buf[2] + buf[3] + buf[4]) & 0xFF;
if (checksum == buf[5]) {
uint32_t unixTime = ((uint32_t)buf[4] << 24) | ((uint32_t)buf[3] << 16) | ((uint32_t)buf[2] << 8) | buf[1];
saveEventLog(0x02, unixTime);
Serial.println(F("OK: Sync Success"));
} else {
Serial.println(F("Error: Checksum Mismatch"));
}
}
}
// テキストコマンドの処理 (既存のRead/Clear)
else {
char cmd = Serial.read();
if (cmd == 'D') { // Dump
Serial.println(F("START_DUMP"));
for (int i = 0; i < EEPROM_MAX; i++) {
uint8_t b = EEPROM.read(i);
if (b < 16) Serial.print('0');
Serial.print(b, HEX);
if ((i + 1) % 16 == 0) Serial.println();
}
Serial.println(F("END_DUMP"));
}
else if (cmd == 'C') { // Clear
for (int i = 0; i < EEPROM_MAX; i++) EEPROM.update(i, 0xFF);
currentAddr = 0;
Serial.println(F("OK: EEPROM Cleared"));
}
else if (cmd == 'P') { // Peek Get 波形取得
Serial.println(F("START_PDUMP"));
// 時間情報の出力 (ここはOK)
for (int i = 0; i < W_NUM; i++) {
Serial.print(windowTime[i]);
Serial.println(); // 改行があった方が良いかもしれません
}
// 波形データの出力
// wbufferは [ウィンドウ数 * ポイント数 * チャンネル数] の1次元配列
for (int i = 0; i < WBUFFER_SIZE; i += NUM_CHANS) { // NUM_CHANSずつ進める
for (int ch = 0; ch < NUM_CHANS; ch++) {
Serial.print(wbuffer[i + ch]);
if (ch < NUM_CHANS - 1) Serial.print(",");
}
Serial.println();
}
Serial.println(F("END_PDUMP"));
}
}
}
}
上記のArduinoスケッチのほかに、PC側のPowerShellツール(同期、EEPROMログ回収、EEPROMクリア、波形データ回収)を用意しています。
読みだした生データ(EEPROM)
02E7085F690004AA0300000009004B42
01000000000000000308000000000000
0286325F690000380100000000000000
02C3395F690000400337000000000313
034600000000123F03030C000100189E
03000306000018AD03030602020018BE
03040702030018BFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
読みだしたデータ(波形分)(太文字は追記コメント)
1621795 ←1件目のイベント検出 millis
1621745 ←2件目のイベント検出 millis
0,1,0,0 ←ここから1件目の波形データ
0,0,0,0
0,0,1,3
0,1,0,0
4,4,0,0
1,7,2,0 ←ここで閾値越え
0,2,2,0
0,0,2,1
0,0,1,2
1,0,0,1
3,0,0,0
2,1,0,0
2,3,0,0
1,4,1,0
0,4,0,0
0,1,1,0
0,0,0,0
0,0,0,1
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0 ←ここのへんから2件目の波形データ
0,3,0,0
0,0,1,1
0,0,0,1
3,0,0,0
2,6,0,0 ←ここで閾値越え
0,4,1,0
0,2,2,1
0,0,1,1
0,0,1,2
1,0,0,1
1,0,0,1
3,1,0,0
2,2,0,0
1,2,0,0
0,3,0,0
0,1,0,0
0,1,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
本番運用開始直前の検証結果
[ EEPROM Data Analysis Report ]
Type | Date Time (UTC/Local) | Millis (raw) | Details
--------------------------------------------------------------------------------
SYNC | 2026-01-08 10:31:19 | 305664 | Time Synchronized (Unix:1767835879)
DATA | 2026-01-08 11:48:25 | 4932096 | CH0:0, CH1:0, CH2:0, CH3:9
RSET | 2026-01-08 13:28:39 | 0 | System Boot / Reset Detected
DATA | 2026-01-08 13:28:39 | 0 | CH0:8, CH1:0, CH2:0, CH3:0
SYNC | 2026-01-08 13:28:54 | 14336 | Time Synchronized (Unix:1767846534)
RSET | 2026-01-08 13:59:30 | 0 | System Boot / Reset Detected
SYNC | 2026-01-08 13:59:47 | 16384 | Time Synchronized (Unix:1767848387)
DATA | 2026-01-08 14:02:52 | 201472 | CH0:55, CH1:0, CH2:0, CH3:0
DATA | 2026-01-08 14:19:26 | 1195776 | CH0:70, CH1:0, CH2:0, CH3:0
DATA | 2026-01-08 14:26:23 | 1613312 | CH0:3, CH1:12, CH2:0, CH3:1
DATA | 2026-01-08 14:26:27 | 1617152 | CH0:0, CH1:3, CH2:6, CH3:0
DATA | 2026-01-08 14:26:32 | 1621504 | CH0:3, CH1:6, CH2:2, CH3:2
DATA | 2026-01-08 14:26:32 | 1621760 | CH0:4, CH1:7, CH2:2, CH3:3
--------------------------------------------------------------------------------
Analysis Finished.
イベント情報は、上のログ解析結果のように確認できるようになりました。
14:00以降は、センサーを叩いて信号を検知できるか検証しました。
センサーの固定方法(ピエゾ素子の振動を貼り付けたボンドが押さえている?)の問題か、実験室より感度が鈍くなったような気がします。本番運用と変更して、残りのセンサーを使って感度を上げられないかを別途検討してみます。
当初の計画どおりとまでは言えませんが、これで、本番運用を開始できました。










