最もシンプルなアナログ表示時計の作成
今まで、いろいろな時計を作ってきましたが、大きめのTFT液晶に最もシンプルな表示のアナログ時計が一番気に入りました。さすがにシンプル過ぎると思いましたが、余計な装飾をすればするほど気に入らなくなってしまい、時、分、秒の針だけで、時計盤も文字を使わずに目盛りだけにしました。これが一番シンプルでいいんです。
1.アナログ時計の構成
ILI9341ドライバで動くTFT液晶とRaspberry Pi Picoを、ジャンパーワイヤで接続します。接続方法は、前に投稿した記事と同じです。左がTFT液晶、右がRaspberry Pi Picoです。
GND -> GND
VCC -> 5V OUT
SCL -> GP18
SDA -> GP19
RES -> GP27
DC -> GP26
CS -> GP17
LCD -> 3.3V OUT
MISO -> GP16
2.プログラム
針の動きですが、時間の針に関してはリアルタイムで位置を反映しますが、分の針に関しては60秒経った時点で1分進む仕様にしました。その方が読みやすいのです。
あと、時刻の取得で、configTimeに東京オフセット時間を加えても有効になりませんでした。そのため、time関数で東京オフセット時間を加えています。この辺は、使用するライブラリにより変わってくると思います。今回、Raspberry Pi Picoで使用したボードは、「Earle F. Philhower, III」さん作です。
また、ライブラリにLovyanGFXを使っていますので、インストールしてください。
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <WiFi.h>
#include <time.h>
#define TFT_MISO 16
#define TFT_MOSI 19
#define TFT_SCLK 18
#define TFT_CS 17
#define TFT_DC 26
#define TFT_RST 27
// wifiの設定
const char* ssid = "XXXXXXXXXX";
const char* password = "XXXXXXXXXX";
// 時計の設定
const char* ntpServer = "ntp.jst.mfeed.ad.jp"; // NTPサーバー
const char* ntpServer2 = "ntp.nict.jp";
const long gmtOffset_sec = 9 * 3600; // 時差9時間
const int daylightOffset_sec = 0; // サマータイム設定なし
// 「あろしーど」さん定義例使用
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_ILI9341 _panel_instance;
lgfx::Bus_SPI _bus_instance;
public:
LGFX(void)
{
{ // バス制御の設定を行います。
auto cfg = _bus_instance.config(); // バス設定用の構造体を取得します。
cfg.spi_host = 0; // 使用するSPIを選択
cfg.spi_mode = 0; // SPI通信モードを設定 (0 ~ 3)
cfg.freq_write = 40000000; // 送信時のSPIクロック (最大80MHz, 80MHzを整数で割った値に丸められます)
cfg.freq_read = 20000000; // 受信時のSPIクロック
cfg.pin_sclk = TFT_SCLK; // SPIのSCLKピン番号を設定
cfg.pin_mosi = TFT_MOSI; // SPIのMOSIピン番号を設定
cfg.pin_miso = TFT_MISO; // SPIのMISOピン番号を設定 (-1 = disable)
cfg.pin_dc = TFT_DC; // SPIのD/Cピン番号を設定 (-1 = disable)
_bus_instance.config(cfg); // 設定値をバスに反映します。
_panel_instance.setBus(&_bus_instance); // バスをパネルにセットします。
}
{ // 表示パネル制御の設定を行います。
auto cfg = _panel_instance.config(); // 表示パネル設定用の構造体を取得します。
cfg.pin_cs = TFT_CS; // CSが接続されているピン番号 (-1 = disable)
cfg.pin_rst = TFT_RST; // RSTが接続されているピン番号 (-1 = disable)
cfg.pin_busy = -1; // BUSYが接続されているピン番号 (-1 = disable)
// cfg.panel_width = 240; // 実際に表示可能な幅
// cfg.panel_height = 320; // 実際に表示可能な高さ
// cfg.offset_x = 0; // パネルのX方向オフセット量
cfg.offset_y = 0; // パネルのY方向オフセット量
_panel_instance.config(cfg);
}
setPanel(&_panel_instance); // 使用するパネルをセットします。
}
};
void print_wifi_state(){
delay(100);
/* raspberry pi pico なので、シリアル出力はしない */
}
void setup_wifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
}
print_wifi_state();
}
// LovyanGFX インスタンス作成
static LGFX lcd;
static LGFX_Sprite canvas(&lcd);
void setup() {
// put your setup code here, to run once:
lcd.init();
canvas.createSprite(lcd.width(), lcd.height());
canvas.fillScreen(TFT_BLACK); // 背景塗り潰し
setup_wifi();
// オフセットを設定しても、無効になる??
configTime(30, gmtOffset_sec, ntpServer, ntpServer2) ;
delay(1000);
}
void loop() {
// put your main code here, to run repeatedly:
time_t t;
struct tm *tm;
t = time(NULL) + gmtOffset_sec ; // ここで、オフセットを加える
if (t != 0) {
tm = localtime(&t);
}
delay(100);
canvas.fillScreen(TFT_BLACK);
// 現在の時刻を取得
int hours = tm->tm_hour ;
int minutes = tm->tm_min ;
int seconds = tm->tm_sec ;
// 時計盤の描画
int radius = min(lcd.width(), lcd.height()) / 2 - 1;
int centerX = lcd.width() / 2;
int centerY = lcd.height() / 2;
// 時計盤の外枠を描画
canvas.drawCircle(centerX, centerY, radius, TFT_WHITE);
double wkhours = hours + (double)minutes / (double)60 ;
if (wkhours > 12) {
wkhours = wkhours - (double)12 ;
}
int hourAngle = (int)((double)360 * (double)wkhours / (double)12) ;
// int hourAngle = map(hours % 12, 0, 12, 0, 360); // 60分ごとに針を動かす時に使う
int minuteAngle = map(minutes, 0, 60, 0, 360);
// double wkminutes = minutes + (double)seconds / (double)60 ; // 分の針をリアルタイムで動かす時に使う
// int minuteAngle = (int)((double)360 * (double)wkminutes / (double)60) ;
int secondAngle = map(seconds, 0, 60, 0, 360);
// 時針の描画
int hourHandLength = radius * 0.5;
int hourHandX = centerX + hourHandLength * sin(hourAngle * DEG_TO_RAD);
int hourHandY = centerY - hourHandLength * cos(hourAngle * DEG_TO_RAD);
canvas.drawLine(centerX, centerY, hourHandX, hourHandY, TFT_WHITE);
// 分針の描画
int minuteHandLength = radius * 0.7;
int minuteHandX = centerX + minuteHandLength * sin(minuteAngle * DEG_TO_RAD);
int minuteHandY = centerY - minuteHandLength * cos(minuteAngle * DEG_TO_RAD);
canvas.drawLine(centerX, centerY, minuteHandX, minuteHandY, TFT_WHITE);
// 秒針の描画
int secondHandLength = radius * 0.85;
int secondHandX = centerX + secondHandLength * sin(secondAngle * DEG_TO_RAD);
int secondHandY = centerY - secondHandLength * cos(secondAngle * DEG_TO_RAD);
canvas.drawLine(centerX, centerY, secondHandX, secondHandY, TFT_WHITE);
int memoriLength ;
for (int i=0;i<12;i++) {
memoriLength = radius * 0.9 ;
int memoristartX = centerX + memoriLength * sin((i * 30) * DEG_TO_RAD);
int memoristartY = centerY - memoriLength * cos((i * 30) * DEG_TO_RAD);
memoriLength = radius * 1.0;
int memoriendX = centerX + memoriLength * sin((i * 30) * DEG_TO_RAD);
int memoriendY = centerY - memoriLength * cos((i * 30) * DEG_TO_RAD);
canvas.drawLine(memoristartX, memoristartY, memoriendX, memoriendY, TFT_YELLOW);
}
canvas.pushSprite(0, 0);
lcd.display();
delay(100);
}
ソースで、必要以上にキャストしている部分があると思いますが、省略できる部分は省いてもいいです。
Wi-Fiに接続しますから、SSIDとパスワードを各自指定してください。
3.あとは好きなようにカスタマイズしてください
秒の針に関しては、タイミングによって遅れて動いたり、早く動いたりするときがあります。この辺が気になる人は、チューニングしてください。また、いくら何でもシンプル過ぎると言う人も、自由に機能を追加してください。
4.YouTubeに動画を載せます
今回の記事紹介のために、YouTubeの動画を作成しました。その理由ですが、google検索から当サイトの記事が排除もしくは下位にされている関係で、アクセスが激減しているためです。
個人で運営するサイトは、もうgoogle検索に頼れなくなりました。たとえ内容がどれだけ優れていようが、法人で名前が通っているサイトの記事の方が優先表示されています。今回、YouTubeで宣伝しても効果は少ないと思いますが、いろいろ試していきたいと思います。