![]() |
| 本格稼働前に防水ボックス作らないと…… |
今日は簡単な電子工作を一つ。
![]() |
| 本格稼働前に防水ボックス作らないと…… |
今日は簡単な電子工作を一つ。
デュアル入力昇降圧DCDCのLTC3118にはVccという3.8Vの出力端子があり内部用に定電圧化されています。引っ張れる電流は、Vin>6V で60mAとのこと。
よく使うチップ抵抗、MLCCはこんな感じで100均一の「旅行用クリーム小分け容器」に入れてます。最初左のでかいのに入れてたんですが引き出しがいっぱいになったので右の小さいのに順次更新中です。
便利なんですが……100均ってモデルチャンジが激しいというか売り切れたらそれまでの商品が多いので、なかなか同じものを買い足すことができません。
「そういえば皮膚科でもらう軟膏の入れ物、あれならモデルチェンジしないかも」
と思って探したら、出てきました。
あれ、軟膏壺っていうんですね。しぶいわぁ。
| カットしてレジスト塗ってUEW線ジャンパー飛ばした図 |
毎回、複数枚作業してて終わり頃になるとコツが掴めてくるので忘備録。
二度のPCB A不良(BGA短絡)と納期遅延(基板実装5-6日の予定だったのものが基板完了後10日間放置状態)が続いたことについてクレームを入れた後、新しい注文を入れることができなくなりました。クレームとの関連等についての問い合わせにも回答が得られていません。残念です。
JLCPCB様、今までありがとうございました。加筆:2026年7月27日
※この原稿はJLCPCB社からの依頼により執筆しています。依頼だからといって手心は加えていないつもりですが、その旨、ご承知おきください※
先日私引っ越しをしたのですが、40年前、社会人として仕事する前のバイト時代のお仕事メモが出てきてしまいました。知人の会社でZ80 CPUボードを作るお手伝いをした記録なのですが……A4サイズの両面基板で1枚28万円と書いてあります。当時は基本料金の他にスルーホール1個いくら、ドリル穴1個あたりいくら、という課金方式だったんですよね。回路図を渡してから2ヶ月ぐらいかかったような記憶があります。
それからさらに10年ほど前、自宅で塩化第二鉄で生基板をエッチングしてプリント基板を作っていました。廃液処理に悩んだもんです。
そして今。インターネット経由で基板を注文して、数千円で10日ほどで基板が届いてしまうのだから、びっくりです。21世紀ですねー(年寄りの感想)。
というわけで、2回にわたって、基板設計〜JLCPCBさんに基板発注〜届いた基板を評価〜実装……というあたりをお伝えしようと思います。
CADはEagleを使います。私が使い始めたころは結構高かったんですが……いまはFusion 360のおまけみたいになっちゃいましたね。時代は変わる。
今回作る基板は技術的にはそう難しくないけれど、同じものが繰り返しなのでできれば手で配線したくない、というタイプです。
24Vの信号入力が32本あって、それをフォトカプラで受けてラズパイに読み込むのですが、24Vの信号がプラスかマイナスかわからない(!)ので双方向性のフォトカプラを使い、ラズパイのI/Oも足りないのでMCP23017を2個使います。
作ることが決まったのが月曜日で、2週間後には現場に設置しなければいけない、という割と忙しいスケジュール。
現場で仕様を確認したのが10/4日、設計が終わったのは10/5日でした。
うちで基板を作る場合、だいたいこんな流れです。
プリント基板はガーバーファイルで発注します。ガーバーファイルはCAM Processorで作ります。PCB屋さん各社、自社用のCAMを用意しているところが多いです。JLCPCBさんのEagle gerberはこちらから。お使いのEagleのバージョンに合ったCAMファイルをダウンロードして、所定のディレクトリに置きます。ファイルはレイヤー数ごとに用意されています。普通の両面基板なら2_layersです。
WindowsとLinuxについては記述がありますが、Macについては書いてないですね……Macの場合は、~/書類/eagle/cam/の下に入れておくとよいかと思います。あと、私のところではダウンロードすると.cam.txtという拡張子になってしまったので.camに修正します。
How to Generate Gerber and Drill Files in Autodesk Eagle
Eagleをbrdウィンドウに切り換えます。
ツールバーのCAM processorアイコン、あるいはFileメニューから「CAM Processor...」を選びます。
次に、Load Job Fileアイコン(フロッピーディスクアイコンの左)をクリック、Local CAM jobs >can > jlcpcb_2_layer_v9.camを選びます。
右のOutput Files一覧のTop Copper, Bottom CopperなどをクリックするとGerberとして出力されるファイルの中身を見ることができます。長方形以外の基板だとときどき外形が間違って認識されていたりするので、Board Outlineで確認します。あと、標準状態だとシルクにはNameしか出力されませんが、tValueも出力したい場合にはSlikscreeen Topを選んでLayers一覧の左下アイコンをクリックしてtValueを追加します。
確認して間違いがなければ、右下のProcess Jobsボタンをクリックします。数秒でファイル保存ダイアログが開きます。任意の場所・ファイル名を指定してSaveをクリックします。
Gerberファイルは通常はzipで圧縮されています。
以上で発注に必要なGerberは用意できました!
ブラウザでjlcpcb.comをアクセスします。Sign Inからログインしますが、初めてのアクセスの場合には、Sign In → New Customer Start Hereからアカウントを登録してください。
私はGoogleからサインアップしています。
するとトップページの中断左側に「Add gerber file」のリンクがあるので、ここからさっき作った.zip形式のgerberファイルをアップロードします。数秒〜数十秒後、画面が切り替わり、さっき作った brd が表示されるはずです。
以前は寸法など手入力しなければいけなかったんですが、今はガーバーアップロードすると自動的に読み込んでくれるので楽になりました(しみじみ)。
さて、あとはPCB製造に必要な条件を入力していきます。
今までM5Stackの簡易版で何とかやってきたんですが、もっと高性能高解像度のサーモグラフィーがあればリワークなどの際に対象位置の温度を正確に把握できて修理品質があがる!
てことに今更ながら気付いてですね。
買いました。まぁ、わりと強力な電源とちょっとした電源、2件の開発案件が来ていて、その検証用に使えば元が取れますし。
スマフォ対応サーモグラフィーとしては「FLiR」が有名なのですが……これはAmazonのレギューを見ると正規品・1年保証であっても、「正常に使っていれば壊れるはずがない。故にお前の使い方が悪いのだ」というスーパーロジックで修理拒否される、という感じなので、欲しいけど壊れたら……と手が出せませんでした。
FLIR(フリアー)【国内正規品】iPhone/iPad用 FLIR ONE Pro LT版 4800画素 1年保証 赤外線サーモグラフィ
と、煩悶していたところ先輩氏から「Thermal Seekも良いぞ」と教えていただいて。今まで全然知らなかったのですが、とりあえずレビューに悪いこと書いてないですし(重視)、値段も手頃なので購入しました。FLIRのウリであるスマフォのカメラで撮影した可視光画像と重ねる機能はないんですが、こんだけ解像度高ければ、チップ部品も把握できるじゃろ、と。
【国内正規品】Seek Thermal シークサーマル Compact iPhone/iPad用 サーモグラフィーカメラ 赤外線カメラ
で、到着して5日、やっと開封して使ってみました。iPhone 12 miniに取り付けると、「アプリがねえよ」と言われ、App Storeへ行けばそのままインストーラーが起動する。全然探したり迷ったりする手間もなく起動します。
私の中の小学生はとりあえずマグカップを撮影したりしていたのですが、おおっと思ったのがこの写真。お仕事中のiMac 5kにカメラを向けてみた画像です。
上の方はiMac自身の発熱だと思うのですが、中央付近、巨大なウンコのような物体が左側の手を上げているのがおわかりでしょうか。モニターの前の私です。反射した像がそのまま写ってるんですね。
表面温度を捉える、という意味では、どーなの?という気もしますけど、反応速度や感度はとりあえず問題なさそうです。
キーボードの奥にあるマグカップを撮影したのがこれです。マグカップが温かいのはあたりまえですが、その手前にあるキーボード、ホームポジション付近がほんのり緑色になっているのがおわりいただけるでしょうか。
駅のベンチなどで他人の体温を感じるのは微妙に不快だったりしますが、これがあれば事前に観察して冷たいベンチにだけ座れます(何の意味が)。あと、フローリングで立ち仕事した後なんかもはっきり残りますね。
いやー、おもしろいわー。
あ、もちろん本来の目的である電源動作確認なども進めます。どうぞよしなに。
ニキシー管用の昇圧回路、一度トランスを使ってみたかったのですが、なかなかいい感じのトランスが見つからず。
が、黒井宏一さんの名著「最新のチップで動かすニキシー管」の巻末に、いい感じのカップル度インダクタが紹介されていて、早速注文しました。
高昇圧率DCDCについてはここを参考にしました。
DC/DC Converter Topologies and Techniques to Obtain High Boost Ratios
黒井さんの回路だと、タップ付きトランスで使うような回路で実験されているのですが、私は素直にトランスとして使ってみました。
NJW4131もLT1930も、トランスの配線は
スイッチ内蔵で、スイッチング周波数を300Khz-1Mhzの間で可変できます。秋月で入手できるのもポイント高いっす。
昇圧用スイッチングレギュレータIC 40V NJW4131GM1-A-TE2 (2個入)
普通のチョッパー型昇圧インバーター回路のインダクタンスをトランスにした形で、「High Boost Ratios」のFigure 4の形式に近い配線です。
結果は、最初700khzでは発熱が多くてパッとしなかったのですが、300khzまで落としてみたところ、入力0.80Wに対して、出力164V負荷36KΩ=0.75W、効率93%が得られました。約4Vから一応動作しますが、発熱が多く、入力電圧8Vぐらいが一番効率が良かったです。ただトランスが1:10に昇圧しているのでインバーターとしての昇圧は約2倍程度、ちょっと物足りないですが、ここは効率優先ということで……。
あとトランスの接続に関して黒井さんの方法も試してみたのですが書籍と同様効率75%でインダクタンスが80度近くまで暖かくなりました。タップ付きインダクタを使えばもっと良い結果がでるのではないでしょうか。
こっちはスイッチング周波数1.2Mhz、実験前からダメだろうと思っていたのですが、試してみないとね。あと、トランスの二次側の極性も実験してみました。
原因不明のトラブルでチップを1個死なせてべったりハンダ付けしたSOT23を交換するというつらい事件はありましたが、なんとか組み立てて試した結果、一番効率のいい電圧での入力1.82Wに対して、出力170V負荷36KΩ=0.80W、効率43%……ICもインダクタもチンチンに熱くなります。スイッチング周波数1.2Mhzに対して20uHのカプルドインダクターはデカすぎますね。
で、トランスの二次側を入れ替えてみたののですが、電圧がぜんぜん上がりませんでした。まぁそりゃそうですね……でも実験は大事。
※この度、PCBWayさんからの無償オファーをいただき、試用させていただきました。利用者としての公平な視点で記述したつもりですが、レビューの前提としてご承知おきください※
中国のPCB屋さん、どこも発注入力ページのデザインがかなり似通っていて、オープンソースがあるのか同じ業者さんがWebつくっているのだろうか。
ということで、他社とあまり変わりなくサクサク入力設定できます。丁度作ろうと思っていた45x40mmの基板があったので、両面、1.6mm、レジスト黒、鉛フリー仕上げ、あとはデフォルトで指定。$18。発注確定9月15日 14:12、完了予定は9月19日。レジスト緑で1.6mm厚だとキレイでない業者でもあんまり外れないので、今度はあえて黒にしてみました。黒はシルクがかすれるとすぐ目立ちます。
なお日本の住所とフォーマットが違うため、郵便番号入力から出てきた市町村名に都道府県名を付記する必要がありました。レポート入れたけど、改善されるかな。
便はDHLを指定しました。
個人的にはOCS/ANA好きなんですが、知っている限りElecrowとFusionしか扱っていないのが残念。
そろそろ進捗を確認しようと思っていたらその前にDHLから「荷物届くぞ」の連絡。DHL引き取りが9月17日 23:00。早い。
安心のDHL。と思ったら、配達予定9月24日になっておる……せっかくの連休のお楽しみがDHLによってツブされてしまう……orz
まぁ届くのは連休明けかな、と思っていたけどさー……。
ってことで、9月24日、DHLで届きました。梱包は段ボール箱に入ってDHLのビニール袋。DHLなので中身が潰れている、なんてこともなく。
上の写真は表面の拡大。
パッと見てシルクはズレやカスレなし。非常にクリア。ただし50um程度のズレがある。大きさ指定忘れてた24mil大の文字(「J3」ってやつ)はちょっと潰れ気味。
レジストについてはドリル穴との相対位置誤差はうちのツールでは誤差の範囲で測れません。塗りムラも特になくきれい。
断端は国産並みにツルツルです。こんなにツルツルなのは中華では初めてかも。
ハンダメッキは国産に比べるとちょっとムラがあるけども実用上問題なし。
お試しの機会をありがとうがございました。
はんだ付け作業はルーペ必須です。
※高齢者の感想です♡
ただ、モニター画面上の回路図を確認しながらはんだ付けをする際には、ルーペを上げたり下げたり上げたり下げたりを無限に繰り返すことになりうざいです。
ってことで、自動化しました。もうオートフォーカス老眼鏡も時代遅れさ(何を根拠に)。
市販の安い中華ヘッドルーペを改造します。
Nikayoni Stores ヘッドルーペ 拡大鏡 レンズ メガネ タイプ 2灯式 LED ライト 倍率 1.2倍 1.8倍 2.5倍 3.5倍
レンズホルダーとフレームを3Dプリンタで作成し、ホルダーとフレームの間を2個のサーボモーターで駆動します。バイザーの上に加速度センサー(ADXL345)とマイコン(Seeduino XIAO)を搭載し、はんだ付けするのにいい感じに頭を前傾させたらサーボを動かしてレンズを目の前におろします。
このアイディアそのものは最初にヘッドルーペを買った日に思いついたんですが、サーボモーターを固定するいい感じの方法が見つからず、ずっと放置して忘れてました。さらに、3Dプリンタを買って10ヶ月ほど経過したところで、このプロジェクトを思い出し、3Dプリンタの故障や不慣れなFDM機などにも翻弄されながら、ついに日の目をみた次第です。
ご笑覧ください ^ ^
Fusion360で設計し、Snapmaker 2.0のFDMプリンタで出力しました。FDM初めてなので、なかなかキレイにだせず、10回目の試作で何とかきちんと収まりました。
ここからダウンロードできます(github.com/TareObjects/3D-Datas)。
XIAOからサーボモーターSG-90を直接駆動しています。よくある中華基板のADXL345も直結です。秋月の45x45mm基板にのせました。サーボとの配線、手持ちのMolex 5051が丁度いい感じで使えたので、使ってます。
| XIAO | ADXL345 | Servo R | Servo L |
| 5V | 5V | 5V | |
| GND | GND | GND | GND |
| 8 | SIGNAL | ||
| 9 | SIGNAL | ||
| 3V3 | VCC | ||
| 4 | SDA | ||
| 5 | SCL | ||
| 3V3 | !CS |
あれ罫線が足りない。まぁいいや。
もうちょっといじりたいけど……とりあえず動くやつ。角度にヒステリシスつけないといけないし、中断モードも必要ですね。
M5 Atom Matrixを使って、40cm間隔の室温を計ってみました。
やっぱ天井は少し暑いですね……。ちなみに27.0-28.5度が青、30.5-32.0度が赤、その中間はグラデーションです。
LEDには1分ごとの温度変化をグラフにしていますが、そのぐらいだと変わりませんね……Ambientだとこんな感じです。
10:25ぐらいにサーキュレーターを作動させました。てきめん、天井付近の温度が下がり始めて、LEDにも変化が。
いや、君、下がりすぎでしょ?!
こういう楽しさをあなたに ^ ^
| CJMCU-9930 | ESP32 |
| VL | 3.3v |
| GND | GND |
| VCC | 3.3v |
| SCL | 22 |
| SDA | 21 |
//
// WiFi Secure, IFTTT
//
#include <WiFiClientSecure.h>
#define ssid "....ssid...."
#define password "....password...."
const char* server = "maker.ifttt.com"; // Server URL
const char* test_root_ca = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \
"-----END CERTIFICATE-----\n";
WiFiClientSecure client;
void submit() {
Serial.println("\nStarting connection to server...");
if (!client.connect(server, 443))
Serial.println("Connection failed!");
else {
Serial.println("Connected to server!");
// Make a HTTP request:
client.println("POST https://maker.ifttt.com/trigger/light_up/with/key/....api key.... HTTP/1.0");
client.println("Host: maker.ifttt.com");
client.println("Connection: close");
client.println();
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
Serial.println("headers received");
break;
}
}
while (client.available()) {
char c = client.read();
Serial.write(c);
}
client.stop();
}
}
//
// APDS9930
//
#include <Wire.h>
#include <APDS9930.h>
// Global Variables
APDS9930 apds = APDS9930();
float ambient_light = 0; // can also be an unsigned long
uint16_t ch0 = 0;
uint16_t ch1 = 1;
//
// setup
//
void setup() {
// Initialize Serial port
Serial.begin(115200);
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// attempt to connect to Wifi network:
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
// wait 1 second for re-trying
delay(1000);
}
Serial.print("Connected to ");
Serial.println(ssid);
// Initialize APDS-9930 (configure I2C and initial values)
if ( apds.init() ) {
Serial.println(F("APDS-9930 initialization complete"));
} else {
Serial.println(F("Something went wrong during APDS-9930 init!"));
}
// Start running the APDS-9930 light sensor (no interrupts)
if ( apds.enableLightSensor(false) ) {
Serial.println(F("Light sensor is now running"));
} else {
Serial.println(F("Something went wrong during light sensor init!"));
}
delay(500);
}
//
// loop
//
uint32_t prevMillis = 0;
void loop() {
// Read the light levels (ambient)
if ( !apds.readAmbientLightLux(ambient_light)) {
Serial.println(F("Error reading light values"));
} else {
Serial.print(F("Ambient: "));
Serial.println(ambient_light);
uint32_t thisMillis = millis();
if ((prevMillis == 0 || thisMillis - prevMillis > 180 * 1000) && ambient_light > 50) {
submit();
prevMillis = thisMillis;
}
// char fstr[16];
// dtostrf(ambient_light, 5, 1, fstr);
// M5.Lcd.drawString(fstr, 80, 70);
}
delay(100);
}
| XIAO | DRV8835 | その他の接続相手 |
| GND | GND | 外部電源のGND, Volumeのmin側 |
| 5V出力 | Vcc, MODE | |
| 3.3V出力 | Volumeのmax側 | |
| 3 | AIN1 | |
| 4 | AIN2 | |
| 5 | BIN1 | |
| 6 | BIN2 | |
| 7 | Volumeの中間 | |
| AOUT1 | モーターの黒 | |
| AOUT2 | モーターの緑 | |
| BOUT1 | モーターの青 | |
| BOUT2 | モーターの赤 | |
| VM | 外部電源の+側(5-17V) |
#include <Stepper.h>
Stepper myStepper(200, 3, 5);
bool running = false;
void setup() {
Serial.begin(115200);
pinMode(4, OUTPUT);
pinMode(6, OUTPUT);
}
void loop() {
int value = analogRead(7) / 2 - 5;
if (value < 0) {
if (running) {
digitalWrite(4, LOW);
digitalWrite(6, LOW);
running = false;
}
delay(50);
} else {
if (!running) {
digitalWrite(4, HIGH);
digitalWrite(6, HIGH);
running = true;
}
Serial.println(value);
myStepper.setSpeed(value) ;
myStepper.step(value / 10);
}
}