

前々回、前回に引き続きニキシー管時計の製作を行っていきます。今回は最終回で、筐体と完全なプログラムを作成します。
当初はラズベリーパイを使ったサイネージと一体化する予定でしたが、世界観が合わないので止めました。あと市販の小型LCDの解像度が低すぎて、遠目から見づらいというのもあります。
プログラムの作成#
今回実装する機能は以下の通りです。
- 自動調光
- 日付表示
- 炎ノイズシミュレート
- タッチセンサによる設定
- マルチコアによる時刻表示部の独立
完成したプログラムは前回の記事のダウンロードリンクから入手できるようにしていました。ここでは主要部分だけ抜き出して解説します。
① 変数宣言部#
#include "WiFi.h"
#include "time.h"
#include "Gaussian.h"
#include "math.h"
#define bright 12
#define colon 16
#define cds 35
#define button_cds T2//GPIO2
#define button_noise T0//GPIO4
#define button_date_time T4//GPIO13
const int PIN_BIT[] = {15,33,32,27};//4bit OUTPUT
const int CLK[] = {26,25,23,19,18,17};//hh:mm:ss output pin
const char* ssid = "Your-SSID";
const char* password = "Your-Password";
const char* ntpServer1 = "ntp.nict.jp";
const char* ntpServer2 = "time.google.com";
const long gmtOffset_sec = 9 * 3600;
const int daylightOffset_sec = 0;
int cnt = 0;
int brightness = 0;
int threshold1 = 30;
int threshold2 = 50;
int value_button_cds = 0;
int value_button_noise = 0;
int value_button_date_time = 0;
int ave_button_cds = 0;
int ave_button_noise = 0;
int ave_button_date_time = 0;
bool cds_flag = false;
bool noise_flag = false;
bool date_time_flag = false;//true -> Display date. false -> Display time.
volatile bool button_cds_flag = false;
volatile bool button_noise_flag = false;
volatile bool button_date_time_flag = false;cppここでは今後使うピン番号や各種変数、フラグなどを宣言しています。Gaussian.h は正規分布に基づく乱数を生成する際に使います。
② 表示部#
void displayDigit(unsigned short digit, unsigned short pin){
for(int i = 0; i < 4; i++){
digitalWrite(PIN_BIT[i], (1 << i) & digit);
}
digitalWrite(pin, HIGH);
delayMicroseconds(20);
digitalWrite(pin, LOW);
delayMicroseconds(20);
}
void printNixiesLocalTime(){
struct tm timeinfo;
getLocalTime(&timeinfo);
if(!getLocalTime(&timeinfo)){
for(int i = 0; i < 6; i++){
displayDigit(9, CLK[i]);
}
return;
}
displayDigit(timeinfo.tm_hour / 10, CLK[0]);
displayDigit(timeinfo.tm_hour % 10, CLK[1]);
displayDigit(timeinfo.tm_min / 10, CLK[2]);
displayDigit(timeinfo.tm_min % 10, CLK[3]);
displayDigit(timeinfo.tm_sec / 10, CLK[4]);
displayDigit(timeinfo.tm_sec % 10, CLK[5]);
}
void display_date(){
struct tm timeinfo;
getLocalTime(&timeinfo);
displayDigit((timeinfo.tm_year-100) / 10, CLK[0]);
displayDigit((timeinfo.tm_year-100) % 10, CLK[1]);
displayDigit((timeinfo.tm_mon+1) / 10, CLK[2]);
displayDigit((timeinfo.tm_mon+1) % 10, CLK[3]);
displayDigit(timeinfo.tm_mday / 10, CLK[4]);
displayDigit(timeinfo.tm_mday % 10, CLK[5]);
}cpp参考元をベースにしつつ、日付表示も追加しています。
③ 機能部#
void gotTouch1(){
button_cds_flag = true;
}
void gotTouch2(){
button_noise_flag = true;
}
void gotTouch3(){
button_date_time_flag = true;
}
void noise(int value){
Gaussian myGaussian(0.5, 0.3);
float myRandom = myGaussian.random();
if(myRandom<0){
myRandom=0;
}
if(myRandom>1){
myRandom=1;
}
ledcWrite(0,int(value*myRandom));
ledcWrite(1,int(value*myRandom*0.3));
}
int get_cds_value(){
float value = analogRead(cds)/8;
value = int(pow(1.0136,value));
return value;
}cppgotTouch1〜3 はタッチセンサ割り込みのフラグ立て、noise() は炎の揺らぎ再現、get_cds_value() はCdSによる自動調光です。
void functions(){
if(cnt % 50 == 0){
cnt = 0;
if(cds_flag){
brightness = get_cds_value();
}else{
brightness = 1024;
}
if(noise_flag){
noise(brightness);
}else{
ledcWrite(0,brightness);
ledcWrite(1,brightness);
}
}
cnt++;
//toggle auto brightness adjustment feature
if(button_cds_flag){
for(int i=0; i<5; i++){
value_button_cds += touchRead(button_cds);
}
if(value_button_cds/5 < threshold1){
cds_flag = !cds_flag;
//Wait for the finger to leave the touch sensor
while(ave_button_cds < threshold2){
for(int i=0; i<5; i++){
value_button_cds += touchRead(button_cds);
}
ave_button_cds = value_button_cds/5;
value_button_cds = 0;
delay(1);//Watchdog measures
}
ave_button_cds = 0;
}
value_button_cds = 0;
button_cds_flag = false;
}
//toggle flame noise simurating feature
if(button_noise_flag){
for(int i=0; i<5; i++){
value_button_noise += touchRead(button_noise);
}
if(value_button_noise/5 < threshold1){
noise_flag = !noise_flag;
while(ave_button_noise < threshold2){
for(int i=0; i<5; i++){
value_button_noise += touchRead(button_noise);
}
ave_button_noise = value_button_noise/5;
value_button_noise = 0;
delay(1);
}
ave_button_noise = 0;
}
value_button_noise = 0;
button_noise_flag = false;
}
//Display date while touching sensor
if(button_date_time_flag){
for(int i=0; i<5; i++){
value_button_date_time += touchRead(button_date_time);
}
if(value_button_date_time/5 < threshold1){
date_time_flag = true;
display_date();
while(ave_button_date_time < threshold2){
for(int i=0; i<5; i++){
value_button_date_time += touchRead(button_date_time);
}
ave_button_date_time = value_button_date_time/5;
value_button_date_time = 0;
delay(1);
}
ave_button_date_time = 0;
date_time_flag = false;
}
value_button_date_time = 0;
button_date_time_flag = false;
}
}cppCdS調光と炎ノイズシミュレート機能は共存できるようにしてあり、関係は以下の図の通りです。
日付表示機能は、タッチ判定があると date_time_flag を true にし、別コアで動いている時刻表示を一時停止させて、display_date() による上書きを行う仕組みです。
//略
date_time_flag = true;
display_date();
//略
date_time_flag = false;//button_date_time_flagではないことに注意cpp④ 設定系#
void subProcess(void *pvParameters){
delay(3000);
while(1){
functions();
delay(1);//Watchdog measures
}
}
void setup() {
Serial.begin(115200);
for(int i = 0; i < 6; i++){
pinMode(CLK[i],OUTPUT);
}
for(int i = 0; i < 4; i++){
pinMode(PIN_BIT[i],OUTPUT);
}
pinMode(bright,OUTPUT);
pinMode(colon,OUTPUT);
pinMode(cds,INPUT);
touchAttachInterrupt(button_cds, gotTouch1, threshold1);
touchAttachInterrupt(button_noise, gotTouch2, threshold1);
touchAttachInterrupt(button_date_time, gotTouch3, threshold1);
ledcSetup(0,500,10);
ledcSetup(1,500,10);
ledcAttachPin(bright,0);
ledcAttachPin(colon,1);
ledcWrite(0,1024);
ledcWrite(1,1024);
for(int i = 0; i < 6; i++){
displayDigit(0, CLK[i]);
}
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
xTaskCreatePinnedToCore(subProcess, "subProcess", 8192, NULL, 1, NULL, 0);
}
void loop() {
printNixiesLocalTime();
while(date_time_flag){
delay(1);
}
}cppsubProcess() が別コアで動く機能部、loop() がメインの時刻表示部です。
前回の記事の下部からは、全体の .ino ファイルも配布されていました。ファイル名は CompletedProgram.ino です。
筐体の製作#
筐体はアクリル板とパンチングアルミ板、秋月で買った丸型スペーサーで作りました。表から見える部分にはトルクスネジを使っています。
以下にAmazonで買ったものを載せておきます。元のアフィリエイト埋め込みは切れていたので、通常の商品リンクに置き換えています。
Amazon.co.jp ステンレス板 タッチセンサ用の電極として使用。 Amazon.co.jp ワッシャー スペーサーの微妙な高さ調節に使用。 Amazon.co.jp トルクスねじ 表から見える部分の固定に使用。 Amazon.co.jp 薄型ヒートシンク 高電圧DC-DCの裏面に貼り付けて放熱補助。
動かすとこんな感じです。
終わりに#
というわけで、ニキシー管時計製作プロジェクトはここで一区切りとなります。
構想から完成までに4か月ほどかかってしまいましたが、設計通り動いてくれてよかったです。
また暇になったらDAC製作でも再開しようかと思っています。
ここまでお付き合いいただきありがとうございました。