◯やりたいこと
前回RX8025を利用してSSD1306を表示器にした時計を作りRTCの制御ノウハウを得たのですが、

アマゾンを見ていたら送料共10個で千円のDS1307を発見。

おっ、こりゃ安くていいなという事でDS1307でもその制御ノウハウを得るため同様の時計を作って見ることにしました。
◯やったこと
・DS1302でスケッチ開発
DS1307が中国から届くまでの間DS1302で実験機を作り時計を動作させていました。ブレッドボードにWR02の書き込み機も載せDSシリーズ用のスケッチを開発。

DS1302はデータ通信が3wire接続のためシンプルサイズのWR02ではどうしても端子不足になってしまいコンパクトサイズのWR02を使用。

(写真はコンパクトサイズのWR02で組み上げたものです)
これでDS1302用のスケッチを開発した上で

DS1307に転用するという手順を踏みました。
・製作
前回RX8025で作ったブレッドボードをそのまま転用します。

これに32.767KHzの水晶や容量負荷を追加したものを作成し

チップを乗せると実験機の完成です。


・スケッチ
スケッチは以下の通りです。
/** * * WR02_DS07_OLED v0.0.1 (ok) * */ // BSP(Board Support Package) // ESP8266 Boards v3.1.2 // FlushSize ESP01:1MB Wroom-02D:2MB -02:4MB // Built-in Library (Arduino IDE v1.8.19) #include <Arduino.h> #include <time.h> #include <Wire.h> // BSP Built-in Library #include <ESP8266WiFi.h> // Installed Library #include <Adafruit_GFX.h> // v1.11.3 #include <Adafruit_SSD1306.h> // v2.5.7 // Global Variable /* WiFi */ #define WIFI_SSID "YOUR_SSID" #define WIFI_PASS "YOUR_PASS" /* TCP/IP */ IPAddress ip (YOUR_IP); // ex. (192,168,0,2) IPAddress gw (YOUR_GW); IPAddress dns (YOUR_DNS); // Google:(8,8,8,8) IPAddress mask (YOUR_MASK); // ex. (255,255,255,0) /* UART */ #define UART_TX 1 #define UART_RX 3 /* I2C */ #define I2C_SDA 2 #define I2C_SCK 0 #define I2C_CLK 100000 // fSCL=100KHz /* NTP DATA */ /* NTP DATA */ #define NTP_SRV1 "ntp.nict.jp" #define NTP_SRV2 "ntp.jst.mfeed.ad.jp" /* DS1307 */ #define RTC_ADR 0x68 // I2Cアドレス #define RTC_SEC 0x00 // レジスタ先頭アドレス 0-6 #define RTC_CTR 0x07 // SQW/OUT制御用アドレス #define RTC_RAM 0x08 // RTC RAM トップアドレス(08-3F) struct rtcTime { uint8_t sec; // 0-59 uint8_t min; // 0-59 uint8_t hour; // 0-23 uint8_t day; // 1-31 uint8_t mon; // 0-11 uint8_t year; // 00-99 uint8_t wday; // 0(Sun)-6(Sat) }; rtcTime RT = {0,0,0,1,1,0,0}; /* OLED */ #define OLED_ADR 0x3C // I2Cアドレス #define OLED_RESET -1 #define OLED_WIDTH 128 // OLED width(pixels) #define OLED_HEIGHT 64 // OLED height Adafruit_SSD1306 dsp(OLED_WIDTH, OLED_HEIGHT, &Wire, OLED_RESET); static const char *WDay[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; /* Time */ time_t UXTM = 0; // UnixTime uint32_t STTM = 0; // スタート時間 milli() ///// ハードウエア 初期設定 void setup() { // Start time STTM = millis(); // WiFi //WiFi.setOutputPower(10); // [dBm] max 20.5dBm // I2C Wire.begin(I2C_SDA, I2C_SCK); Wire.setClock(I2C_CLK); delayy(10); // Pin Mode digitalWrite(UART_TX, HIGH); pinMode (UART_TX, OUTPUT); // スタート表示 dsp_start(); // DS1307 安定化待ち while ((millis()-STTM)<1000) delayy(1); // RTC CHビットチェック /* CH on */ if (rtc_chkCH()) { dsp.setCursor( 0, 8); dsp.print("CH: Syncing NTP time"); dsp.display(); // DS1307 初期化 rtc_init(); // NTP時刻をRTCにセット rtc_setNTP(); } /* CH off */ else { dsp.setCursor( 0, 8); dsp.print("CH: off "); dsp.display(); } /* RTC時刻->RT */ rtc_getRT(); dsp.clearDisplay(); } ///// メインルーチン void loop() { // 毎日時刻合わせ if(RT.hour==1 && RT.min==3 && (RT.sec/5)==3) rtc_setNTP(); // 時刻表示 dsp_datetime(); // LED点滅 byte led = digitalRead(UART_TX); led = (led==LOW) ? HIGH : LOW; digitalWrite(UART_TX, led); delay(1000); } ///// I2C Control ///// // i2cのデータが確定するまで待つ inline void i2c_wait() { while(Wire.available()<1); } ///// DS1307 Control ///// // CH(Clock Halt)ビット読込み 1:クロック停止中 bool rtc_chkCH() { return (rtc_regRD(RTC_SEC) & 0x80); } // RTC 初期化 void rtc_init() { // SQW制御レジスタ(0x07)に1Hz出力設定 // (bit4) 1:出力on (bit3) 1:SQWon (bit2-0) 00:1Hz rtc_regWR(RTC_CTR, 0x18); } // RT構造からRTCのレジスタへ時刻をセット void rtc_setRT() { // RT構造上の時刻データ(DEC)をBCDに変換して書込み uint8_t buf[7]; buf[0] = dec2bcd(RT.sec & 0x7f); // CHビットクリア(時計スタート) buf[1] = dec2bcd(RT.min); buf[2] = dec2bcd(RT.hour); buf[3] = dec2bcd(RT.wday + 1); // DS1307は1=日曜 buf[4] = dec2bcd(RT.day); buf[5] = dec2bcd(RT.mon + 1); // DS1307は1-12 buf[6] = dec2bcd(RT.year % 100); // 下2桁のみ rtc_regSET(RTC_SEC, 7, buf); // RTC_SEC=0x00から7バイト書き込み } // RTCのレジスタから時刻を読出しRT構造にセット void rtc_getRT() { // RTCから日時データ読込 uint8_t data[7]; rtc_regGET(RTC_SEC, 7, data); // 日時データ(BCD)をDECに変換しRT構造にセット RT.sec = bcd2dec(data[0] & 0x7f); // CHビット無視 RT.min = bcd2dec(data[1]); RT.hour = bcd2dec(data[2]); RT.wday = bcd2dec(data[3]) - 1; // DS1307は1=日曜 -> 0=日曜 RT.day = bcd2dec(data[4]); RT.mon = bcd2dec(data[5]) - 1; // DS1307は1-12 -> 0-11 RT.year = bcd2dec(data[6]); } // NTPから時刻を取得しRTCにセット boolean rtc_setNTP() { /* WiFi接続:未接続はRTC時刻を更新しない */ if (wifi_connect()) { if (ntp_connect()) { /* NTP時刻->UXTM->RT構造->RTC */ UXTM = time(NULL)+1; uxt2rt(UXTM); rtc_setRT(); wifi_stop(); /* RTC カウント開始 */ return true; } } wifi_stop(); return false; } ///// RTC RAM Control ///// // RTC RAM に1byte書込む void rtc_ramWR(uint8_t adr, uint8_t val) { if (adr < RTC_RAM || adr > 0x3F) return; rtc_regWR(adr, val); } // RTC RAM から1byte読込む uint8_t rtc_ramRD(uint8_t adr) { if (adr < RTC_RAM || adr > 0x3F) return 0; return rtc_regRD(adr); } ///// RTC レジスタ Control ///// // RTCの指定レジスタから1byteデータを読込む uint8_t rtc_regRD(uint8_t reg) { Wire.beginTransmission(RTC_ADR); Wire.write(reg); Wire.endTransmission(); Wire.requestFrom(RTC_ADR, 1); i2c_wait(); return Wire.read(); } // RTCの指定レジスタへ1byteデータを書込む void rtc_regWR(uint8_t reg, uint8_t val) { Wire.beginTransmission(RTC_ADR); Wire.write(reg); Wire.write(val); Wire.endTransmission(); } // RTCの指定レジスタから連続でデータを書込む void rtc_regSET(uint8_t reg, int num, uint8_t *data) { Wire.beginTransmission(RTC_ADR); Wire.write(reg); Wire.write(data, num); Wire.endTransmission(); } // RTCの指定レジスタから連続でデータを読込む void rtc_regGET(uint8_t reg, int num, uint8_t *data) { Wire.beginTransmission(RTC_ADR); Wire.write(reg); Wire.endTransmission(); Wire.requestFrom(RTC_ADR, num); for (int i = 0; i < num; i++) { i2c_wait(); data[i] = Wire.read(); } } ///// Display Control ///// /* OLED 起動処理 */ void dsp_start() { // 初期化 dsp.begin(SSD1306_SWITCHCAPVCC, OLED_ADR); // 初期表示 /* タイトル表示 */ dsp.clearDisplay(); dsp.setTextColor(WHITE); dsp.setTextSize(1); dsp.setCursor( 0, 0); dsp.print("Start ESP8266/DS1307"); dsp.display(); //dsp.clearDisplay(); } /* OLED 時刻表示 */ void dsp_datetime() { // RTC 現在時刻の取得 rtc_getRT(); // DATE 文字列作成 char fmtDate[11]; sprintf( fmtDate, "%02d/%02d(%s)", RT.mon+1, RT.day, WDay[RT.wday] ); // TIME 文字列作成 char fmtTime[6]; sprintf( fmtTime, "%02d:%02d", RT.hour, RT.min ); // 表示 dsp.clearDisplay(); //dsp.fillRect( 0, 0,128, 16,BLACK); dsp.setTextSize(2); dsp.setCursor( 4, 4); dsp.println(fmtDate); //dsp.setTextSize(2); //dsp.setCursor( 68, 0); //dsp.println(fmtTime); dsp.setTextSize(3); dsp.setCursor( 20, 32); dsp.println(fmtTime); dsp.display(); } ///// WiFi Control ///// /* WiFi接続開始処理(ノンブロッキング風) * <引数> Timeout: 接続制限時間(0 or 省略は接続するまで無限ループ) * <戻値> 接続:true 未接続:False */ boolean wifi_connect() { return wifi_connect(0); } boolean wifi_connect(int timeout) { // 初期化 unsigned long start = 0; unsigned long last = 0; // ノンブロッキング用時間変数 unsigned long now = 0; unsigned long itvl = 500; // クライアント再接続時のインターバル時間 // WiFi 初期化 wifi_init(); // WiFiが未接続の場合のみbeginする if (WiFi.status() != WL_CONNECTED) { WiFi.begin(WIFI_SSID, WIFI_PASS); delayy(100); } // WiFi接続(指定時間内) 接続:true 接続失敗:false start = millis(); /* タイムアウトチェック */ while (timeout==0 || millis() - start < timeout) { // WiFi接続は非同期(ノンブロッキング)風で now = millis(); if (now-last >= itvl) { last = now; // 接続チェック if (WiFi.status() == WL_CONNECTED) { return true; } // 未接続の場合,0.3-0.5sec おきに再接続 itvl = random(300, 500); } yield(); } // 接続不可 エラーリターン return false; } /* WiFi 初期化処理 */ boolean wifi_init() { // WiFi初期化 (順番大事) // disconnect(true)されて再接続の場合 mode,configの再設定が必須 WiFi.mode(WIFI_STA); // 1.WiFi モード設定 WiFi.persistent(false); // 2.WiFi設定情報 保存しない if (!WiFi.config(ip, gw, mask, dns)) { // 3.固定ip 設定 /* DHCPフォールバック(DHCP切替え処理) */ if (!WiFi.config(0U, 0U, 0U)) { return false; } } delayy(100); return true; } /* WiFi stop処理 */ void wifi_stop() { // WiFiが接続の場合のみdisconnectする if (WiFi.status() == WL_CONNECTED) { WiFi.disconnect(true); } } /* 終了処理 */ void wifi_end() { // WiFi切断 wifi_stop(); // sleep ESP.deepSleep(60*1000*1000); delay(100); } ///// NTP Control ///// /* NTP 同期処理(ノンブロッキング風) * <引数> Timeout[ms]:同期制限時間(0 or 省略は同期するまで無限ループ) * <戻値> Unixtime or 0(エラー) */ time_t ntp_connect() { return ntp_connect(0); } time_t ntp_connect(int timeout) { // WiFi接続している時のみ時間取得する if (WiFi.status() != WL_CONNECTED) { return 0; } // 初期化 unsigned long start; unsigned long itvl = 250; // NTP再接続時のインターバル時間 unsigned long last = 0; // ノンブロッキング用時間変数 // タイムゾーン,NTPサーバー設定 configTzTime("JST-9", NTP_SRV1, NTP_SRV2); /* 最初に一定時間待機すると同期時にループが省かれる(短時間化) */ delayy(50); // NTPサーバー接続 (timeout秒間 接続確認) start = millis(); while (timeout==0 || millis()-start < timeout) { // NTP接続は非同期(ノンブロッキング)風で if (millis()-last >= itvl) { last = millis(); // 現在時刻の取得 time_t uxtm = time(NULL); // unix時間 // NPT同期チェック (RTC初期値=1,000,000,000) if(uxtm>1000000000) { return uxtm; } // 未接続の場合,0.2-0.3sec おきに再接続 itvl = random(200, 300); } yield(); } // 同期不可 エラーリターン return 0; } ///// Time Controle ///// // UnixTimeからRTC時刻(RT構造)を得る (UnixTime->tm構造->RT構造) void uxt2rt(time_t uxtm) { struct tm *t = localtime(&uxtm); RT.year = (t->tm_year + 1900) % 100; // tm_year: 1900年から RT.mon = t->tm_mon; // tm_mon : 0-11 RT.day = t->tm_mday; RT.wday = t->tm_wday; // 曜日: 0(日)-6(土) RT.hour = t->tm_hour; RT.min = t->tm_min; RT.sec = t->tm_sec; } // RTC時刻(RT構造)からUnixTimeを得る (RT構造->tm構造->UnixTime) time_t rt2uxt() { struct tm t; t.tm_year = RT.year + 100 ; // tm_year: 1900年から t.tm_mon = RT.mon; // tm_mon : 0-11 t.tm_mday = RT.day; t.tm_wday = RT.wday; // 曜日: 0(日)-6(土) t.tm_hour = RT.hour; t.tm_min = RT.min; t.tm_sec = RT.sec; t.tm_isdst= -1; // 夏時間設定を無効化 return mktime(&t); } ///// Other Control ///// // BCD Control /* DEC(10進)->BCD(2進化10進) */ inline int dec2bcd(int dec) { return (((dec / 10) << 4) | (dec % 10)); } /* BCD->DEC */ inline int bcd2dec(int bcd) { return ((bcd >> 4) * 10 + (bcd & 0x0f)); } // yield delay(ノンブロッキング ディレイ) inline void delayy(unsigned long tm) { unsigned long now = millis(); while (millis()-now < tm) { yield();} }
(相変わらずの我流スケッチ、読みにくさご勘弁です)
◯やってみて
実験機は無事一発動作してくれました。

よかった、よかった。
でも1個100円のRTCならモーションセンサーの長寿命化用に思い切って大量に使いまくることが出来そうで、なんか一安心です。
さっそく
をDS1307用に改造していくことにしましょう。
















































































































