[ M5 ATOM Lite ] RTC DS3231を組み込む。
<開発環境>
VScode + platformIO
<使用ライブラリ>
・M5ATOM by M5Stack
・FastLED by Daniel Garcia
・LovyanGFX by lovyan03
・M5Unit-ENV byM5Stack
・RTClib by Adafruit(エイダフルーツ)
<Adafuit RTClibライブラリー>
DS3231初期化関数
/**************************************************************************/
/*!
@brief Start I2C for the DS3231 and test succesful connection
@param wireInstance pointer to the I2C bus
@return True if Wire can find DS3231 or false otherwise.
*/
/**************************************************************************/
bool RTC_DS3231::begin(TwoWire *wireInstance) {
if (i2c_dev)
delete i2c_dev;
i2c_dev = new Adafruit_I2CDevice(DS3231_ADDRESS, wireInstance);
if (!i2c_dev->begin())
return false;
return true;
}
Twowireクラス ESP32ライブラリ Wire.h
#ifndef TwoWire_h
#define TwoWire_h
#include <esp32-hal.h>
#if !CONFIG_DISABLE_HAL_LOCKS
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#endif
#include "Stream.h"
// WIRE_HAS_BUFFER_SIZE means Wire has setBufferSize()
#define WIRE_HAS_BUFFER_SIZE 1
// WIRE_HAS_END means Wire has end()
#define WIRE_HAS_END 1
#ifndef I2C_BUFFER_LENGTH
#define I2C_BUFFER_LENGTH 128 // Default size, if none is set using Wire::setBuffersize(size_t)
#endif
typedef void(*user_onRequest)(void);
typedef void(*user_onReceive)(uint8_t*, int);
class TwoWire: public Stream
{
protected:
uint8_t num;
int8_t sda;
int8_t scl;
size_t bufferSize;
uint8_t *rxBuffer;
size_t rxIndex;
size_t rxLength;
<略>
void onReceive( void (*)(int) );
void onRequest( void (*)(void) );
size_t slaveWrite(const uint8_t *, size_t);
};
extern TwoWire Wire;
extern TwoWire Wire1;
#endif
初期化例
//DS3231初期化-------------------------------------------------------
if (! rtc.begin(&Wire1)) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
時刻の取得、フォーマット化
今回のコードの解説
if(Ticker3.flag)//割り込み許可フラグでIN
{
Ticker3.flag = false;
//RTC時間表示------------------------------------------------
DateTime now = rtc.now();
timeFormat.getBytes(dateByteText,30);
Serial.printf("timeFormat.getBytes %s\r", dateByteText);
now.toString((char*)dateByteText);
Serial.printf("now.toString %s\r", dateByteText);
dateString = String((char*)dateByteText);
dateString = dateString.substring(0,8);
weekString = String((char*)dateByteText);
weekString = weekString.substring(9,12);
timeString = String((char*)dateByteText);
timeString = timeString.substring(13,21);
//SSD1306 時刻表示------------------------------------------
canvas1.beginTransaction();
canvas1.setTextSize(0.5);
canvas1.fillRect(0,0,128,20,TFT_BLACK);
canvas1.drawString(dateString,0,0);
canvas1.drawString(weekString,60,0);
canvas1.drawString(timeString,0,10);
canvas1.pushSprite(0,0);
canvas1.endTransaction();
//Serial.printf("%s %s %s\r",dateString,weekString,timeString);
}
時刻読み出し関数 now()
DateTime now = rtc.now();
DS3231から時刻データをI2C経由で受信したのち、
DataTimeクラスコンストラクタで、インスタンス化します。
/**************************************************************************/
/*!
@brief Get the current date/time
@return DateTime object with the current date/time
*/
/**************************************************************************/
DateTime RTC_DS3231::now() {
uint8_t buffer[7];
buffer[0] = 0;
i2c_dev->write_then_read(buffer, 1, buffer, 7);
return DateTime(bcd2bin(buffer[6]) + 2000U, bcd2bin(buffer[5] & 0x7F),
bcd2bin(buffer[4]), bcd2bin(buffer[2]), bcd2bin(buffer[1]),
bcd2bin(buffer[0] & 0x7F));
}
//DateTimeクラスコンストラクタ
DateTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour = 0,
uint8_t min = 0, uint8_t sec = 0);
時刻フォーマット化
表示文字列のフォーマット指定子 RTClib.cpp
| specifier | output |
|-----------|--------------------------------------------------------|
| YYYY | the year as a 4-digit number (2000--2099) |
| YY | the year as a 2-digit number (00--99) |
| MM | the month as a 2-digit number (01--12) |
| MMM | the abbreviated English month name ("Jan"--"Dec") |
| DD | the day as a 2-digit number (01--31) |
| DDD | the abbreviated English day of the week ("Mon"--"Sun") |
| AP | either "AM" or "PM" |
| ap | either "am" or "pm" |
| hh | the hour as a 2-digit number (00--23 or 01--12) |
| mm | the minute as a 2-digit number (00--59) |
| ss | the second as a 2-digit number (00--59) |
上記フォーマットに従った文字列を下記のようにヘッダーファイルに定義しておきます。
それと、フォーマットしたデータを格納するchar型配列を用意します。
const String timeFormat="YY/MM/DD DDD hh:mm:ss"; //表示したいフォーマット指定子の文字列
unsigned char dateByteText[30]; //フォーマットデータを格納するバイト配列
次に、now関数で生成したdataTimeクラスインスタンスのデータを、バイト配列に表示フォーマット化して、格納します。
DateTime now = rtc.now();
timeFormat.getBytes(dateByteText,30);
Serial.printf("timeFormat.getBytes %s\r", dateByteText);
now.toString((char*)dateByteText);
Serial.printf("now.toString %s\r", dateByteText);
dateString = String((char*)dateByteText);
dateString = dateString.substring(0,8);
weekString = String((char*)dateByteText);
weekString = weekString.substring(9,12);
timeString = String((char*)dateByteText);
timeString = timeString.substring(13,21);
・Arduino の String クラスの getBytes メソッドは、文字列を byte 型の配列にコピーします。
・コピーしたフォーマット指示文字列をDataTimeクラス
char *toString(char *buffer) const;に引数*bufferに読み込ませます。
これで、バイト配列dataByteTextに時刻データがフォーマット化されて、格納されます。たとえば、
”YY/MM/DD DDD hh:mm:ss”とフォーマット指定文字列を定義した場合、24/01/05 fri 13:15:00 とバイト配列に格納されます。
このバイト配列をから日時を取り出して、表示しています。
<コード>
DS3231用ヘッダーファイル
#ifndef __MYDS3231_H_
#define __MYDS3231_H_
#include <M5Atom.h>
#include "RTClib.h"
//RTC DS3231****************************
RTC_DS3231 rtc;
char daysOfTheWeek[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
const String timeFormat="YY/MM/DD DDD hh:mm:ss";
unsigned char dateByteText[30];
String dateString;
String timeString;
String weekString;
#endif
main.cpp
#include <M5ATOM.h>
#include "myLovyanGFX.h"
#include "myENV3.h"
#include "myInterrupt.h"
#include "myDS3231.h"
//グローバル変数********************************************************
uint ledColorNum=0;//RGBLEDの色配列指定
int ledColor[4]={0x000000,0xFF0000,0x008000, 0x0000FF};//RGBLED Red(8),Green(8),Blue(8)
myENV3* env3;
//LovyanGFXインスタンス*************************************************
static LGFX_SSD1306 lcd;
static LGFX_Sprite canvas(&lcd); //スプライトバッファ
static LGFX_Sprite canvas1(&lcd);
/***************************************************************
* setup
**************************************************************/
void setup()
{
int i;
//ATOM初期化---------------------------------------------------------
M5.begin(true,false, true); //初期化(UART, I2C wire0 , LED)
//割り込み処理-------------------------------------------------------
myInterrupt();//割り込み初期化
//SSD1306初期化 LovyanGFX--------------------------------------------
lcd.init();
canvas.createSprite(lcd.width(), lcd.height()-28);
canvas.setTextWrap(false); //自動折返し:無し
canvas1.createSprite(lcd.width(),28);
canvas1.setTextWrap(false);
//ENV3初期化---------------------------------------------------------
Wire.begin(26,32,400000U);
env3 = new myENV3();
//DS3231初期化-------------------------------------------------------
if (! rtc.begin(&Wire1)) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
//SSD1306初期描画-----------------------------------------------------
canvas1.beginTransaction();/// 通信トランザクションを開始する。(ペリフェラルを占有する);
canvas1.fillScreen(TFT_BLACK); //背景塗り潰し
canvas1.setTextColor(TFT_WHITE); //文字色
canvas1.setFont(&fonts::Font4);
canvas1.setTextSize(0.8);
canvas1.drawString("EVN3",5,0);
canvas1.pushSprite(0,0);
canvas1.endTransaction();/// 通信トランザクションを終了する。(ペリフェラルの占有を終了する);
//RGBLED点灯
M5.dis.drawpix(0,ledColor[2]);//blue点灯
ledColorNum=0;
delay(500);
}
/**************************************************************
* メインループ
****************************************************************/
void loop()
{
if(Ticker2.flag)
{
Ticker2.flag=false;
M5.dis.drawpix(0,ledColor[1]);//red点灯
if(env3->sht30.get()==0)
{
env3->tmp = env3->sht30.cTemp;
env3->humi = env3->sht30.humidity;
Serial.printf(
"Temp: %2.1f \r\nHumi: %2.0f%% \r\nPressure:%2.0fPa\r\n---\n",
env3->tmp,env3->humi,env3->pressure);
canvas.beginTransaction();
canvas.setFont(&fonts::Font4);
canvas.setTextSize(0.8);
canvas.fillScreen(TFT_BLACK); //背景塗り潰し
canvas.setCursor(0,3);
canvas.printf("%2.1fC",env3->tmp);
canvas.setCursor(60,3);
canvas.printf("%2.1f%%",env3->humi);
env3->pressure = env3->qmp6988.calcPressure()/100;
canvas.setCursor(3,20);
canvas.printf("%4.1fhPa",env3->pressure);
canvas.pushSprite(0,28);
canvas.endTransaction();
}else
{
env3->tmp=0;
env3->humi=0;
}
M5.dis.drawpix(0,ledColor[3]);//blue点灯
}
if(Ticker3.flag)
{
Ticker3.flag = false;
//RTC時間表示------------------------------------------------
DateTime now = rtc.now();
timeFormat.getBytes(dateByteText,30);
Serial.printf("timeFormat.getBytes %s\r", dateByteText);
now.toString((char*)dateByteText);
Serial.printf("now.toString %s\r", dateByteText);
dateString = String((char*)dateByteText);
dateString = dateString.substring(0,8);
weekString = String((char*)dateByteText);
weekString = weekString.substring(9,12);
timeString = String((char*)dateByteText);
timeString = timeString.substring(13,21);
//SSD1306 時刻表示------------------------------------------
canvas1.beginTransaction();
canvas1.setTextSize(0.7);
canvas1.fillRect(0,0,128,28,TFT_BLACK);
canvas1.drawString(dateString,0,0);
canvas1.drawString(weekString,70,0);
canvas1.drawString(timeString,0,14);
canvas1.pushSprite(0,0);
canvas1.endTransaction();
//Serial.printf("%s %s %s\r",dateString,weekString,timeString);
}
delay(10);
}
捕捉 C++ constメンバ関数
char *DateTime::toString(char *buffer) const;
メンバー関数の後ろにconstをつけると、メンバー変数を関数内で変更できなくなります。