完成したクリスマスツリーの動作です
前回の記事の続きです
今度は、120個のLEDストリップから6個のLEDを切り出した残り114個を光らせます。
ラジオペンチさんのこちらの記事、Arduino nanoのプログラムをSTM32F103C8に移植する作業になります。
今回はCDSを使って暗くなったら点灯して、タイマーで指定した時間デモを行った後、消灯する仕様を追加しました。
CDSの接続はコメントに書いている通りです。入力ポートはPA7を選びました。
// 回路構成:3.3V → CdS → 10kΩ → GND
// ADC入力:CdSと10kΩの分圧点
// ADC仕様:12bit(0~4095)
// AD値の意味:
// 大きい → 明るい(CdS低抵抗)
// 小さい → 暗い(CdS高抵抗)
#define CDS_PIN PA7 // CdS接続ピン(有効時のみ必要)
このように半田付けした後、熱収縮チューブでまとめます。

なお、信号線にはトグルスイッチをつけました。
トグルスイッチを切ると、PA7がオープンで入力レベルが上がって強制ONになります。
生活の知恵ですね。

回路図と接続は前回書いた通りですね。
NEOPIXEL WS2813は、下記のようにつながっていますから、まず電源としてBLUEPILLの5V(VBUS)、
GNDを接続し、信号線はPA8から330Ωを介してDIに接続します。
多重化のBIはGNDに接続する様になっています。(私は間違って、DI同様 330ΩでPA8につなぎました)
CDSは秋月で買った500kΩくらいのものを使います。
3.3V電源につなぐと10kΩと直列にしているので、明るい時に3V近く、暗い時に0.2V程度になります。
STM32F103のADCは12bitなので、AD値で明るい時 3000以上、暗い時 250位になるわけです。
この辺は実験して決める必要があります。
最終のプログラムはここに置いておきます。
STM32_110Led_XmassTreeV230_add_three_effect5_20251227g.ino
実際のNEOPIXELのファンクション動作は、ラジオペンチさんのブログを参照してくださいね。
いつものように、移植はAIのChatGPTとGROK先生に手伝ってもらいました。
ChatGPTのコードレビューと修正提案
https://chatgpt.com/c/694d0c6e-4528-8320-bb08-acdb5aa8e484
GROKのSTM32F103 BLUE PILLプログラム TickerからHardwareTimerへ変更
https://x.com/i/grok/share/cxtIkN7zs8QsJXVA8aeNKCj1y
プログラムの最初の部分を説明しますね。
// STM32用 NeoPixel 110LEDクリスマスツリー // ラジオペンチさんのプログラムをChatGPTで改造 // MsTimer2 を Ticker に置き換え、PROGMEM削除 // 2025/12/21 Todopapa 改造版 // 251222_add three effects // 251223_180分で止まらないバグ修正(TickerからHardwareTimerに変更)→ 300分に変更 // 251223_CDSを使って、暗くなったら起動するように仕様変更 PA7に入力 3.3V → CDS → 10k → GND // 251224_ PA7に入力にトグルスイッチを入れて、明るくても動作するモードを追加 // 251224_ 110個 → 114個に変更 // 251224_ CDSで起動して240分動作し停止後、明るくなっても再起動しないバグを修正 // 251225_ CDSで起動して240分動作し停止後、暗かったら再起動するバグを修正 // 251227_ GROK先生のアドバイスでプログラムのリファクタリング実施 #include <Arduino.h> #include <Adafruit_NeoPixel.h> // #include <Ticker.h> // tickerはSTM32で安定して動かないので timerを使う方式に変更 #define LED_PIN PA8 // LED制御用のシリアル出力ピン #define LED_COUNT 114 // LEDカウント数 #define STD_BRIGHTNESS 50 // 標準明るさ #define OFF_TIMER_VAL 300 // 自動OFFタイマー(分単位)180→300(5時間) //#define OFF_TIMER_VAL 3 // 自動OFFタイマー(分単位)3分でテスト(1ループ終わらないと止まらない) // ===== CdS(光センサ)設定 ===== // CdSを使いたいときは以下の行のコメントを外す #define USE_CDS // CdS機能有効(コメントアウトするとCdS無効=常にデモ実行) // 回路構成:3.3V → CdS → 10kΩ → GND // ADC入力:CdSと10kΩの分圧点 // ADC仕様:12bit(0~4095) // // AD値の意味: // 大きい → 明るい(CdS低抵抗) // 小さい → 暗い(CdS高抵抗) #define CDS_PIN PA7 // CdS接続ピン(有効時のみ必要) #define CDS_DARK_TH 400 // これ以下で「暗い」暗いと判断する閾値(0-4095、実測で調整。例: 明るい時3000、暗い時500) #define CDS_BRIGHT_TH 800 // 550 → 800に変更 これ以上で「明るい」(ヒステリシス十分) volatile bool demoRunning = false; // デモ実行中フラグ #define RAND_COL7 col[random(7) + 1] // 7色からランダムに色を選択 #define RAND_COL8 col[random(8)] // 黒を含む8色からランダムに色を選択 #define RAND_COL7H colH[random(7) + 1] // 7色からランダムに色を選択 #define RAND_COL8H colH[random(8)] // 黒を含む8色からランダムに色を選択 Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); //timer 1秒周期 HardwareTimer timer(TIM2); // TIM2を使用
Arduno NanoのArduino IDEに、STM32F103はSTM32dunoという基板ライブラリを使います。
そのため、MSTIMER2ライブラリは使えないので、HardwareTimerというライブラリを使います。
詳しい使い方はこちらにありますので、興味のあるかたは見てくださいね。
実際のタイマーの設定に関してはこちらになります。 前にPZEM-004Tを使った電力計を作った時と同じですね。
// -----------------------
// 割り込み1秒ごと
// -----------------------
void timer2IRQ() { //割り込みハンドラ
static bool led = false;
led = !led;
digitalWrite(PC13, led); // PC13トグル(常に動作)
if (demoRunning) { // デモ実行中だけ時間を進める
sec++;
if (sec >= 60) {
sec = 0;
mmm++;
}
}
t2Flag = true; // フラグを立てる(loopで待つ用)
}
// -----------------------
void setup() {
Serial.begin(115200);
pinMode(PC13, OUTPUT); //割り込みモニタ用
strip.begin();
strip.show();
strip.setBrightness(STD_BRIGHTNESS);
// ===== 1秒タイマーの設定 =====
timer.pause(); // 一旦停止
timer.setPrescaleFactor(7200); // プリスケーラ:72MHz / 7200 = 10kHz
timer.setOverflow(10000); // オーバーフロー:10kHzで10000カウント = 1秒
timer.refresh(); // リセット
timer.attachInterrupt(timer2IRQ);// 割り込み関数設定
timer.resume(); //タイマー開始!
-------
void timer2IRQ() の割り込みハンドラは、demorunning中は sec,mmm という時間カウンタを1秒ずつ進める
ことと、モニタのためにPC13の緑LEDを点滅させます。
また、loop()での1秒待ちのために、 t2Flag = true; というフラグを立てます。
loop()内では demoRunningが有効な間は下記Functionのデモを実施します。(1ループ 5分ほど)
if (demoRunning) {
demo1;
demo2;
-------
}
デモを抜けると、タイマーUPの検出に続いて1秒待ちでCDSの状態を検知するルーチンに入ります。
/* ===== OFFタイマー経過で停止処理 =====
※ demoRunning の外に置くのが重要 */
if (mmm >= OFF_TIMER_VAL) {
if (demoRunning) {
demoRunning = false;
todayDone = true; // 今日の点灯は完了
}
strip.clear();
strip.show();
// 停止表示(PC13 ゆっくり点滅)
digitalWrite(PC13, HIGH);
delay(30);
digitalWrite(PC13, LOW);
delay(2000);
}
#ifdef USE_CDS
/* ===== CdS 読み取り ===== */
int cdsValue = analogRead(CDS_PIN);
/* ===== ヒステリシス付き明暗判定 ===== */
// 暗くなった判定(AD値が小さくなる)
if (!isDark && cdsValue < CDS_DARK_TH) {
isDark = true;
}
// 明るくなった判定(AD値が大きくなる)
else if (isDark && cdsValue > CDS_BRIGHT_TH) {
isDark = false;
todayDone = false; // 翌日の点灯を許可
}
/* ===== 暗くなったら起動(1日1回) ===== */
if (isDark) {
if (!demoRunning && !todayDone) {
demoRunning = true;
mmm = 0;
sec = 0;
}
}
else {
// 明るい間は必ず停止
demoRunning = false;
}
#endif
/* ===== 1秒同期(必ず最後) ===== */
while (!t2Flag) {
// 割り込み待ち(実質ビジーループ)
}
t2Flag = false;
} // loop() 終了
// -----------------------
これでCDSで暗くなったのを検出してデモ動作を開始して、時間が来たら消灯するようになります。
暗い間に再度起動しないように、 todayDone = false; というフラグが明るくならないとクリア
されないように制御します。
これで、毎日 暗くなると一定時間デモを行い、次の日も暗くなると同様にデモを行うことができるようになりました。
(クリスマス終わったんですが(笑))
ラジオペンチさんのクリスマスツリーは、アクリル板をカットして10段で n x (n+1)=110個のLEDを制御します。
トドお父さんは、そのような加工技術も根気もないので、そのまま小型のクリスマスツリーに巻きつけました。

これでも、十分きれいに光りますすね。さすが、ラジオペンチさん。
これで、今回のNIOPIXEL114個でのクリスマスツリー作成の話は終わりです。
クリスマスには何とか間に合ってよかったです!
それでは、皆さんおやすみなさい。





















