大阪教育大学の光永さんの
Arduino でオシロスコープ
を基に128x64の1.3インチOLEDで使えるように変更して
ArduinoとOLEDで2CHオシロスコープ・Pulse generator・Function Generator・周波数カウンタ
を作ったので、それを基にESP32に移植したものが
ESP32とOLEDで2CHオシロスコープ・Pulse generator・Function Generator
です。
しかしながら、ADCのリニアリティーは悪いし、高速サンプリングは不安定、PWMはジッターが不安、Cosine Wave Generatorは周波数の高い方でギザギザが乗ると良いところ無しでした。
そこで、ESP32の特徴であるWiFi機能を用いてWEBオシロを試してみました。まだ不完全ですがPCやスマートフォンの大きな画面で波形を確認できます。
まだ基本的に波形が表示できるようにしただけなので、ユーザーインターフェースのためにOLED表示とタクトスイッチは残してあります。出来ればWEB画面に操作コントロールを追加してOLEDとタクトスイッチ無しで設定変更できるようにしたいと思っています。
波形データの送信にはWebSocketを使いました。WebSocketの通信は手抜きでEndian処理をしていません。そのためBig EndianのCPUでは正しく波形が表示できません。いまどきそんなCPUは少ないと思いますしPentiumやARM系のAndroidであれば使えますので大丈夫でしょう。波形の描画にはHTML5のCanvasを使っています。オシロスコープ機能はマルチコアのCORE1で処理し、WiFi機能はCORE0で処理するようにしています。
画面が大きくなって振幅方向の分解能が増えたためにADCの非線形性が目立つようになったので、簡単に2区間の直線近似で補正を加えました。ESP32の個体毎に微調整が必要かもしれません。サンプル数は取り敢えずOLED表示と同じ128ポイントです。もっと増やしたかったのですが、500usec/divレンジで256ポイントを取り込もうとしたらウォッチドッグタイマーでリブートしてしまい、解決方法が見つからないので諦めました。(追記 2022.08.09)見直してみたら、ループ変数をbyteにしていたのが問題だったようです。
開発環境は
Arduino IDE 2.3.10 + Arduino core for the ESP32 2.0.3 ESP32 3.3.12です。
ESP32 2.0.3 から 3.0.0になってからtimerAlarmとledcの関数呼び出しが変更になってコンパイルできなくなって放置していました。こういう変更がよくあるのでESP32からはしばらく遠ざかっていましたが、思い立ってVersion1.33からESP32 3.3.5に対応しました。
Version1.32以前のソースをコンパイルする場合にはESP32 2.0.17まで戻す必要があります。 (2026.01.12 update)
この環境ではAnalogRead()が使えるのですがオーバーヘッドが多過ぎるようで遅いので、adc1_get_raw()とadc2_get_raw()を使うようにしました。それでもまだ遅いです。
I2Sを使って1Mspsまで出来るようにしてみましたが不安定です。500kspsまで出来るようにしてみました。(訂正 2022.12.25)
書き忘れましたが、その後ADCの実力に合わせて250kspsまでに変更しました。(追記 2026.08.07)
WEBオシロにした所、OLED版よりもさらに不安定になってしまいました。
時間軸拡大機能を追加しました。4us/divは250kspsの10倍拡大、8us/divは5倍拡大、20us/divは2倍拡大になります。拡大にはsin(x)/xによる補間をしています。(2024.03.13 update)
v1.37からESP32-C3にも対応してADCのContinuous Modeを使うようにしているので、C3ではない無印ESP32でもI2Sを使うのはやめてContinuous Modeを使うようにしてI2S使用時よりも安定しました。 ESP32-C3版に限定した説明は別に ESP32-C3 Super Miniで2CH WEBオシロスコープ・Pulse generator・Function Generator に用意してあります。(2026.08.07 update)
v1.39から周波数カウンターを組み込みました。ESP32-C3では使えません。(2026.08.07 追記)
最新版はGitHubに置いてあります
https://github.com/siliconvalley4066/ESP32OLEDOscilloscope
ESP32WebOscillo143.zip 2026.10.20 update for ESP32 3.3.12
ESP32WebOscillo132.zip 2024.03.13 update for ESP32 2.0.3
ESP32WebOscillo125.zip Old version
ボード設定は"DOIT ESP32 DEVKIT V1"と"NodeMCU-32S"が確認済みです。
ESP32-C3 Super Miniの場合ボードは"Nologo ESP32C3 Super Mini"を選択します。
OLEDは小さくても良いので0.96インチにして、ドライバはSSD1306用です。Adafruit_SSD1306.hがインストールされていない場合はライブラリマネージャーから検索してインストールしてください。SH1106用にするには#define DISPLAY_IS_SSD1306の行をコメントにしてAdafruit_SH110Xをライブラリマネージャーから検索してインストールすればOKです。
WebTask.inoの (削除 2026/10/20)
const char* ssid = "XXXX";
const char* pass = "YYYY";
に使用するWiFiアクセスポイントのssidとpasswordをXXXXとYYYYに記入する必要があります。ソースコードを少し変更すればAPモードにすることも可能です。
v1.43からWiFiマネージャーを採用しました。 初期設定を行う際は、「ESP32_XXXXXXXX」(「XXXXXXXX」は固有のチップID)という名称のアクセスポイントに接続し、ブラウザで「192.168.4.1」にアクセスしてください。その後、接続先となるWiFiネットワークのSSIDとパスワードを入力します。一度設定が完了すれば、それ以降の設定は不要です。設定を変更したい場合は、デバイスの起動時に「DOWN」ボタンを長押ししてください。 (追記 2026/10/20)
WebSocketsのライブラリはライブラリマネージャーから検索できるものではなく、以下を使いました。
Links2004/arduinoWebSockets
WebSocketsのライブラリはライブラリマネージャーから検索できるものを使いました。 (2026.01.12 update)
WebSockets by Markus Sattler version 2.7.2
使用ライブラリ: (追記 2026/10/20)
Version 1.26からはTFT液晶表示版と配線の共通化をするために、CH1のDC/AC切り替えスイッチとPWM outのピン割り当てを変更したので、version 1.25以前のソフトを使う場合は旧回路図を参照してください。(追記 2023/08/08)
10:1プローブを使うために入力インピーダンスを1Mohmに近付けたいので次のような入力回路にしました。
簡単にするならADC入力のIO34とIO35に直列に10kohm程度の抵抗を付けるだけでも実験できます。ただしDCモードだけになります。
ESP32-C3 Super Miniでの回路図 (追記 2026.08.07)
DCモードだけのESP32-C3 Super Miniでの回路図 (追記 2026.08.07)
version 1.25以前のソフトを使う場合の旧回路図
簡単にするならADC入力のIO34とIO35に直列に10kohm程度の抵抗を付けるだけでも実験できます。ただしDCモードだけになります。
先ず起動する前にArduino IDEのシリアルモニターを開いておきます。プログラムが開始してWiFiアクセスポイントに接続成功したらIPアドレスを取得してシリアルモニタに表示します。そのIPアドレスをWebブラウザのアドレスバーに入力すればWEBオシロに接続できるはずです。
例えばシリアルモニタに、
WiFi Connected. IP = 192.168.1.128
と表示されたら、Webブラウザのアドレスバーに192.168.1.128と入力します。
ATMEGA版ではタクトスイッチ4個で操作するようにしましたが、スイッチの場所を取るので5方向スイッチが使えるようにしました。5方向スイッチでは上下同時押しや左右同時押しが出来ないので、右上同時押しと左下同時押しに変更しました。#define BUTTON5DIR を有効にすれば5方向スイッチにすることができます。
OLED画面右側のメニューは4ページ構成になっています。UpボタンとDownボタンでメニュー内の項目を移動します。一番上の項目でUpボタンを押すと前のページに切り替わります。一番下の項目でDownボタンを押すと次のページに切り替わります。それぞれの項目の位置でRightボタンかLeftボタンを押すとその項目の値を変更したりOn/Offの切り替えをします。
ページに関係なくUpボタンとDownボタンを同時押しすると文字表示を消して全画面波形表示になります(5方向スイッチでは右上同時押しになります)。再度同時押しすれば元に戻ります。
LEDCのPWMで発生できるパルスをほぼ全て出せるようにしました。80MHzを1/2から1/65536まで設定できます。分周比は1/1から1/256まで全て使います。発生できる周波数は4.768Hzから40MHzまでになります。周波数自体を設定するのではなく、発生できる上または下の周波数へ設定を変更して、その設定で発生される周波数を計算して表示します。Duty cycleも実現可能な値が限られている。周波数の分解能は1/256になり、ATMEGA版より荒くなります。なお、分周器がRate Multiplierなのでジッターが発生する可能性があります。
v1.41まではLEDCに関しての認識が間違っていたのが分かってv1.42で設定方法を修正しました。
80MHzを1/1から1/1023まで分周することと、Resolutionを1bitから17bitまで使うことで1Hzから40MHzまでを発生出来ます。
高い方の周波数はResolution=1bitで1/1, 1/3, 1/4 ... 1/1023と変化させて小数点以下を使わずになるべく細かく周波数を設定できるようにしましたが、dutyは50%固定になります。
低い方の周波数はResolution=17bitで1/512から1/611まで変化させて最低0.9989Hzになります。
もっと低い周波数にも出来ますが、そもそもAruduino用のライブラリは整数でしか周波数を設定できないので無理して想定外の設定をして余計なトラブルに遭遇するのは避けたいと思います。ESP32-C3での最低周波数はResolution=14bitで1/1023になって4.773Hzになります。
このような設定を実現するためにより低レベルのledc_ll_set_duty_resolution()やledc_hal_set_clock_divider()というライブラリを使っています。周波数カウンタでもLEDCを使っていて時々相互干渉してリブートすることがあります。なるべく干渉が無いように対策をしていますが完全には解消できていません。 (2026.10.02 update)
Lab3 - Laboratory for Experimental Computer Science を流用させていただきました。波形データはその他色々な所から集めました。周波数の設定と波形の選択だけがオリジナルです。周波数の分解能は取り敢えず0.01Hzにしました。インタラプト処理が入るので速いサンプリング時にオシロスコープの波形が間延びするようなら明示的にOFFにした方が良い。インタラプトのオーバーヘッドが大きいようで、サンプリング周波数が5kHzまでしか上げられませんでした。ATMEGA版の31kHzより遥かに低い周波数です。せっかく8ビットDACがあるのに残念ですが、とりあえずローパスフィルタは無くても使えます。その代わりメモリに余裕があるので用意した23波形すべてを使えます。
ESP32にはCosine Wave Generatorがあって、CPUの負荷なくsine波形なら50Hzから200kHzまで出せる。
参考: GitHub - krzychb/dac-cosine: API to operate cosine waveform generator inside ESP32
ただし、約130Hzより低い周波数はRTC 8 MHz Clockの分周比を設定する必要があり、他に何か影響があると怖いので分周比の設定はやめた。
dac_cw_generator_config()で130Hzから55000Hzまでuint32_tで出来るのだが、試してみると65537Hzまで行けた。65535の二つ上までってどういうことなのか分からないが、内部の計算でオーバーフローとか起こっているのだろうか?周波数は何でもできるわけではなく130Hzステップでしか変わらないようだ。また、オフセットの設定にバグがあって、int8_tで-128から+127まで設定できるはずなのが、0を設定しても-128になってしまう。オフセットと周波数の設定はこのAPIを使うのはやめて、上記参考記事の方法を使わせてもらった。
結局129.70Hzから129.70Hzステップで約200kHzまで設定できるようになりました。上限はもっと行けて600kHzでも何かしら波形が出てきますが波形歪みがひどくなります。100kHzでも結構ギザギザが乗っています。
DDS Function GeneratorとCosine Wave Generatorとの切り替えは周波数によって切り替えます。
0.01Hzから518.79HzまではDDS Function Generatorで、それ以上は波形の設定にかかわらずCosine Wave Generatorに切り替わり、518.80Hzから129.70HzステップでCosine Wave Generatorになります。周波数が下がる方向には518.80Hzから389.10Hz, 259.40Hz, 129.70まではCosine Wave Generatorでそれ以下はDDS Function Generatorになります。この時の389.10Hz, 259.40Hz, 129.70はDDS Function Generatorよりきれいな波形が出せます。
I2Sを使えば150kspsができるとか、1Mspsのオシロを作ったという記事
esp32TTGO_i2s_scope (tested on core 1.0.5, not working with esp core 2.0.3)
があったので実装してみました。時間軸の10us, 20us, 50us, 67us, 100us, 200us/div20us, 40us, 100us, 133us, 200us, 400us/divがI2Sで動作します。(訂正 2022.12.25)
ESP-IDFのドキュメント ESP-IDF Programming Guide には150kspsが上限と書いてあるのですが、それ以上でも設定できたので1Mspsまで設定できるようにしました。総じてI2Sでのサンプリングは時間軸がふらついて不安定です。1Mspsでデータは取れますが、同じ値が3回から4回程続くのでADCの実力は200kspsから300kspsの間位と思われます。仕様で150kspsまでとなっているのは妥当な所でしょう。150kspsになる時間軸は67us/divです。波形観測したところでは50kHzを超えた信号は訳の分からない波形になりました。測定対象に注意が必要です。
(追記 2022.12.25)データをよく見ると、サンプリング周波数が低い時でも2回ずつ同じ値が続いています。どうやらI2S_CHANNEL_FMT_ALL_LEFTにしてもデータの並びはLRLRでRにはLの値が入っているようです。そこでLの値だけ取り出すように変更したら、階段状態が無くなり綺麗なデータになりました。したがって、1Mspsだと思っていた設定では500kspsになります。
(追記 2026.08.07)その後I2S_CHANNEL_FMT_ONLY_LEFTに変更してADCの実力に合わせて最高250kspsにしました。
(追記 2026.08.07)しかしながら、I2Sではデータ抜けが頻発して不安定なのでContinuous Modeを使うように変更しました。
(追記 2026.08.07)ESP32-C3でContinuous Modeを使うように出来たので、無印ESP32でも使うようにしました。以前は設定したサンプリング周波数と12%程ずれていたので採用を控えていたのですが、周波数が合うようになったのでこちらに切り替えました。I2Sに比べてサンプル抜けが無く安定している上に、2チャンネルも可能になります。これによって以前は2チャンネルがソフトループ方式の1ms/divまでだったのが、Continuous Modeで100us/divまで出来るようになりました。ただ、無印ESP32の場合はシングルチャンネルの場合にI2Sの時と同様に奇数番目と偶数番目のサンプルが入れ替わっています。ESP32-C3ではそんなことは無いのですが、順番を直すのに余計な手間がかかってしまいます。
サンプリング周波数は2MHzまで設定できますが同じ値が続くだけなので250kHzを上限にしました。
(追記 2026.08.19)。ESP32で周波数カウンター
で作ったものを組み込みました。PCNTをLEDCのパルスでゲートして正確に計測できるものです。ESP32-C3では使えません。ゲート時間は約1秒です。
OLEDメニューのFCNTでRightボタンを押すと開始します。Leftボタンで終了します。
計測中にさらにRightボタンを押すと較正機能が働きます。
基準周波数の30MHz, 25MHz, 24MHz, 20MHz, 16MHz, 12MHz, 10MHz, 8MHz, 6MHz, 5MHz, 4MHz, 3MHz, 2MHz, 1MHz, 100kHz, 32768Hzと50ppm以内の誤差ならば自動的に補正して補正値を保存するようにしています。
WEB用のGUIは作っていません。
周波数カウンターは時々起動に失敗して "Guru meditation Error" になってリブートすることが有ります。原因は分かりません。パルスジェネレーターの起動でも不安が有ります。ネット上ではESP32ボード3.3.2になってからLEDC関係で起きやすくなったと言っている人がいます。
| 機能 | 内容 |
|---|---|
| 入力チャンネル数 | 2 |
| 入力結合 | DC and AC |
| 入力電圧範囲 | 0 to 3.3V |
| 入力インピーダンス | 1Mohm |
| 電圧レンジ(volts/div) | 1V, 0.5V, 0.2V, 0.1V, 50mV |
| 時間レンジ(time/div) | 10s, 5s, 2s, 1s, 0.5s, 0.2s, 0.1s, 50ms, 20ms, 10ms, 5ms, 2ms,
(DMA) 1ms, 500us, 200us, 100us, (samples only 1 channel) 40us, 20us, 8us, 4us (250ksps) |
| トリガモード | Auto, Normal, Scan |
| トリガ極性 | rise/fall edge |
| トリガソース | CH1 or CH2 |
| トリガレベル調整 | 画面内位置を上下に調整 |
| 外部トリガ | No |
| 波形位置調整 | 上下に調整可能 |
| 波形反転表示 | Yes |
| 電圧測定表示 | Max, Average, Min (AC入力でもDCレベルを表示) |
| 周波数&Duty測定 | Yes |
| 電圧・周波数・Duty表示 | 表示・非表示切り替え |
| 文字表示 | 文字サイズ固定 |
| FFT | Yes |
| A/D変換分解能 | 12bits |
| サンプル数 | 128samples/channel 12bit/sampleを電圧レンジに応じて0から400にスケーリングして表示 |
| 表示解像度 | 128x400 dots, 水平10dots/div 垂直50dots/div |
| 等価時間サンプリング | No |
| 設定状態EEPROM保存 | Yes |
| 操作方法 | タクトスイッチ4個 (Left, Right, Down, Up) または 5方向スイッチ (Left, Right, Down, Up) |
| プリトリガー | No |
| ホールドオフ機能 | No |
| 時間軸拡大表示 | x2 (20us/div), x5 (8us/div), x10 (4us/div) |
| X-Y表示 | No |
| Pulse Generator | 1Hz to 40MHz 約3.3Vpp方形波 Duty ratio可変 ESP32-C3は4.773Hz to 40MHz |
| Function Generator | 8bit DAC DDS 0.01Hz to 518.79Hz(実用は200Hzまで) 約3.3Vpp
23波形 sine, saw, revsaw, triangle, rectangle, 20% pulse, 10% pulse, 5% pulse, 1dot pulse, noise, gaussian noise, ECG, sinc5, sinc10, sinc20, sine 2 harmonic, sine 3 harmonic, chopped sine, sine abs, trapezoid, step2, step4, chainsaw 8bit DAC Cosine Wave Generator 129.70Hzから129.70Hzステップで200kHz程度まで。 |
| 周波数カウンタ | ゲート方式 1Hz - 32MHz (Max 40MHz) |
| PCへのデータ転送 | No |
| SDカードへの波形保存 | No |
イタリアのWEBショップ Tiger2000 Electronics SHOP ON LINE でユニバーサル基板に組んだものが販売されていました。49ユーロだから8000円くらい。 ソフトはオープンソースだからハードの値段なのでしょうか。(追記 2025.05.25)
Arduinoで電子工作に戻る
Siliconvalley4066番地に戻る