AE測定、長期ロギングシステム構築 (その7) 運用開始編

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

ArduinoIDE
ArduinoIDE

 長期ロギング用の実装を事前準備していたのですが、実際にロギングを開始してみるといくつもの新たな課題を確認しました。それらの課題はつぎのとおりです。

  • 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以降は、センサーを叩いて信号を検知できるか検証しました。
センサーの固定方法(ピエゾ素子の振動を貼り付けたボンドが押さえている?)の問題か、実験室より感度が鈍くなったような気がします。本番運用と変更して、残りのセンサーを使って感度を上げられないかを別途検討してみます。

当初の計画どおりとまでは言えませんが、これで、本番運用を開始できました。


関連記事

AE測定、長期間ロギングシステム構築 (その6) 最終検証編

前回までで、センサーから信号を取り出し、Arduinoで複数チャンネルのAD変換してイベントをとらえる事前検討が完了しました。ここでは、長期ロギングに向けて最終的な調整をしていきます。チャンネルごとにセンサーの設置状況も違うので、センサーごとに異なる閾値を設定したほうが良いと推測されます。
 

閾値検討用元データの取得用 Arduinoスケッチ

const int NUM_CHANS = 4;
const int MAX_HIST_INDEX = 200;
unsigned int histogram[MAX_HIST_INDEX + 1][NUM_CHANS];

// タイムアウト設定 (2時間 = 7,200,000ms)
const unsigned long TIMEOUT_MS = 7200000;
unsigned long startTime;

void setup() {
Serial.begin(115200);
ADCSRA &= ~(bit(ADPS2) | bit(ADPS1) | bit(ADPS0));
ADCSRA |= bit(ADPS2);

for (int i = 0; i <= MAX_HIST_INDEX; i++) {
for (int j = 0; j < NUM_CHANS; j++) histogram[i][j] = 0;
}

Serial.println(F("Long-term Noise Analysis Started..."));
Serial.println(F("Target: 20,000 non-zero samples OR 2 hours."));
startTime = millis();
}

void loop() {
bool limitReached = false;
unsigned long nonZeroCount = 0; // 0以外の総サンプル数

while (!limitReached) {
// タイムアウトチェック
if (millis() - startTime >= TIMEOUT_MS) {
Serial.println(F("Time limit reached (2 hours)."));
limitReached = true;
break;
}

for (int ch = 0; ch < NUM_CHANS; ch++) {
int rawVal = analogRead(A0 + ch) >> 2;

// 0以外の場合のみカウント
if (rawVal > 0) {
int index = (rawVal > MAX_HIST_INDEX) ? MAX_HIST_INDEX : rawVal;
histogram[index][ch]++;
nonZeroCount++;

// 0以外のサンプルが合計20,000に達したら終了
if (nonZeroCount >= 20000) {
Serial.println(F("Non-zero sample limit reached."));
limitReached = true;
break;
}
}
}
}

// 結果の出力
Serial.println(F("Value,CH0,CH1,CH2,CH3"));
for (int i = 1; i <= MAX_HIST_INDEX; i++) { // 0は除外して出力
Serial.print(i);
if (i == MAX_HIST_INDEX) Serial.print("+");
for (int ch = 0; ch < NUM_CHANS; ch++) {
Serial.print(",");
Serial.print(histogram[i][ch]);
}
Serial.println();
}

Serial.print(F("Total Non-Zero Samples: ")); Serial.println(nonZeroCount);
Serial.print(F("Elapsed Time (ms): ")); Serial.println(millis() - startTime);
Serial.println(F("--- Analysis Finished ---"));
while (1);
}

開発環境(室内)での事前検証結果

Long-term Noise Analysis Started...
Target: 20,000 non-zero samples OR 2 hours.
Time limit reached (2 hours).
Value,CH0,CH1,CH2,CH3
1,0,2,2,1
2,0,2,0,0
3,0,0,0,1
4,0,0,0,0
5,0,0,0,0
6,0,0,0,0
7,0,0,0,0
8,0,0,0,0
9,0,0,0,0
10,0,0,0,0
11,0,0,0,0
12,0,0,0,0
13,0,0,0,0


186,0,0,0,0
187,0,0,0,0
188,0,0,0,0
189,0,0,0,0
190,0,0,0,0
191,0,0,0,0
192,0,0,0,0
193,0,0,0,0
194,0,0,0,0
195,0,0,0,0
196,0,0,0,0
197,0,0,0,0
198,0,0,0,0
199,0,0,0,0
200+,0,0,0,0
Total Non-Zero Samples: 8
Elapsed Time (ms): 7200214
--- Analysis Finished ---

この結果から、2時間放置してもノイズ信号のMax値は3であり、少しだけ余裕を見て閾値を5に設定すればよさそうだと判断しました。 そして次はフィールドテストへシフトしました。

フィールドテストでの追加検討

 機器類を整理して、実環境に配置して、閾値を再検討してみました。2時間の放置検証して、事前検証と同じ結果を得ました。 チャンネル別に閾値を設定する想定でしたが、かなり低い閾値設定なので、すべてのチャンネルとも5に設定することにします。

 これで、長期ロギングに向けた最終検討に移行できそうです。
次回は、最終検証、本番運用に移行していきます。


関連記事

AE測定、長期間ロギングシステム構築 (その5) 複数信号キャプチャ実験編

前回までで、センサーからの信号をArduinoでAD変換して簡易オシロで観測するところの事前検討が完了しました。最終的なシステム構成の確定に向けた検証をしていきます。
 ここでは、前回課題として見つけたチャンネル間クロストークの影響の見極めと使用チャンネル数の確定をします。2ch分の取り出した(AE)信号をAD変換して読み込む実験を行い、妥当な情報をロギングできそうかを検証していきます。
 前回の確認で4チャンネルでもそれなりの波形確認できそうなので、2回路分の実装と2回路分のダミー実装(半固定抵抗を通してGND接続)の合計4回路分を用意しました。

分圧回路
分圧回路

作成済みの2回路分を接続しました。

2ch
2ch

これに2ch分のAE(ピエゾ素子)をつないで、信号を検証しました。Arduinoスケッチは前回のものと同じです。

2chのAEセンサーを横並びに机に押し当てて、その左側、右側の机をペンで叩いて衝撃を計測してみました。

2ch


 A0チャンネルとA1チャンネルの信号の大きさが交互に変わっていることがわかります。また、 A2、A3チャンネルは 0のままで、影響を受けていないことが分かります。現状のチャンネル切り替えスピードで影響を受けずに計測できそうです。

4チャンネルでも行けそうなので、回路構成は4chで確定して進めることにしました。
つぎは、最終的な本番構成にして、動作確認して問題なければフィールドテストでスライスレベルの調整などを検討しましょう。

関連記事

AE測定、長期間ロギングシステム構築 (その4) 信号検出実験編

前回までで、センサーから信号を取り出すアナログ部分と、AD変換を行うArduinoの事前検討が完了しました。ここでは、取り出した(AE)信号をAD変換して読み込む実験を行い、妥当な情報をロギングできそうかを検証していきます。
 ここで、前出の記事( AD変換部 事前検証編 )で、検討したリセット問題に関連して新たな課題を理解しました。avrdudeで、Flash readデータの吸出しを行う予定にしていましたが、avrdudeを実行するとリセットが発生することが分かりました。このため、avrdudeを利用するなら電解コンデンサーを追加してリセット発生を抑制する方法は使えないことが分かりました。リセットを発生させない運用ならロギング実装の中にデータを吸い出す実装が必要ということになります。どちらを選択するかが課題として残ります。その選択は信号検出実験の後に再検討することにしましょう。

やりかたは、まずは1ch、そして最終ゴールを見据えて4ch分のデータ取得をやってみましょう。データは、とりあえずRAM上(変数)に保持して、データ転送し、シリアルプロッタで、プロットしてみます。 シリアルプロッタでの結果が、オシロスコープで見た波形と一致するかを見てみましょう。そして、データ取得の閾値や周期などを検討してみようという方針です。

AE信号出力の分圧回路の製作(手持ち部品から選抜)

前段のアンプは9V電源で、Max9Vの出力があります。Arduino Uno V3の耐圧というかアナログ信号入力の制限は0~5Vです。改めてオシロでアンプの出力をDCで確認すると、だいたい0~9Vの範囲で、振れていて、4.5Vあたりが中心電圧です。
オシロ 検出信号波形

osc_dc
osc_dc


時間もないので、手持ち部品から選抜して、できるだけ適した回路を作成してみます。
10kΩの半固定抵抗が4個あったので、これを使います。本番を想定して4回路分取る前提で、とりあえず1回路分を作成しました。

回路写真

分圧回路
分圧回路


これを使って、仮配線してみます。これで、システム全体の仮組み(仮SI)ができました。実験用のコードを動かして確認しましょう。プログラムは変数として使える2KBの範囲内で収まるようしました。要約すると、入力波形をAD変換しながら取得・蓄積して、閾値を超えたらPCに蓄積した波形をなげるというものです。

ロギングの検証

簡易オシロ Arduinoスケッチ(OSCsimulator.ino)

// --- 設定項目 ---
const int NUM_CHANS = 4;
const int BUFFER_SIZE = 1500; // バッファサイズ (1.5KB)
const int TRIGGER_CH = 0; // A0をトリガーに設定
const int THRESHOLD = 30; // 閾値 (8bit: 0-255) ※約2.5V
//const int PRE_TRIGGER_SAMPLES = 200; // トリガー前に何セット分表示するか
const int DISPLAY_POINTS = 50; // 画面に表示したい総行数
const int PRE_TRIGGER_SAMPLES = 5; // そのうちの20%(過去分)
const int POST_TRIGGER_SAMPLES = 50; // 残りの80%(未来分)

byte buffer[BUFFER_SIZE];
int head = 0; // 書き込み位置

void setup() {
Serial.begin(115200);

// ADCの高速化 (Prescaler 16 = 約1MHzのADCクロック)
// 分割比を16に設定することで、4chでも実用的な速度を確保します。
ADCSRA &= ~(bit(ADPS2) | bit(ADPS1) | bit(ADPS0));
ADCSRA |= bit(ADPS2);

Serial.println("System Ready. Waiting for trigger on A0...");
}

void loop() {
// --- 1. 定常サンプリング ---
int val = analogRead(A0 + (head % NUM_CHANS));
buffer[head] = (byte)(val >> 2);

// --- 2. トリガー判定 ---
if ((head % NUM_CHANS) == TRIGGER_CH && buffer[head] > THRESHOLD) {
captureAndSend(head);
// 送信が終わったら head をリセットして、次のトリガーに備える
head = 0;
// 少し待機(連続トリガーによるチャタリング防止)
delay(500);
}

head = (head + 1) % BUFFER_SIZE;
}

void captureAndSend(int tIndex) {
// 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. 画面を一度リセットするために「空のデータ」を少し送る
// これにより前の波形と新しい波形の間に隙間ができます
for (int i = 0; i < 50; i++) {
Serial.println("0,0,0,0");
}

// 3. 送信開始位置の計算
int startOffset = (tIndex - (PRE_TRIGGER_SAMPLES * NUM_CHANS) + BUFFER_SIZE * 2) % BUFFER_SIZE;
startOffset = (startOffset / NUM_CHANS) * NUM_CHANS;

// 4. データ送信
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;
Serial.print(buffer[idx]);
if (ch < NUM_CHANS - 1) Serial.print(",");
}
Serial.println();
// ここでdelayを入れると、波形が描画される「スピード」を調整できます
delayMicroseconds(500);
}
}

この仮の簡易オシロ スケッチで取得したシリアルプロットでの波形の観測動画です。 

AE仮配線観測

 とりあえずこれで、Arduinoで波形を観測できそうなことを確認できました。A0チャンネルの1回路にだけ接続して他はフリー状態ですが、A0の影響を受けて波形を観測しており、他のチャンネルを接続したとき影響を受けないか確認が必要そうです。つぎは、ほぼ本番の接続状態で、複数チャンネルでの観測を試しましょう。


関連記事

AE測定、長期間ロギングシステム構築 (その3の2) AD変換部 事前検証編

前回までで、センサーから信号をとりだすアナログ部分の仮構築が完了しました。ここからは、取り出したAE信号をAD変換してロギングする部分を構築していきます。
前出(アナログ部製作編)の採用するシステム構成の方針により、ロギングシステムの中核につぎのパーツを採用しました。

AD変換&ロギング部

[107385]Arduino Uno Rev3
https://akizukidenshi.com/catalog/g/g107385/

事前検討方針

 ドキュメントレベルの調査結果から、次の課題があることを確認しました。
・USBケーブル挿抜で再起動される(運用上、 通常ロギング時は単体で動作し、データ吸い上げ時だけUSB接続する運用にする必要がある)
・再起動でRAMはリセットされ、EEPROMはリセットされないが書き込み回数上限制限がある(カウンタ等の頻繁に更新される変数保存ができない)
・EEPROMの容量が小さい(長期間ロギングのため、できるだけ大きいデータ領域を確保したい)

これらの問題を、解決する必要があります。そこで2つめと3つめの課題は、次回検討するプログラミング実装レベルで解決することにしました。ここでは、USBケーブル挿抜に関わる挙動の確認と対策、そして再起動されていないかどうかについて確認する方法を検討することにしました。

Arduino IDE をインストールするPCのスペック確認

Arduino IDE に必要なスペックを確認しておく。Windows 10以降のPCならほぼ大丈夫。
問題なければ、インストール。
Arduino 公式ソフトウェアページ

動作確認

PCにArduinoを繋ぎ、デバイスマネージャーの「ポート(COMとLPT)」に「Arduino Uno」が表示されるか確認する。

USBを挿す前は Port のさきに COM3などが見えていましたが、 USBをさすとPortがグレーアウトされました。
Board: ”Arduino Uno” → Arduino AVR Boardsで Arduino Unoを選択しました。
→ これでは、まだ認識していないようです。
USBを一度抜挿して確認、これでもまだ認識していないようだが、 再度開きなおすと
下のキャプチャのようにCOM4(Arduino Uno)が表示される。

最初の動作確認

出荷時は、次のサンプルが動いているようです。
サンプルを開く:
「ファイル」→「スケッチ例」→「01.Basics」→ 「Blink」

delay(100);  など、任意の時間に変更して、
再度、画面左上の 「→(書き込み)」ボタン を押すと、コードを書き込んで点滅間隔がかわる。

Arduino Unoの動作状況確認

Arduino Unoは基本1方向らしい、吸出しなどはavrdude コマンドらしい。

“C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avrdude.exe” -C “C:\Program Files (x86)\Arduino\hardware\tools\avr\etc\avrdude.conf” -v -p atmega328p -c arduino -P COM4 -b 115200 -U flash:r:backup.hex:i

ユーザーパスにインストールしていたので、avrdude.exeは次のパスにありました。
C:\Users\<ユーザー名>demo\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17\bin

パラメータを確認
> .\avrdude.exe -?
Usage: avrdude.exe [options]
Options:
-p <partno> Required. Specify AVR device.
-b <baudrate> Override RS-232 baud rate.
-B <bitclock> Specify JTAG/STK500v2 bit clock period (us).
-C <config-file> Specify location of configuration file.
-c <programmer> Specify programmer type.
-D Disable auto erase for flash memory
-i <delay> ISP Clock Delay [in microseconds]
-P <port> Specify connection port.
-F Override invalid signature check.
-e Perform a chip erase.
-O Perform RC oscillator calibration (see AVR053).
-U <memtype>:r|w|v:<filename>[:format]
Memory operation specification.
Multiple -U options are allowed, each request
is performed in the order specified.
-n Do not write anything to the device.
-V Do not verify.
-u Disable safemode, default when running from a script.
-s Silent safemode operation, will not ask you if
fuses should be changed back.
-t Enter terminal mode.
-E <exitspec>[,<exitspec>] List programmer exit specifications.
-x <extended_param> Pass <extended_param> to programmer.
-y Count # erase cycles in EEPROM.
-Y <number> Initialize erase cycle # in EEPROM.
-v Verbose output. -v -v for more.
-q Quell progress output. -q -q for less.
-l logfile Use logfile rather than stderr for diagnostics.
-? Display this usage.

avrdude version 6.3-20190619, URL: <http://savannah.nongnu.org/projects/avrdude/>

■flash(プログラム領域)の吸い上げ の試行
※参考にする場合はパス(特に赤字)を環境に合わせてください。

.\avrdude.exe "-CC:\Users\<ユーザ名>\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf" -v -p atmega328p -c arduino "-PCOM4" -b115200 -U flash:r:backup.hex:i

avrdude.exe: Version 6.3-20190619
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2014 Joerg Wunsch

System wide configuration file is "C:\Users\<ユーザ名>\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf"

Using Port : COM4
Using Programmer : arduino
Overriding Baud Rate : 115200
AVR Part : ATmega328P
Chip Erase delay : 9000 us
PAGEL : PD7
BS2 : PC2
RESET disposition : dedicated
RETRY pulse : SCK
serial program mode : yes
parallel program mode : yes
Timeout : 200
StabDelay : 100
CmdexeDelay : 25
SyncLoops : 32
ByteDelay : 0
PollIndex : 3
PollValue : 0x53
Memory Detail :

Block Poll Page Polled
Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00
signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00

Programmer Type : Arduino
Description : Arduino
Hardware Version: 3
Firmware Version: 4.4
Vtarget : 0.3 V
Varef : 0.3 V
Oscillator : 28.800 kHz
SCK period : 3.3 us

avrdude.exe: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.02s

avrdude.exe: Device signature = 0x1e950f (probably m328p)
avrdude.exe: safemode: lfuse reads as 0
avrdude.exe: safemode: hfuse reads as 0
avrdude.exe: safemode: efuse reads as 0
avrdude.exe: reading flash memory:

Reading | ################################################## | 100% 5.22s

avrdude.exe: writing output file "backup.hex"

avrdude.exe: safemode: lfuse reads as 0
avrdude.exe: safemode: hfuse reads as 0
avrdude.exe: safemode: efuse reads as 0
avrdude.exe: safemode: Fuses OK (E:00, H:00, L:00)

avrdude.exe done. Thank you.

上の実行の結果、ファイルが実行パスに出力されていることを確認しました。
面白くなってきました。
このファイルのサイズは78KBで、テキストエディターでも見える形式です。
行頭に”:”があり4byte分アドレス相当の情報、実データ、最後に1byteチェックサムがついているようですね。

つぎにEEPROMの中身を読みだしてみます。

> .\avrdude.exe "-CC:\Users\<ユーザ名>\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf" -v -p atmega328p -c arduino "-PCOM4" -b115200 -U eeprom:r:eeprom.hex:i

avrdude.exe: Version 6.3-20190619
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2014 Joerg Wunsch

System wide configuration file is "C:\Users\<ユーザ名>\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf"

Using Port : COM4
Using Programmer : arduino
Overriding Baud Rate : 115200
AVR Part : ATmega328P
Chip Erase delay : 9000 us
PAGEL : PD7
BS2 : PC2
RESET disposition : dedicated
RETRY pulse : SCK
serial program mode : yes
parallel program mode : yes
Timeout : 200
StabDelay : 100
CmdexeDelay : 25
SyncLoops : 32
ByteDelay : 0
PollIndex : 3
PollValue : 0x53
Memory Detail :

Block Poll Page Polled
Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00
signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00

Programmer Type : Arduino
Description : Arduino
Hardware Version: 3
Firmware Version: 4.4
Vtarget : 0.3 V
Varef : 0.3 V
Oscillator : 28.800 kHz
SCK period : 3.3 us

avrdude.exe: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.02s

avrdude.exe: Device signature = 0x1e950f (probably m328p)
avrdude.exe: safemode: lfuse reads as 0
avrdude.exe: safemode: hfuse reads as 0
avrdude.exe: safemode: efuse reads as 0
avrdude.exe: reading eeprom memory:

Reading | ################################################## | 100% 3.11s

avrdude.exe: writing output file "eeprom.hex"

avrdude.exe: safemode: lfuse reads as 0
avrdude.exe: safemode: hfuse reads as 0
avrdude.exe: safemode: efuse reads as 0
avrdude.exe: safemode: Fuses OK (E:00, H:00, L:00)

avrdude.exe done. Thank you.

eepromのデータを読み出せました。ファイルサイズは3kbです。ですが、中身はFlashメモリの冒頭部分と同じようでした。 確認のため、 eeprom_clearというサンプルをアップロードして確認してみます。→結果、再度読み込んだファイルの中身は、変わりませんでした。 上のEEPROMの読み出しは機能していないようです。失敗しているよう。

サンプルに、eeprom_clearや eeprom_readがあるのでこれを使ってみます。eeprom_readをアップロードして確認してみます。右上のシリアルモニタとシリアルプロッタを使って、確認すると。EEPROMの1kb分が0で帰ってきました。

基本的な動きが確認できたので、ここでの検証課題のUSB挿抜でのリセット発生状況の確認と回避確認です。
と、ここで、想定外の情報、Arduino IDE(PC側)のシリアルモニタを開く操作をすると、Arduinoボード本体に自動的にリセットがかかるらしい、USB挿抜以外にもチェックするべき観点が増えた。
要約すると、おもに電源に関連する不安定要素によるものと、 通信制御によるリセットの2つで、通信制御によるものはUSB挿抜と、DTR/RTS信号によるオートリセットということらしい。電源に関してはACアダプターでの安定供給と、シールド対策をするとして、USB挿抜とDTR/RTS信号によるリセットされることについては「 RESET-GND間に10μFのコンデンサを挟む」対策をする。

 さて、要因は1つ増えたが、対策は増えていないので、方針は変えずに先に進めよう。
 さらに追加情報をみつけた、「リセット理由の切り分けは MCUSR (MCU Status Register) レジスタ」でできるらしい。

MCUSRレジスタのビットマップ解説
このレジスタの 下位4ビット がリセット要因を示します。
ビット番号 ビット名 名称 内容
Bit 3 WDRF ウォッチドッグ・リセット ウォッチドッグタイマーのタイムアウトによるリセット
Bit 2 BORF ブラウンアウト・リセット 電源電圧が規定値を下回ったことによるリセット(電源不安定が原因)
Bit 1 EXTRF 外部リセット RESETピンを「LOW」にしたことによるリセット(DTR信号/物理ボタンが原因)
Bit 0 PORF パワーオン・リセット 電源が完全にOFFの状態からONになった時のリセット

本番向け実装も考慮して、MCUSRレジスタも含めてデータ構造設計をします。方針は、あらかじめデータ保存領域を00クリアしておきます。データ登録はブロック単位でおこない、更新は追記のみで上書きはしないものとします。
データブロック構造を次のように定義する。
00 00 00 00 00
最初の1byteを、種別フラグとする。
意味を次のようにする。
00: 未使用
01: リセットデータ
2byte目:MCUSRレジスタ
02: 同期データ
2byte目:クロックタイマー
6?byte目:時刻データ
03: 未定義
1x: チャンネルデータ
  2byte目:クロックタイマー
6?byte目:AD変換データ

とりあえず、LED点滅回数でリセット回数が分かるようにしましょう

時刻データは、PCから受信を想定しています。
EEPROMの容量と1ブロックのサイズは、クロックタイマー+時刻データと クロックタイマー+AD変換データのうちの大きいほうを採用して決めます

LED点滅による通知仕様
リセット回数が5回なら 0.2秒間隔で5回点滅した後、2秒消えた後、5回点滅という繰り返しにします。

できるだけ長期間のロギングをしたいために、無駄なデータ領域を消費しないようにする。
なお、リセット回数は、 EEPROMにどこまで書き込まれたかとリセット回数をカウントアップして保持して、データ追加と点滅回数に使用します
1ブロック 8バイト固定の決定案

Unix Timeは4バイト(32bit)すべてを格納。

バイト01234567備考
01 (Reset)01MCUSR<—millis(4B)—>0000最もシンプルな構成
02 (Sync)02<—Unix Time(4B)—><—millis(4Bの一部)—>※後述
1x (Data)1x00<—millis(4B)—><—AD値(2B)—>

1. EEPROM走査関数: 起動時に先頭から8バイトずつ読み、01の数を数えつつ、最初の00を見つける。

2. リセット記録: setup()の最初で01レコード(MCUSR付き)を追記。

3. LED点滅関数: loop()内でmillis()を監視し、カウントされた回数分だけ点滅(delayを使わない)。

4. PC同期受信: シリアルで 0x02 を受信したら、続く4バイトをUnix Timeとして処理。

※補足:3ヶ月ロギングで1KBのEEPROM(128回分)が溢れるのが心配な場合は、追記時に「これ以上書かない」というガードも入れる。


1.書き込み位置特定: 8バイトすべてが 0x00(完全な未使用領域)であることを条件に、追記位置を決定します。

2.時刻同期(02): PCから [0x02][UnixTime 4B][CheckSum 1B] の計6バイトを受信する仕様とします。チェックサムはUnixTime 4バイトの加算下位1バイトとします。

3.データ構造の遵守:
 1. 01(Reset): [01][MCUSR][00 00 00 00 00 00] (計8バイト)
2. 02(Sync): [02][UnixTime 4B][millis 3B(上位)]
※02にのみ、PC時刻とArduino内時計を紐付けるための基準点としてmillisを保持します。

ここまで作業が進んだところで、EEPROMの読み込み問題に気が付いたので先に書いた対策を実施しました。詳しくはこちらを参照してください→「Arduino Uno 事前検証過程(その3の1)でのEEPROM読み出し問題の回避策

本体実装 (Arduinoスケッチ ResetDetector.ino)

まず、リセットと同期の実装を入れた実験版です。

/*
* Reset logging PreCode
*/

#include <EEPROM.h>
#include <avr/wdt.h>

// --- 定数定義 ---
const int BLOCK_SIZE = 8;
const int EEPROM_LIMIT = 1024;
const int LED_PIN = 13;

int totalResetCount = 0;
int nextAddr = 0;
byte lastMcusr = 0;

void setup() {
lastMcusr = MCUSR;
MCUSR = 0; // 次回のためにクリア

Serial.begin(9600);
pinMode(LED_PIN, OUTPUT);

// 1. 書き込み位置の特定(全8バイトが0x00であることを確認)
findNextAddrAndCount();

// 2. リセット記録(設計案通り、millisを含まない構成)
recordReset();
}

void loop() {
updateLED(); // LED点滅処理(非ブロッキング)
checkSerial(); // PCからの同期信号(02)待ち
}

// 課題1:書き込み位置特定(種別00かつ全データ00を判定)
void findNextAddrAndCount() {
nextAddr = -1;
totalResetCount = 0;

for (int i = 0; i < EEPROM_LIMIT; i += BLOCK_SIZE) {
bool allZero = true;
byte type = EEPROM.read(i);

if (type == 0x01) totalResetCount++;

// 全8バイトが0x00かチェック
for (int j = 0; j < BLOCK_SIZE; j++) {
if (EEPROM.read(i + j) != 0x00) {
allZero = false;
break;
}
}

if (allZero && nextAddr == -1) {
nextAddr = i;
}
}
if (nextAddr == -1) nextAddr = 0; // 満杯時は先頭(運用に合わせて要調整)
}

// 課題4:当初案通りのリセット記録(余計なmillisは排除)
void recordReset() {
if (nextAddr + BLOCK_SIZE > EEPROM_LIMIT) return;

EEPROM.update(nextAddr, 0x01); // 種別フラグ
EEPROM.update(nextAddr + 1, lastMcusr); // MCUSR(補助情報)
for (int i = 2; i < BLOCK_SIZE; i++) {
EEPROM.update(nextAddr + i, 0x00); // 残りは00埋め
}

nextAddr += BLOCK_SIZE;
// 点滅用にカウントを更新
totalResetCount++;
}

// 課題2:時刻同期(チェックサム付き)
void checkSerial() {
if (Serial.available() >= 6) {
if (Serial.read() == 0x02) {
uint32_t unixTime = 0;
byte checksum = 0;

for (int i = 0; i < 4; i++) {
byte b = Serial.read();
unixTime |= ((uint32_t)b << (i * 8));
checksum += b;
}

byte receivedSum = Serial.read();
if (checksum == receivedSum) {
saveSyncData(unixTime);
Serial.println("OK: Sync Success");
} else {
Serial.println("Error: Checksum Mismatch");
}
}
}
}

void saveSyncData(uint32_t ut) {
if (nextAddr + BLOCK_SIZE > EEPROM_LIMIT) return;

uint32_t currentMs = millis();

EEPROM.update(nextAddr, 0x02); // 種別フラグ

// UnixTime (4byte) を記録
for (int i = 0; i < 4; i++) {
EEPROM.update(nextAddr + 1 + i, (ut >> (i * 8)) & 0xFF);
}

// millis() の上位3バイトを記録 (Byte 5, 6, 7)
// 下位8bit(0-255ms)を切り捨てることで、0.25秒精度の同期を実現
EEPROM.update(nextAddr + 5, (currentMs >> 8) & 0xFF);
EEPROM.update(nextAddr + 6, (currentMs >> 16) & 0xFF);
EEPROM.update(nextAddr + 7, (currentMs >> 24) & 0xFF);

nextAddr += BLOCK_SIZE;
Serial.println("OK: Sync Data Recorded (0.25s precision)");
}

void updateLED() {
static unsigned long lastUpdate = 0;
static int flashStep = 0;
static int currentFlash = 0;
static bool pausing = false;

unsigned long now = millis();

if (pausing) {
if (now - lastUpdate >= 2000) {
pausing = false;
currentFlash = 0;
lastUpdate = now;
}
return;
}

if (now - lastUpdate >= 200) {
lastUpdate = now;
if (currentFlash < totalResetCount) {
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
flashStep++;
if (flashStep >= 2) {
flashStep = 0;
currentFlash++;
}
} else {
digitalWrite(LED_PIN, LOW);
pausing = true;
}
}
}

同期実装(PowerShell sync.ps1)

同期処理を行うのに使用します。 ファイルを右クリックして、「PowerShellで実行」をくちっくして使用します

$ErrorActionPreference = "Stop"  # 全てのエラーを「中断(catchに飛ばす)」対象にする
# --- 設定項目 ---
$portName = "COM4" # Arduinoが接続されているポートを確認して変更してください
$baudRate = 9600

try {
# シリアルポートの初期化
$port = New-Object System.IO.Ports.SerialPort($portName, $baudRate, "None", 8, "One")
$port.Open()
Start-Sleep -Seconds 2 # Arduinoのリセット待ち(DTRによるリセット対策)

# 1. 現在のUnix Time(1970/1/1からの経過秒)を取得
$unixTime = [DateTimeOffset]::Now.ToUnixTimeSeconds()

# 2. 4バイトのバイナリ配列に変換 (リトルエンディアン)
$utBytes = [BitConverter]::GetBytes([uint32]$unixTime)

# 3. チェックサムの計算 (4バイトの加算下位1バイト)
[byte]$checksum = 0
foreach ($b in $utBytes) {
$checksum = ($checksum + $b) -band 0xFF
}

# 4. 送信データの組み立て [0x02] + [Data 4B] + [Checksum 1B]
[byte[]]$sendBuffer = @(0x02) + $utBytes + @($checksum)

# 5. 送信
$port.Write($sendBuffer, 0, $sendBuffer.Length)

Write-Host ("送信成功: UnixTime = {0} (CheckSum: 0x{1:X2})" -f $unixTime, $checksum)
Write-Host ("送信バイナリ: " + [System.BitConverter]::ToString($sendBuffer))

# Arduinoからの応答("OK: Sync Success"など)を確認
Start-Sleep -Milliseconds 500
if ($port.BytesToRead -gt 0) {
$response = $port.ReadExisting()
Write-Host "Arduino応答: $response"
}

}
catch {
Write-Error "エラーが発生しました: $_"
}
finally {
if ($port -and $port.IsOpen) {
$port.Close()
Write-Host "ポートを閉じました。"
}
}


# --- スクリプトの最後に追加 ---
Write-Host "`nEnterキーを押すと終了します..."
Read-Host

実験結果

同期処理の実行結果は次の通り

送信成功: UnixTime = 1767166738 (CheckSum: 0xA2)
送信バイナリ: 02-12-D3-54-69-A2
Arduino応答: OK: Sync Data Recorded (0.25s precision)
OK: Sync Success

ポートを閉じました。

Enterキーを押すと終了します...

リセットの発生状況の検証を実施しました。 USBの挿抜では、リセットは発生していないようです。
9V ACアダプタ電源のコンセント抜き差しでも、リセット発生していません。電源供給の9V ACアダプタとUSBの切り替えがうまくいっているようです。つまり、ここまでは、LEDの点滅が1回の状態が続いています。
 つぎに、USBと9V ACアダプタ電源のコンセント両方を抜きました。再度刺したところで、LEDは2回点滅に変わりました。これは、想定どおりです。ということで、USBの挿抜でのリセット発生は必ず起こるものではなさそうです。
このあと、一度同期をおこなって、追加で「DTR/RTS信号によるオートリセット」を強制的に行ってみます。

検証用コード (PowerShell: reset.ps1)

# --- 設定 ---
$portName = "COM4"
$baudRate = 115200

try {
# 1. ポートの初期化
$port = New-Object System.IO.Ports.SerialPort($portName, $baudRate, "None", 8, "One")

# 2. ポートを開く (この時点で多くのボードは一度リセットされます)
$port.Open()
Write-Host "Port $portName Opened."

# 3. DTR/RTSを操作して明示的にリセットをかける
# 一旦両方をFalse(High電位)にしてからTrue(Low電位)にする
Write-Host "Resetting Arduino..."
$port.DtrEnable = $false
$port.RtsEnable = $false
Start-Sleep -Milliseconds 200 # 信号を安定させるための待ち時間

$port.DtrEnable = $true
$port.RtsEnable = $true

Write-Host "Reset signal sent."

# 4. ブートローダーが立ち上がるまで少し待つ
Start-Sleep -Seconds 2
Write-Host "Arduino should be restarted now."

}
catch {
Write-Error "Error: $($_.Exception.Message)"
}
finally {
if ($port -and $port.IsOpen) {
$port.Close()
Write-Host "Port Closed."
}
}

Write-Host "Press Enter to exit..."
Read-Host

実行結果は次の通り。 このリセット後は、LEDの点滅は3回になりました。 設計通りの挙動です。  このあと、もう一度同期して一旦実験完了です。 

Port COM4 Opened.
Resetting Arduino...
Reset signal sent.
Arduino should be restarted now.
Port Closed.
Press Enter to exit...

それでは、EEPROMにロギングした結果を吸い出して検証してみましょう。

:100000000100000000000000023AD25469D50F0040
:1000100001000000000000000212D35469CA000071
:10002000010000000000000002CCDD54697B0400E8
:1000300000000000000000000000000000000000C0
:1000400000000000000000000000000000000000B0
:1000500000000000000000000000000000000000A0
:100060000000000000000000000000000000000090
:100070000000000000000000000000000000000080
:100080000000000000000000000000000000000070
:100090000000000000000000000000000000000060
:1000A0000000000000000000000000000000000050
:1000B0000000000000000000000000000000000040
:1000C0000000000000000000000000000000000030
:1000D0000000000000000000000000000000000020
:1000E0000000000000000000000000000000000010
:1000F0000000000000000000000000000000000000
:1001000000000000000000000000000000000000EF
:1001100000000000000000000000000000000000DF
:1001200000000000000000000000000000000000CF
:1001300000000000000000000000000000000000BF
:1001400000000000000000000000000000000000AF
:10015000000000000000000000000000000000009F
:10016000000000000000000000000000000000008F
:10017000000000000000000000000000000000007F
:10018000000000000000000000000000000000006F
:10019000000000000000000000000000000000005F
:1001A000000000000000000000000000000000004F
:1001B000000000000000000000000000000000003F
:1001C000000000000000000000000000000000002F
:1001D000000000000000000000000000000000001F
:1001E000000000000000000000000000000000000F
:1001F00000000000000000000000000000000000FF
:1002000000000000000000000000000000000000EE
:1002100000000000000000000000000000000000DE
:1002200000000000000000000000000000000000CE
:1002300000000000000000000000000000000000BE
:1002400000000000000000000000000000000000AE
:10025000000000000000000000000000000000009E
:10026000000000000000000000000000000000008E
:10027000000000000000000000000000000000007E
:10028000000000000000000000000000000000006E
:10029000000000000000000000000000000000005E
:1002A000000000000000000000000000000000004E
:1002B000000000000000000000000000000000003E
:1002C000000000000000000000000000000000002E
:1002D000000000000000000000000000000000001E
:1002E000000000000000000000000000000000000E
:1002F00000000000000000000000000000000000FE
:1003000000000000000000000000000000000000ED
:1003100000000000000000000000000000000000DD
:1003200000000000000000000000000000000000CD
:1003300000000000000000000000000000000000BD
:1003400000000000000000000000000000000000AD
:10035000000000000000000000000000000000009D
:10036000000000000000000000000000000000008D
:10037000000000000000000000000000000000007D
:10038000000000000000000000000000000000006D
:10039000000000000000000000000000000000005D
:1003A000000000000000000000000000000000004D
:1003B000000000000000000000000000000000003D
:1003C000000000000000000000000000000000002D
:1003D000000000000000000000000000000000001D
:1003E000000000000000000000000000000000000D
:1003F00000000000000000000000000000000000FD
:00000001FF

ほぼ想定の結果が得られました。比較的うまくいっているようです。これからわかることは、01 (reset)の次はすべて00で、MCUSRレジスタから得られた情報はなさそう。
同期情報もしっかり記録されており、同期も機能していそうです。
 事前検証はこれで、完了とします。つぎはいよいよ、AD変換でのデータ収集の実験に入っていきます。

参考サイト:

avrdude の使い方 (電子工作の実験室)
Arduinoのプログラムを吸い出す方法 (Lang-ship)


関連記事




Arduino Uno 事前検証過程(その3の1)でのEEPROM読み出し問題の回避策

暫定でEEPROMにログ保存して、読み出しを検証しようかとEEPROMをavrdudeコマンドで読みだそうとしたが、何をとち狂ったかFlashメモリの冒頭部分を読みだしていることに気が付いた。回避策もないようなので、Arduino スケッチ(Arduino に書き込むプログラム)とPowerShell実装の組み合わせで対処することにした。
 Arduinoの仕組みのせいか、なかなかめんどくさい。


目的

Arduino UNO 上の EEPROM 内容を確実に取得する。
avrdude(ブートローダ経由)では EEPROM/Flash の取り違えや挙動差があるため、
Arduino 自身に EEPROM を読ませて PC に転送する方式を採用する。


全体構成

  • Arduino 側
    EEPROM → Intel HEX 形式 → UART(USB CDC)送信
  • PC 側
    UART 受信 → 同期マーカー判定 → HEX ファイルとして保存

Arduino 側の動作原理

  1. EEPROM.read() を用いて EEPROM アドレス空間を順次走査
  2. 読み出したバイト列を Intel HEX レコードとして整形
  • アドレス
  • データ長
  • チェックサムを含む
  1. UART(Serial)へ ASCIIテキストとして出力

同期制御

  • データ送信の前後に明示的なマーカーを出力
EEPROM_BEGIN
:10....
:10....
...
:00000001FF
EEPROM_END

これにより PC 側は、

  • 受信開始点
  • 正常終了点
    を確実に判定できる。

再送設計

  • 送信処理は一定周期(例:20秒)で繰り返す
  • PC 側の起動タイミングや USB 再接続に依存しない

PC(PowerShell)側の動作原理

  1. System.IO.Ports.SerialPort で COM ポートをオープン
  2. 行単位(ReadLine())で受信
  3. 受信内容を以下のルールで処理
受信内容動作
EEPROM_BEGIN書き込み開始
: で始まる行HEX レコードとして保存
EEPROM_END書き込み終了・クローズ
  1. ファイルは スクリプトの配置ディレクトリに生成
    $PSScriptRoot を基準に絶対パス化)

この方式のポイント

  • EEPROM と Flash の経路を完全に分離
  • 読み出し主体は MCU 本体
  • PC 側は「単なる受信・保存」
  • ブートローダ/avrdude 非依存
  • テキスト転送
  • ロジアナ/ターミナルで可視
  • 途中破損の検出が容易
  • 再送前提設計
  • 信頼性は通信層ではなく運用で確保


Arduinoスケッチ(EEPROMreader.ino)

#include <EEPROM.h>

const unsigned long INTERVAL = 20000;

uint8_t checksum(uint8_t *buf, uint8_t len) {
uint16_t sum = 0;
for (uint8_t i = 0; i < len; i++) sum += buf[i];
return (uint8_t)(-sum);
}

void sendHexLine(uint16_t addr) {
uint8_t rec[5 + 16];
rec[0] = 16; // length
rec[1] = addr >> 8;
rec[2] = addr & 0xFF;
rec[3] = 0x00; // record type

for (int i = 0; i < 16; i++) {
rec[4 + i] = EEPROM.read(addr + i);
}

uint8_t cs = checksum(rec, 4 + 16);

Serial.print(':');
for (int i = 0; i < 4 + 16; i++) {
if (rec[i] < 0x10) Serial.print('0');
Serial.print(rec[i], HEX);
}
if (cs < 0x10) Serial.print('0');
Serial.println(cs, HEX);
}


void setup() {
Serial.begin(115200);
while (!Serial) { ; }
}

void loop() {
Serial.println("EEPROM_BEGIN");
for (uint16_t addr = 0; addr < 1024; addr += 16) {
sendHexLine(addr);
}
Serial.println(":00000001FF"); // EOF
Serial.println("EEPROM_END");

digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(200); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(INTERVAL);
}

PowerShell(eepromGet.ps1)

$port = New-Object System.IO.Ports.SerialPort COM4,115200,None,8,one
$port.ReadTimeout = 5000
$port.Open()

#$outFile = "eeprom.hex"
#$sw = New-Object System.IO.StreamWriter $outFile
$outFile = Join-Path $PSScriptRoot "eeprom.hex"
$sw = New-Object System.IO.StreamWriter($outFile)
$sw.AutoFlush = $true

Write-Host "Waiting for EEPROM_BEGIN..."

# ===== BEGIN待ち =====
while ($true) {
try {
$line = $port.ReadLine().Trim()
if ($line -eq "EEPROM_BEGIN") {
Write-Host "BEGIN received"
break
}
} catch {
# timeout → 何もしないで待ち続ける
}
}

# ===== データ受信 =====
while ($true) {
try {
$line = $port.ReadLine().Trim()

if ($line -eq "EEPROM_END") {
Write-Host "END received"
break
}

$sw.WriteLine($line)
Write-Host $line

} catch {
# timeout 継続
}
}

$sw.Close()
$port.Close()

Write-Host "DONE"

初期状態で得られる結果

eeprom.hex

:1000000000000000000000000000000000000000F0
:1000100000000000000000000000000000000000E0
:1000200000000000000000000000000000000000D0
:1000300000000000000000000000000000000000C0
:1000400000000000000000000000000000000000B0
:1000500000000000000000000000000000000000A0
:100060000000000000000000000000000000000090
:100070000000000000000000000000000000000080
:100080000000000000000000000000000000000070
:100090000000000000000000000000000000000060
:1000A0000000000000000000000000000000000050
:1000B0000000000000000000000000000000000040
:1000C0000000000000000000000000000000000030
:1000D0000000000000000000000000000000000020
:1000E0000000000000000000000000000000000010
:1000F0000000000000000000000000000000000000
:1001000000000000000000000000000000000000EF
:1001100000000000000000000000000000000000DF
:1001200000000000000000000000000000000000CF
:1001300000000000000000000000000000000000BF
:1001400000000000000000000000000000000000AF
:10015000000000000000000000000000000000009F
:10016000000000000000000000000000000000008F
:10017000000000000000000000000000000000007F
:10018000000000000000000000000000000000006F
:10019000000000000000000000000000000000005F
:1001A000000000000000000000000000000000004F
:1001B000000000000000000000000000000000003F
:1001C000000000000000000000000000000000002F
:1001D000000000000000000000000000000000001F
:1001E000000000000000000000000000000000000F
:1001F00000000000000000000000000000000000FF
:1002000000000000000000000000000000000000EE
:1002100000000000000000000000000000000000DE
:1002200000000000000000000000000000000000CE
:1002300000000000000000000000000000000000BE
:1002400000000000000000000000000000000000AE
:10025000000000000000000000000000000000009E
:10026000000000000000000000000000000000008E
:10027000000000000000000000000000000000007E
:10028000000000000000000000000000000000006E
:10029000000000000000000000000000000000005E
:1002A000000000000000000000000000000000004E
:1002B000000000000000000000000000000000003E
:1002C000000000000000000000000000000000002E
:1002D000000000000000000000000000000000001E
:1002E000000000000000000000000000000000000E
:1002F00000000000000000000000000000000000FE
:1003000000000000000000000000000000000000ED
:1003100000000000000000000000000000000000DD
:1003200000000000000000000000000000000000CD
:1003300000000000000000000000000000000000BD
:1003400000000000000000000000000000000000AD
:10035000000000000000000000000000000000009D
:10036000000000000000000000000000000000008D
:10037000000000000000000000000000000000007D
:10038000000000000000000000000000000000006D
:10039000000000000000000000000000000000005D
:1003A000000000000000000000000000000000004D
:1003B000000000000000000000000000000000003D
:1003C000000000000000000000000000000000002D
:1003D000000000000000000000000000000000001D
:1003E000000000000000000000000000000000000D
:1003F00000000000000000000000000000000000FD
:00000001FF

1回PowerShellを実行して一部しかデータ取得できなかった場合は、即座にもう一回実行すると、20秒後には全データを取得できます。

とりあえず、これで、avrdudeコマンドで読みだしたのとほぼ同じ内容が取れていると思います。 本番とほぼ同じデータ形式になるので、このデータで解析用の実装を作成しておけば、ほとんど改修なしで流用できるはずです。


関連記事

AE測定、長期間ロギングシステム構築 (その2) アナログ部 検証編 

的が遠いシステムを構築する場合、つまり多数のコンポーネントを組み合わせてシステムを構築するSI(システムインテグレーション)では、一歩一歩検証しながら作業を進めていく必要があります。そして、その検証には測定器や計測器(OSC)が必要です。そしてその妥当性を確認できる手法です。

 今回は特に、中古部品も活用して構築するので、その部品自体の妥当性も検証が必要でした、というか早めにやっておいてよかった。9VのACアダプタですが、以前に購入して使っていた変圧とプラグ切り替え機能付きのACアダプタを使う予定でした。しかし、事前にテスターで確認すると9Vに設定しても9Vにならないことが判明し、ACアダプタを追加調達しました。 切り替えで12Vになりっぱなしだったり、一瞬17Vになったり、他の部品を壊してしまう原因になっていたかもしれません。

検証用の機器

既存のデジタルマルチテスター:
  電圧や導通試験に使用。このテスター自体も年代物で怪しいところがあったので事前に電池などで妥当性検証しておきました。前述の、ACアダプタの問題はこのテスターで検証して確認しました。

新規オシロスコープ:
[112972]DSO Shell 200kHz (DSO150) 完成品
https://akizukidenshi.com/catalog/g/g112972/


  オシロスコープとしては激安です。どこまで使えるか分かりませんが調達してみました。別案として激安の中古品(オークション)という線もありましたが、今回は納期と妥当性を勘案して、この製品を採用してみました。

検証

検証過程で、いろいろ動作しない事象にぶつかり、修正を繰り返して、最後は下の画像や動画のとおりの信号検出の確認ができました。

まず、最初は、それらしい信号がでてこず、何度か確認と修正をしています。参考のため注意しておくべきチェック観点を書いておきます。
・基板への部品取り付けは、後戻りが大変なので十分に注意してやりましょう。
・配線、入力と出力が同じミニジャック
・配線、R,L2回路ある
・ICの向き。 ICソケットの向きは注意して半田付けしたが、IC挿すときも注意
・電源の入力などなど
・その他の接触不良

つぎに、それらしい信号が見えるようになったところで、不安定な信号を見つけてそれの対策をしました。接触不良が主な原因でした、なかでもオシロ付属のプローブが不安定な信号の主要因でした。仕方がないので、自前のBNCケーブル(無線用のもの)で、オシロと、アンプの出力を直結して対処しました。

 最後は、オシロの使い方ですね。 このオシロは、 安価なつくりに必要最小限の実装を詰め込んでいるので、ぱっと見操作が良くわからない。さらに説明書が簡単すぎですね。 この機種は初めてオシロを使う人にはわかりにくいかもしれません。オシロスコープとはどういうものかをそれなりの機種で理解したうえで(購入する)使うのが良いかもしれません。

検証動画

センサーを叩くと、検出した信号波形をオシロで捉えることができました。波形は約5kHzで、この素子の共振周波数とほぼ一致していました。

関連記事

Win11リモートデスクトップ周りの設定対処していたらテキストコピーができなくなった→copy問題解決

Windows 11での「リモートデスクトップでモヤっと。」を直そうとしていろいろトライしていたら、テキストコピーができなくなった。 まずモヤっと問題と関連があるか調査が必要だが、作業効率を考えると、こっちを優先して対処する必要がある。以前の設定変更を確認して、 書き戻し作業で原因を特定して復旧しました。やはり、モヤっと問題の対処でいじくったとことが問題でした。

問題の原因

記事を確認すると、以下の設定を実施されています:

# グラフィック最適化の無効化  
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" /v fDisableClip /t REG_DWORD /d 1 /f

このfDisableClip = 1がクリップボードのリダイレクト(コピペ機能)を無効化しています!

解決方法

【最優先】クリップボードのリダイレクトを有効化

**接続先PC(サーバー側)**で以下を実行してください:

# レジストリで無効化されているクリップボード機能を有効化

# パターン1: ポリシーレジストリ
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" /v fDisableClip /t REG_DWORD /d 0 /f

# パターン2: RDP-Tcp設定(念のため確認・修正)
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v fDisableClip /t REG_DWORD /d 0 /f

# 再起動
Restart-Computer

これで復旧しました。

関連記事

AE測定、長期間ロギングシステム構築 (その1) アナログ部製作編 

AE測定、といっても….。狙いはAEつまり、100kHzレベルですが、なかなかそのレベルまでのものを用意するのは高額になってしまいます。そこで、できる限りそのレベルを目指した設計で、かつ低コストで実現できるシステムを目指すことにしました。
 高度なレベルと低コストをバランスを取って実現するために、「イベント信号波形の取得」ではなく「イベントが発生した時刻の取得」に特化することにしました。
もう一つ重要な要件として、「短時間で環境整備する必要がある」ということです。そこで、採用するシステム構成の方針を次のようにしました。

・比較的安価であること
・可能な限り、既製品を利用する
・短期間/短納期で入手できる部材を採用する
・検証しながら作業を進められるようにする
・既保有の部材や道具を利用する

採用部材(その1、アナログ部(プレアンプ))

直ぐに入手するために、国内に在庫があり即入手できるものから探しました。安価でも、中国からの船便となると2週間くらいかかってしまいます。そこで、30年ぶりくらいの部品通販での調達です。以前なら直接店舗で購入でしたが…。

■センサー

[104118]圧電スピーカー(圧電サウンダー)(13mm)PKM13EPYH4000-A0
https://akizukidenshi.com/catalog/g/g104118/
https://akizukidenshi.com/goodsaffix/murata-piezo-speaker-tape.pdf
10個買い

■アンプ

[112145]2回路入J-FET入力オペアンプ NJM072D  ←
https://akizukidenshi.com/catalog/g/g112145/

[112309]NJM4580DD使用ヘッドホンアンプキット
https://akizukidenshi.com/catalog/g/g112309/
このキットにNJM072D を置き換えて使う



[108853]3.5mmステレオミニプラグ⇔スクリュー端子台
https://akizukidenshi.com/catalog/g/g108853/

[108854]BNCオスコネクター⇔スクリュー端子
https://akizukidenshi.com/catalog/g/g108854/

■その他

電源は006Pの9電池でも動作するが、長期間ロギングの必要があるため、9VのACアダプターを使用します。
センサー、アンプ間の配線は既保有のアンテナ線などの廃材を使用します。半田やはんだごてなどは既保有なので省きます。

製作

とりあえず、アンプを製作、40年ぶりくらいのキットでの電子工作です。30分ほどの半田付け作業で完成しました。

左がセンサー代わりのピエゾです、とりあえず直結してます。アンプ基盤の右側が出力です。


以上で、アナログ部の製作は完了です。
そのあとは、アナログ部の動作検証を行う予定です。→(アナログ部検証編はこちら)そしてその後のAD変換でのデータロギングのプログミングを含む作業が待っています。多分これが一番大変でしょう。

関連記事

MCR-4TCでの温度測定について(Tタイプ熱電対使用、4か所測定)

前の記事に続いて、 MCR-4TCを使っての温度測定の話です。
2025/12/21から屋外測定を開始しました。(つぎも参考
4点の測定結果はつぎのとおり

T4
T4

昼間の時間帯の測定値ばらつきが大きく見えている部分がありますが、そのばらつきが大きいところは直射日光や風が当たっていた時間帯と思われます。全体的に想定通りの温度変化をしています。良い感じです。

次は、サンプルの測定結果です。1分に1回測定しています。以下はサンプルのCSVファイルです。 MCR-4TCで取得したファイルは、USB経由でWindows PCに回収できます。独自のバイナリ形式での保存ですが、Graphソフトで CSV形式での出力も可能です。次がそのファイルのサンプルです。


今後の測定分については、上記のようにzip化したCSVファイルをこの下に追加していく予定です。

関連記事