⚡雷サージカウンタの自作 (ESP32)
概要
設置したSPDの効果を可視化するために雷サージカウンタを自作します。市販品はどえらい価格しますし、中古はヤフオクに出ません。
方針
市販品
市販品を見てみると、どれもコアで挟むタイプの電流センサーのようなものを使ってますね。これを参考にします。

検出電流閾値を10 A/50 A/100 Aから選べる製品があったので、とりあえずこれぐらいの電流を検出できるようにすればよさそう。
クランプ式電流センサー(CT)
SCT-013 シリーズがポピュラー。巻線比は1:2000らしい。
コアで挟むと出力は電流源的な働きをするので、電圧として出力するには負荷抵抗を電流センサーの両端子間に挟みます。
内蔵されてる負荷抵抗の値によってモデルのナンバーが違うみたいですね。
多分、例えば SCT-013-010 なら 一次側10 A時/二次側1 V出力 なので、負荷抵抗は200 Ω内蔵されてるはず。
(二次側電流は巻線比より 10 A/2000 = 5 mA)

SCT-013-000は内蔵されてる負荷抵抗なし、つまり電流出力タイプで都合が良いのでこれにします。
(自分で負荷抵抗を決めて、好きな電圧範囲に設定できる)
電流出力タイプはクランプした状態で二次側を開放すると高電圧が出て危険らしいですが、これはTVSダイオード(ツェナーダイオードみたいなやつ)が全モデルに内蔵されてるので間違って開放しても大丈夫なよう

SCT-013-000を買ったら抵抗内蔵タイプのSCT-013-000Vが届きました…
紛争を開始するのは面倒なので分解してみます。
クランプを開けてコードを押し込むと簡単に基板にアクセスできました。

ここのチップ抵抗を吹き飛ばせばOK。黒いのはTVSダイオードかな?
全波整流
一回の雷サージで、サージはどちらか一方向に流れるはず。(電源ラインからアース or アースから逆流)
どちらの方向に流れても検出できるようにするには、2台買って逆向きに設置するか、1台買って回路で全波整流する必要がありそう。
そもそもこのセンサーは交流電流測定用で、サージみたいな片方向の場合にどっちの向きで設置したらどっちに正の電圧が出るかも不明なので、全波整流します。
どうやる?
こんな感じで、普通にダイオードを用いて全波整流をすると、2*Vf 分の電圧が失われます。

Vfが低いショットキーバリアダイオードを使っても、0.6 Vぐらいは持っていかれるので微妙。
色々調べてみると、オペアンプを使った理想ダイオード(Vfがゼロ!)を応用した全波整流回路がありました。

しかもゲインを付けることができます。R5/R3 = 2 のとき全波整流(1倍)が出力されます。20にすれば10倍。
ただしオペアンプの電源に負電源が必要なので、チャージポンプICを使いました。反転した電圧を出力してくれる便利なICです。
ピーク保持回路
最後にピーク保持回路。ここがサージ検出回路全体の出力になります。
信号のピークをコンデンサに貯め、放電抵抗でゆっくり電荷を開放します。
ここでもダイオードのVf分が失われますが、前段のゲイン付き全波整流回路で増幅済みなのでOK
Vf下げるためにダイオードをショットキーにすると、漏れ電流が大きくコンデンサに貯めた電荷が逃げ出すので普通にシリコンにしました。

コンデンサ0.1 uFと抵抗1 MΩで時定数は100 msです。これならESP32のADC入力でも十分検出できる時間。
最初放電抵抗を付けずに回路を組んでみましたが、(コンデンサ、ダイオード、ADC入力の漏れ電流で勝手に電荷がいい感じに抜けると予想)、DC的に浮いてしまうので、常時変な電圧が出力されます。
プルダウン抵抗的な役割としても放電抵抗は置いたほうが良さそう。
検出して記録
ESP32のADC入力を使って検出します。
ESP-IDFでDMAというものを使うとかなり速く(数百kHz?)サンプリングできるらしいですが、むずいのでArduinoライブラリ経由の読み取りでやります。
それでも10 kHz(0.1 ms間隔で読み取り)ぐらいまでなら大丈夫らしい。なによりピーク保持回路があるからそれでも余裕で検出できると思います。
シミュレーション
雷サージの再現
SPDの評価には、雷サージを模擬した8/20 usの電流インパルスがよく用いられるらしい。
https://www.sankosha.co.jp/basic-lightning-protection/lightning-surge
ChatGPTによると…

それっぽいですね。

回路図

CT入力~オペアンプ全波整流まで
- 負荷抵抗 Rb = 100 Ω
- 過電圧防止のため、TVSダイオード6.8 Vを挟む
- 電流制限のための抵抗10 Ωを挟んだのち、1N4148でGNDとクランプ(オペアンプ入力を0±Vfに抑える)
オペアンプ全波整流
- 出力レールtoレールのオペアンプ TLC2272
- ゲインは10倍
ピーク保持回路
- C=0.1 uF, R=1 MΩ 時定数100 ms
結果
8/20 us 電流波形 ピーク10 A

8/20 us 電流波形 ピーク50 A

8/20 us 電流波形 ピーク100 A
ここら辺から出力電圧は頭打ちになりますね

8/20 us 電流波形 ピーク50 kA
最悪の直撃雷が来たと想定して、50 kA。
実際には一次側にこんな大電流が来たとしても、コアが飽和して二次側には大した電流は来ないと思いますが一応。

各素子は大丈夫なんでしょうか?

TVSダイオードが吸収してくれてる。
作る
回路図
ChatGPTに言われた通りに、各ICの電源入力の直前にデカップリングとして1 uFと0.1 uFの積層セラコンを入れました。

チャージポンプIC ICL7660

-3.3 Vをここで作ります。マニュアルによると2個の10 uFのコンデンサが必要。
しかしマニュアル通りにコンデンサを配置してもなぜか動作せず…しかもICが爆熱に。
色々試すと、ICへの+3.3 Vの入力をESP32からの3.3 Vではなく、別で用意した3.3 Vを使うと正常に動作しました。
多分接続時に3.3 Vラインが揺れて誤動作している?
マニュアルにはこんな記載がありました。
“If the voltage supply driving the TJ7660 has a large source impedance (25Ω – 30Ω), then a 2.2µF capacitor from pin 8 to ground may be required to limit rate of rise of input voltage to less than 2V/µs.”
電源がゴミなとき、ICの電源入力とGNDの間に2.2 uFぐらいのコンデンサを入れろと書いてますね。
うーん、でも一応1 uFと0.1 uFが既に入ってるけど。
3.3 Vラインの大元に47 uFのコンデンサを入れてみたら解消しました。
お買い物リスト
- 汎用小信号高速スイッチング・ダイオード 1N4148 100V200mA
- TVSダイオード P4KE6.8CA
- 積層セラミックコンデンサー 0.1μF100V X7R 5mmピッチ (C0G高いから100 V品で代用)
- 積層セラミックコンデンサー 1μF50V X7R 2.54mmピッチ
- 積層セラミックコンデンサー 10μF50V X7S 5mm
- 導電性高分子アルミ固体電解コンデンサー ハイブリッド 47μF63V125℃ PZJ (普通の電解コンでもいいと思う)
- 2回路入CMOS低雑音オペアンプ TLC2272CP
- スイッチトキャパシター電圧コンバーター(チャージポンプ) TJ7660(HTC製)
- 3.5mmステレオミニジャックDIP化キット (あると便利)
(自分は前述したチャージポンプICの誤動作の原因を切り分けるために追加で TC1044SCPAを買ったので結局こっちを使っていますが、安いTJ7660でいいと思う)
はんだ付け

プログラム
ESP32側で検出するプログラムをArduinoで書いていきます。
- 0.2 ms間隔(5 kHz)で割り込みしてADC読み取り
- ADC値が500を超えたらON判定
- ON判定後、30回連続(6 ms間)ADC値が500~100の間の値を記録したら”サージ”としてフラグ立てる(要はノイズ対策)
#include <driver/adc.h>
#include <driver/timer.h>
// ADC雷サージ検出
const uint32_t SAMPLE_US = 200; // 0.2ms間隔(5kHz)
// 閾値とヒステリシス
const int UPPER_THRESHOLD = 500; // ON判定用(高め)
const int LOWER_THRESHOLD = 100; // OFF判定用(低め)
// 連続判定用
const int N_CONSECUTIVE = 30; // 連続判定回数
volatile int consecutiveCount = 0; // ISRで管理
volatile bool thresholdPending = false; // ISR -> loop に通知するフラグ
volatile int lastAdcValueISR = 0; // ISRで保存する最新ADC値
// イベント確定時にコピーする変数
int lastAdcValueEvent = 0;
// ブロック時間(ms)
const unsigned long BLOCK_TIME_MS = 10000;
unsigned long lastTriggerTime = 0;
hw_timer_t *timer = NULL;
void IRAM_ATTR onTimer() {
// 軽量に保つ:ADC読み取り(軽い)、ウィンドウ更新、フラグ立て
int val = adc1_get_raw(ADC1_CHANNEL_0);
lastAdcValueISR = val; // 最新値を保存
// 閾値判定
if (val >= UPPER_THRESHOLD) {
consecutiveCount++;
if (consecutiveCount >= N_CONSECUTIVE) {
thresholdPending = true;
consecutiveCount = 0; // フラグ立てたらリセットして再検出まで待つ
}
} else {
consecutiveCount = 0; // 閾値未満ならカウンタリセット
}
}
void setup() {
// ADC config
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11); // ADC1_CH0(VPピン)の減衰を11dBに設定
adc1_config_width(ADC_WIDTH_BIT_12); // ADC1の解像度を12bit(0~4095)に設定
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, SAMPLE_US, true);
timerAlarmEnable(timer);
}
void loop() {
// ISR がフラグを立てたらここで処理する(ブロック時間チェック)
if (thresholdPending) {
// atomically copy and clear flag (avoid race)
noInterrupts();
bool pending = thresholdPending;
thresholdPending = false;
int capturedVal = lastAdcValueISR; // ADC値をコピー
interrupts();
if (pending) {
unsigned long now = millis();
if (now - lastTriggerTime >= BLOCK_TIME_MS) {
// ここで重い処理して OK
lastTriggerTime = now;
lastAdcValueEvent = capturedVal; // イベント用変数に確定値を保存
Serial.print("閾値イベント発生! ADC値 = ");
Serial.println(lastAdcValueEvent);
}
}
}
}テスト
適当なパルスをCT入力に印加して検出できるかテストします。
Raspberry Pi pico(RP2040)で、10 us間GPIOをHIGHするプログラムを書いて試してみました
#include <Arduino.h>
const int pulsePin = 2; // 出力するGPIOピン番号
int pulse_count = 0;
void setup() {
Serial.begin(115200);
pinMode(pulsePin, OUTPUT);
digitalWrite(pulsePin, LOW);
delay(1000);
}
void loop() {
pulse_count++;
if(pulse_count==20){
// 10μs幅のパルスを出力(10秒毎)
digitalWrite(pulsePin, HIGH);
delayMicroseconds(10); // 10μs
digitalWrite(pulsePin, LOW);
pulse_count=0;
}
delay(500);
}ちゃんと検出できてますね。
閾値イベント発生! ADC値 = 781
閾値イベント発生! ADC値 = 446
閾値イベント発生! ADC値 = 453早く雷来ないかな
LTSpice, KiCadデータ
https://tatuiyo.xyz/wp-content/uploads/2025/08/surge_counter_LTSpice.zip
https://tatuiyo.xyz/wp-content/uploads/2025/08/surge_counter_KiCad.zip







最近のコメント