こちらの記事はPCBGOGOさんに協賛いただいて書いています。 もしよろしければ下のリンク先を覗いていただけると私の記事が役に立ったことになるので覗いていただけたら嬉しいです。
先日透明基板を使ったサーボの駆動基板を作りましたが、実は回路自体は同じでUV印刷無しのものも作っていただいていました。アイディアとして、下記の記事のペッパーズゴーストの様に透明基板にキャラクターが投影される様なものを作れないかと考えていたためです。
結果としては本来の構成ではあまりきれいに映らなかったのですが、少し構成を変えてある程度納得できるレベルの作品を作ることができました。
概要
動作の様子です。
- X
M5Stack Core2と透明基板とハーフミラーを使ってNTPサーバから時刻を取って来てRTC使う時計ができた#PCBGOGO #pr pic.twitter.com/fBBqjeNpTl
— パスコンパス (@pscmps) August 14, 2024
- YouTube Short
ハーフミラーの後ろに透明基板、その後ろにM5 Stack Core2を配置しています。 ハーフミラーはけけゆうさんに教えていただいたアクリ屋さんで購入しました。長方形で寸法指定で購入できるので便利です。
また、透明基板を作る際に透明レジストを指定すると透明度が下がるという記事を拝見しました。今回の様に配線部を見せて透過させたい時はレジスト無しの指定が良いかもしれません。
プログラム
M5UnifiedのRTCのサンプルプログラムの処理をかなり参考にしています。NTPサーバから時刻を取得してRTCを使う一連の部分はほぼそのままいただいています。
#include "SD.h"
#include <M5Unified.h>
#include <WiFi.h>
#include <time.h>
#define JST 3600 * 9 // グリニッジ標準時と日本標準時の差
#define WIFI_SSID "****"
#define WIFI_PASSWORD "****"
#define NTP_TIMEZONE "JST-9"
#define NTP_SERVER1 "0.pool.ntp.org"
#define NTP_SERVER2 "1.pool.ntp.org"
#define NTP_SERVER3 "2.pool.ntp.org"
M5Canvas canvas(&M5.Lcd); // オフスクリーンバッファとしてキャンバスを使用
void setup() {
auto cfg = M5.config(); // M5Stack初期設定用の構造体を代入
cfg.external_rtc = true; // default=false. use Unit RTC.
M5.begin(cfg); // M5デバイスの初期化
configTzTime(NTP_TIMEZONE, NTP_SERVER1, NTP_SERVER2, NTP_SERVER3);
#ifdef WiFi_h
M5.Log.print("WiFi:");
WiFi.begin( WIFI_SSID, WIFI_PASSWORD );
for (int i = 20; i && WiFi.status() != WL_CONNECTED; --i)
{
M5.Log.print(".");
delay(500);
}
if (WiFi.status() == WL_CONNECTED) {
M5.Log.println("\r\nWiFi Connected.");
M5.Log.print("NTP:");
#if SNTP_ENABLED
while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED)
{
M5.Log.print(".");
delay(1000);
}
#else
delay(1600);
struct tm timeInfo;
while (!getLocalTime(&timeInfo, 1000))
{
M5.Log.print(".");
};
#endif
M5.Log.println("\r\nNTP Connected.");
time_t t = time(nullptr)+1; // Advance one second.
while (t > time(nullptr)); /// Synchronization in seconds
M5.Rtc.setDateTime( gmtime( &t ) );
}
else
{
M5.Log.println("\r\nWiFi none...");
}
#endif
M5.Display.clear();
canvas.setTextColor(TFT_BLACK);
canvas.setTextSize(3);
canvas.setFont(&fonts::lgfxJapanMincho_16);
while (false == SD.begin(GPIO_NUM_4, SPI, 15000000)) {
M5.Display.println("SD Wait...");
delay(500);
}
M5.Display.setBrightness(255);
M5.Lcd.fillScreen(WHITE);
// キャンバスを初期化
canvas.createSprite(320, 240); // 画面サイズに合わせてキャンバスを作成
}
int i = 0;
int prev_i = 0;
void loop() {
static constexpr const char* const wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
auto t = time(nullptr);
auto tm = localtime(&t); // for local timezone.
M5.delay(1);
M5.update();
canvas.startWrite();
canvas.setCursor(200, 135);
// 前の位置の上書きは不要。キャンバス上で背景を上書き
canvas.fillRect(0,0,320,240,WHITE);
// canvas.fillRect(0,0,320,240,SKYBLUE);
if(((i+50)%4)<2){
canvas.printf("%02d:%02d", tm->tm_hour, tm->tm_min);
canvas.drawPngFile(SD, "/mgr_dot1.png",i,75,100,100,0,0,2);
if((i+50)%2==0){
// M5.Speaker.tone(600,10);
}
}else{
canvas.printf("%02d %02d", tm->tm_hour, tm->tm_min);
canvas.drawPngFile(SD, "/mgr_dot1_2.png",i-1,75,100,100,0,0,2);
if((i+50)%2==0){
// M5.Speaker.tone(500,10);
}
}
// canvas.drawPngFile(SD, "/m5haikei.png",0,0);
canvas.drawPngFile(SD, "/m5haike2i.png",0,0);
// キャンバス全体を一度に画面に描画
canvas.pushSprite(0, 0);
canvas.endWrite();
i=i-2;
if(i<-50){
i=350;
}
}
後はあらかじめSDカードに保存していたpngの画像を表示して歩かせています。ポイントはcanvasに全て表示させたい画像をまとめてから画面を更新することです。このようにしないと頻繁に点滅してちらつくか、残像の様に前の画像が残ってしまいます。
今のところはかなりシンプルですが、抵抗や素子をピクミンの様に持ち運ばせたり、LEDの付近から画面内に電気のオンオフの紐を垂らして画面の背景の色や本物のLEDのオンオフを切り替えたり、まだまだ色々できそうです。表面実装のLEDであればM5Stackとの隙間に入りそうかなと思っています。
ドット絵
よく見ると画面内の配線と抵抗や素子の位置を合わせています。また、抵抗の色も合わせています。 画面の位置と基板の配置はPC画面上に直接基板を当ててアナログと根性で合わせました。

ドット絵はaspriteというソフトを使っていますが今回の広めの背景で結構操作に慣れるきっかけになったので良かったです。ちょっとしたgifアニメーション位なら作れそうな気がしてきました。最初は1dotを最小単位で書いていたのですが、途中からキャラクターが小さくなりすぎることに気が付いて、サイズを倍にしました。違う単位のdot絵が混在するのが許せなかったので、背景も書き直したのが地味に面倒でした。
↓修正前

↓修正後 ※MAX3485の文字がつぶれています

まとめ
今回は透明基板を使った時計を作ってみました。元々は基板に素子を実装してサーボも絡めてペッパーズゴーストの仕組みで作品を作りたいと思っていたのですが、実際の基板配線に対して画面上に素子があるのも個人的には面白いかなと思います。気が向いたらチップLEDを追加してみます。
つくると!6 | 誰でも参加できるものづくり博覧祭 | 誰でも参加できるものづくり博覧祭
この作品は8/31,9/1の福岡のつくると!で展示予定です。読んで下さり、ありがとうございました。