M5StackにRTCを接続しI2C通信で時刻の設定、取得を行う

##1. はじめに
M5StackにRTC(Real-Time Clock)を接続しI2C通信で時刻の設定、取得をできるようにしました。あわせてI2C通信でレジスタを読み書きするデバッグコマンドを作成しました。

TFT_Clock_Digitalスケッチと組み合わせると現在時刻を表示するデジタル時計になります。

![M5StackAndRTC.jpg](https://qiita-image-store.s3.amazonaws.com/0/221758/2fd797fe-84ce-0005-b2af-2c9bd2e6602d.jpeg)

##2. パーツ
- RTC
  - [aitendo](https://www.aitendo.com)の[RTCモジュール [HZ-8563]](http://www.aitendo.com/product/13987)
  - [PCF8563Tデータシート](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf)
  - [CR1220](https://www.google.co.jp/search?hl=ja&as_q=CR1220)をバッテリーとして使用します

- 温湿度センサ
  - I2C通信の確認用として購入
  - [M5Stack用プロトキット（温湿度センサ付き）](https://www.switch-science.com/catalog/3651/)
  - [DHT12データシート](https://docid81hrs3j1.cloudfront.net/medialibrary/2018/02/DHT12_Datasheet.pdf)


##3. I2C通信
M5StackでI2C通信を行うにあたりM5Stack用プロトキット(温湿度センサ付き)(に同梱されているDHT12)とそのサンプルスケッチを参考にしました。このキットはM5Stackに接続するためのGroveケーブルも入っていて動作確認もサンプルスケッチでできるためM5StackでI2Cを試すのに手ごろです。

- I2Cのデバイスのスレーブアドレスは7bitで、DHT12のスレーブアドレスは0x5cです。
- データシートにはWrite(0b0)またはRead(0b1)のビットを加えた8bit(Write:0xB8、Read:0xB9)でスレーブアドレスが記載されています(データシートの图15 Reference sequence diagram参照)。
- サンプルスケッチはArduino IDEを開き スケッチ例 → M5Stack → Modules → DHT12 にあります。
- サンプルスケッチはDHT12.ino、DHT12.cpp、DHT12.hの3ファイルで構成され、DHT12.cppの DHT12::read()でI2C通信を行っています。

###3.1 wire構造体
デバイスのスレーブアドレスや読み書きするレジスタのアドレス、バイト数などをまとめた構造体です。

- スレーブアドレスは「スレーブアドレス7bit + Write(0b0)またはRead(0b1)」の8bitの値を格納します。
  - 今回使用したデバイスがスレーブアドレスとしてWriteまたはReadのビットを含む8bitの値をデータシートに載せていてその値をそのまま使えるようにしています。
      - DHT12:0xB8(Write)、0xB9(Read)
      - PCF8563:0xA2(Write)、0xA3(Read) (データシートの8.5 Setting and reading the time参照)
- send stopフラグは[Wire.endTransmission()](https://www.arduino.cc/en/Reference/WireEndTransmission)のstopパラメータを参照してください。

```c
typedef struct {
    byte slave_addr; // デバイスのスレーブアドレス
    byte reg_addr;   // 読み書きするレジスタのアドレス
    byte bytes;      // 読み書きするバイト数
    byte *data;      // 読み書きするデータを格納するメモリのポインタ
    bool send_stop;  // send stopフラグ
    bool dbg_print;  // デバッグ用のUART出力フラグ
}wire_t;
```

###3.2 レジスタの値の取得
デバッグ用のSerial.print()を省略したものを以下に載せます(全部入りのプログラムは末尾にあります)。

- スレーブアドレスを1bit右シフトし7bitの値を生成しています。
- デバイスから取得したバイト数を返却します。

```c
int wire_get(wire_t *wire)
{
    byte i, i_max;
    byte received_num;
    
    if( NULL == wire ){ return ERR_NULL; }

    if( wire->dbg_print )
    {
        //省略
    }
    
    Wire.beginTransmission(wire->slave_addr>>1);
    Wire.write(wire->reg_addr);
    Wire.endTransmission(wire->send_stop);

    received_num = Wire.requestFrom(wire->slave_addr>>1, wire->bytes);
    if( wire->dbg_print )
    {
        //省略
    }

    if( Wire.available() < wire->bytes )
    {
        i_max = Wire.available();
    }
    else
    {
        i_max = wire->bytes;
    }

    for(i=0; i<i_max; i++)
    {
        wire->data[i] = Wire.read();
        if( wire->dbg_print )
        {
            //省略
        }
    }

    if( wire->dbg_print )
    {
        //省略
    }

    return received_num;
}
```

###3.3 レジスタへの値の設定
デバッグ用のSerial.print()を省略したものを以下に載せます。

```c
int wire_set(wire_t *wire)
{
    int i;
    
    if( NULL == wire ){ return ERR_NULL; }

    if( wire->dbg_print )
    {
        //省略
    }

    Wire.beginTransmission(wire->slave_addr>>1);
    Wire.write(wire->reg_addr);
    for(i=0; i<wire->bytes; i++)
    {
        Wire.write(wire->data[i]);
    }
    Wire.endTransmission();

    if( wire->dbg_print )
    {
        //省略
    }

    return ERR_OK;
}
```

###3.4 I2Cレジスタ読み書きのデバッグコマンド
[M5Stackでコマンド操作](https://qiita.com/pbjpkas/items/c8c26837a1a5a243f4b6)に以下のコマンドを追加しました。末尾のプログラムのcmd_wire_get()、cmd_wire_set()をご参照ください。

- wireget
- wireset

以下はDHT12のレジスタを読み出した例です。スレーブアドレス:0xB9、レジスタのアドレス:0x00、バイト数:5バイト、Send Stopフラグ:trueで読み出しを行い、湿度73.1%、温度22.6℃と分かりました。
![i2c_dht12_read.png](https://qiita-image-store.s3.amazonaws.com/0/221758/3703efd8-275c-d8d4-9b60-7c240f45912e.png)


##4. RTCの読み書き
PCF8563は時計のほかにアラームやタイマーの機能も備えますが時計に絞って以下のプログラムを作成しました。

- 時刻を設定する
- 時刻を取得する
- 取得した時刻をUARTへ出力する

###4.1 pcf8563構造体
PCF8563のスレーブアドレスやレジスタの値を保持する変数などをまとめた構造体です。

```c
typedef struct {
    byte slave_addr;                // 0xA2(Write), 0xA3(Read)
    bool send_stop;

    // 省略

    /* Time and date registers */
    byte time_reg_addr;             // 0x02
    byte time_reg_length;           // 7bytes(0x02-0x08)
    byte vl;                        // VL_seconds register, bit7
    byte seconds;                   // VL_seconds register, bit6-0
    byte minutes;
    byte hours;
    byte days;
    byte weekdays;
    byte century;                   // Century_months register, bit7
    byte months;                    // Century_months register, bit4-0
    byte years;

    // 省略

}pcf8563_t;
```

###4.2 pcf8563_open()
pcf8563_open()でpcf8563ハンドラを生成します。スレーブアドレス、レジスタのアドレス、読み書きするレジスタのバイト数などを設定します。

```c
pcf8563_t *pcf8563_open()
{
    pcf8563_t *pcf8563;
    pcf8563 = (pcf8563_t*)malloc(sizeof(pcf8563_t));
    if( NULL == pcf8563 ){ return pcf8563; }
    
    //デバイス共通
    pcf8563->slave_addr     = 0xa2;
    pcf8563->send_stop      = true;

    //Timeレジスタ
    pcf8563->time_reg_addr  = 0x02;
    pcf8563->time_reg_bytes = 7;

    return pcf8563;
}
```

###4.3 pcf8563_close()
pcf8563ハンドラを破棄します。

```c
int pcf8563_close(pcf8563_t *pcf8563)
{
    if( NULL == pcf8563 ){ return ERR_NULL; }

    free(pcf8563);
    pcf8563 = NULL;

    return ERR_OK;
}
```

###4.4 pcf8563_set_time()
pcf8563_set_time()は引数で与えられた時刻データを[BCD format](https://www.google.co.jp/search?hl=ja&as_q=BCD+format)で整形し、wire_set()に渡します。

```c
int pcf8563_set_time(pcf8563_t *pcf8563)
{
    wire_t *wire;
    byte   *wiredata;

    if( NULL == pcf8563 ){ return ERR_NULL; }

    wire = (wire_t*)malloc(sizeof(wire_t));
    if( NULL == wire )
    {
        return ERR_MALLOC;
    }

    wiredata  = (byte*)malloc(pcf8563->time_reg_bytes);
    if( NULL == wiredata )
    {
        free(wire);
        return ERR_MALLOC;
    }

    wire->slave_addr = pcf8563->slave_addr;
    wire->reg_addr   = pcf8563->time_reg_addr;
    wire->bytes      = pcf8563->time_reg_bytes;
    wire->data       = wiredata;
    wire->send_stop  = pcf8563->send_stop;
    wire->dbg_print  = false;
    
    wire->data[0] = (pcf8563->seconds/10)<<4 | pcf8563->seconds%10; // seconds
    wire->data[1] = (pcf8563->minutes/10)<<4 | pcf8563->minutes%10; // minutes
    wire->data[2] = (pcf8563->hours  /10)<<4 | pcf8563->hours  %10; // hours
    wire->data[3] = (pcf8563->days   /10)<<4 | pcf8563->days   %10; // days
    wire->data[4] = 0x00;                                           // weekdays
    wire->data[5] = (pcf8563->months /10)<<4 | pcf8563->months %10; // months
    wire->data[6] = (pcf8563->years  /10)<<4 | pcf8563->years  %10; // years

    wire_set(wire);

    free(wiredata);
    free(wire);

    return ERR_OK;
}
```

###4.5 pcf8563_get_time()
pcf8563_get_time()はwire_get()でRTCのレジスタを読み出し、BCD Formatの時刻データを年月日時分秒にパースしてハンドラへ格納します。

```c
int pcf8563_get_time(pcf8563_t *pcf8563)
{
    wire_t *wire;
    byte   *wiredata;

    if( NULL == pcf8563 ){ return ERR_NULL; }

    wire = (wire_t*)malloc(sizeof(wire_t));
    if( NULL == wire )
    {
        return ERR_MALLOC;
    }

    wiredata  = (byte*)malloc(pcf8563->time_reg_bytes);
    if( NULL == wiredata )
    {
        free(wire);
        return ERR_MALLOC;
    }

    wire->slave_addr = pcf8563->slave_addr;
    wire->reg_addr   = pcf8563->time_reg_addr;
    wire->bytes      = pcf8563->time_reg_bytes;
    wire->data       = wiredata;
    wire->send_stop  = pcf8563->send_stop;
    wire->dbg_print  = false;

    wire_get(wire);
    
    pcf8563->vl       =   wire->data[0] & 0x80;
    pcf8563->seconds  = ((wire->data[0] & 0x70) >> 4)*10 + (wire->data[0] & 0x0f);
    pcf8563->minutes  = ((wire->data[1] & 0x70) >> 4)*10 + (wire->data[1] & 0x0f);
    pcf8563->hours    = ((wire->data[2] & 0x30) >> 4)*10 + (wire->data[2] & 0x0f);
    pcf8563->days     = ((wire->data[3] & 0x30) >> 4)*10 + (wire->data[3] & 0x0f);
    pcf8563->weekdays =   wire->data[4] & 0x07;
    pcf8563->months   = ((wire->data[5] & 0x10) >> 4)*10 + (wire->data[5] & 0x0f);
    pcf8563->years    = ((wire->data[6] & 0xf0) >> 4)*10 + (wire->data[6] & 0x0f);

    free(wiredata);
    free(wire);

    return ERR_OK;
}
```

###4.6 pcf8563_print()
pcf8563_print()はpcf8563構造体のメンバーの値をUARTへ出力します。

```c
int pcf8563_print(pcf8563_t *pcf8563)
{
    if( NULL == pcf8563 ){ return ERR_NULL; }

    Serial.print(pcf8563->years);    Serial.print(" ");
    Serial.print(pcf8563->months);   Serial.print(" ");
    Serial.print(pcf8563->weekdays); Serial.print(" ");
    Serial.print(pcf8563->days);     Serial.print(" ");
    Serial.print(pcf8563->hours);    Serial.print(" ");
    Serial.print(pcf8563->minutes);  Serial.print(" ");
    Serial.print(pcf8563->seconds);  Serial.print("\r\n");

    return ERR_OK;
}
```

###4.7 pcf8563のコマンド
以下のコマンドを追加しました。末尾のプログラムのcmd_execute()をご参照ください。

- pcf8563 set
- pcf8563 print

以下はpcf8563 printの実行例です。
![i2c_pcf8563_print.png](https://qiita-image-store.s3.amazonaws.com/0/221758/24ad7935-679f-4c3c-729b-a2b4c348d809.png)


##5. TFT_Clock_Digitalへの応用
TFT_Clock_Digitalにおいて、ビルド日時をパースして変数hh、mm、ssに値を代入する代わりにsetup()で以下の関数を実行することでRTCから取得した時刻で時計のカウントを開始できるようになります。

```c
int tft_clock_get_current_time()
{
    pcf8563_t *pcf8563;
    pcf8563 = pcf8563_open();
    if( NULL == pcf8563 ){ return NULL; }
    
    pcf8563_get_time(pcf8563);
    hh = pcf8563->hours;
    mm = pcf8563->minutes;
    ss = pcf8563->seconds;
    
    pcf8563_close(pcf8563);
    
    return ERR_OK;
}
```


##6. プログラム

```c:MyM5Stack002.ino
#include <M5Stack.h>
#include <Wire.h>

#define ERR_OK       0
#define ERR_INVALID -1 // 不正
#define ERR_NULL    -2 // 引数がNULL
#define ERR_MALLOC  -3 // mallocの戻り値がNULL

/***********************************************************************
  Function Prototype : setup and loop
 ***********************************************************************/
void setup();
void loop();

/***********************************************************************
  Function Prototype : Real-time Clock PCF8563
 ***********************************************************************/
typedef struct {
    byte slave_addr;               // 0xA2(Write), 0xA3(Read)
    bool send_stop;

    /* Control and status registers */
    byte control_status_reg_addr;  // 0x00
    byte control_status_reg_bytes; // 2bytes
    byte control_status_1;
    byte control_status_2;
    
    /* Time and date registers */
    byte time_reg_addr;            // 0x02
    byte time_reg_bytes;           // 7bytes(0x02-0x08)
    byte vl;                       // VL_seconds register, bit7
    byte seconds;                  // VL_seconds register, bit6-0
    byte minutes;
    byte hours;
    byte days;
    byte weekdays;
    byte century;                  // Century_months register, bit7
    byte months;                   // Century_months register, bit4-0
    byte years;

    /* Alarm registers */
    byte alarm_reg_addr;           // 0x09
    byte alarm_reg_bytes;          // 4bytes
    byte minute_alarm;
    byte hour_alarm;
    byte day_alarm;
    byte weekday_alarm;

    /* CLKOUT control register */
    byte clkout_reg_addr;          // 0x0d
    byte clkout_reg_bytes;         // 1byte
    byte clkout_control;

    /* Timer registers */
    byte timer_reg_addr;           // 0x0e
    byte timer_reg_bytes;          // 2bytes
    byte timer_control;
    byte timer;
}pcf8563_t;

pcf8563_t *pcf8563_open();
int pcf8563_close(pcf8563_t *pcf8563);
int pcf8563_print(pcf8563_t *pcf8563);
int pcf8563_get_time(pcf8563_t *pcf8563);
int pcf8563_set_time(pcf8563_t *pcf8563);

/***********************************************************************
  Function Prototype : Wire(I2C)
 ***********************************************************************/
typedef struct {
    byte slave_addr; // デバイスのスレーブアドレス
    byte reg_addr;   // 読み書きするレジスタのアドレス
    byte bytes;      // 読み書きするバイト数
    byte *data;      // 読み書きするデータを格納するメモリのポインタ
    bool send_stop;  // send stopフラグ
    bool dbg_print;  // デバッグ用のUART出力フラグ
}wire_t;

int wire_get(wire_t *wire);
int wire_set(wire_t *wire);

/***********************************************************************
  Function Prototype : Command Mode
 ***********************************************************************/
#define CMD_QUIT             1
#define CMD_OK          ERR_OK
#define CMD_BUF_LENGTH      64 // 63+1
#define CMD_MAX_LENGTH      64 // 63+1
#define ARG_MAX_LENGTH      64 // 63+1

void cmd_print_help(void);
void cmd_print_ver(void);
int  cmd_getstr(char *buf);
int  cmd_getnum(int type);
int  cmd_wire_get(void);
int  cmd_wire_set(void);
int  cmd_execute(char *buf);
void cmd_rx_data(void);

/***********************************************************************
  Function : setup and loop
 ***********************************************************************/
// the setup routine runs once when M5Stack starts up
void setup(){

    // Initialize the M5Stack object
    M5.begin();
    Serial.begin(115200);
    Wire.begin();

    // LCD display
    M5.Lcd.print("MyM5STACK Ver.002");
}

// the loop routine runs over and over again forever
void loop() {
    if(Serial.available()) {
        cmd_rx_data();
    }
}

/***********************************************************************
  Function : Real-time Clock PCF8563
 ***********************************************************************/
pcf8563_t *pcf8563_open()
{
    pcf8563_t *pcf8563;
    pcf8563 = (pcf8563_t*)malloc(sizeof(pcf8563_t));
    if( NULL == pcf8563 ){ return pcf8563; }
    
    //デバイス共通
    pcf8563->slave_addr     = 0xa2;
    pcf8563->send_stop      = true;

    //Timeレジスタ
    pcf8563->time_reg_addr  = 0x02;
    pcf8563->time_reg_bytes = 7;

    return pcf8563;
}
int pcf8563_close(pcf8563_t *pcf8563)
{
    if( NULL == pcf8563 ){ return ERR_NULL; }

    free(pcf8563);
    pcf8563 = NULL;

    return ERR_OK;
}
int pcf8563_print(pcf8563_t *pcf8563)
{
    if( NULL == pcf8563 ){ return ERR_NULL; }

    Serial.print(pcf8563->years);    Serial.print(" ");
    Serial.print(pcf8563->months);   Serial.print(" ");
    Serial.print(pcf8563->weekdays); Serial.print(" ");
    Serial.print(pcf8563->days);     Serial.print(" ");
    Serial.print(pcf8563->hours);    Serial.print(" ");
    Serial.print(pcf8563->minutes);  Serial.print(" ");
    Serial.print(pcf8563->seconds);  Serial.print("\r\n");

    return ERR_OK;
}
int pcf8563_get_time(pcf8563_t *pcf8563)
{
    wire_t *wire;
    byte   *wiredata;

    if( NULL == pcf8563 ){ return ERR_NULL; }

    wire = (wire_t*)malloc(sizeof(wire_t));
    if( NULL == wire )
    {
        return ERR_MALLOC;
    }

    wiredata  = (byte*)malloc(pcf8563->time_reg_bytes);
    if( NULL == wiredata )
    {
        free(wire);
        return ERR_MALLOC;
    }

    wire->slave_addr = pcf8563->slave_addr;
    wire->reg_addr   = pcf8563->time_reg_addr;
    wire->bytes      = pcf8563->time_reg_bytes;
    wire->data       = wiredata;
    wire->send_stop  = pcf8563->send_stop;
    wire->dbg_print  = false;

    wire_get(wire);
    
    pcf8563->vl       =   wire->data[0] & 0x80;
    pcf8563->seconds  = ((wire->data[0] & 0x70) >> 4)*10 + (wire->data[0] & 0x0f);
    pcf8563->minutes  = ((wire->data[1] & 0x70) >> 4)*10 + (wire->data[1] & 0x0f);
    pcf8563->hours    = ((wire->data[2] & 0x30) >> 4)*10 + (wire->data[2] & 0x0f);
    pcf8563->days     = ((wire->data[3] & 0x30) >> 4)*10 + (wire->data[3] & 0x0f);
    pcf8563->weekdays =   wire->data[4] & 0x07;
    pcf8563->months   = ((wire->data[5] & 0x10) >> 4)*10 + (wire->data[5] & 0x0f);
    pcf8563->years    = ((wire->data[6] & 0xf0) >> 4)*10 + (wire->data[6] & 0x0f);

    free(wiredata);
    free(wire);

    return ERR_OK;
}
int pcf8563_set_time(pcf8563_t *pcf8563)
{
    wire_t *wire;
    byte   *wiredata;

    if( NULL == pcf8563 ){ return ERR_NULL; }

    wire = (wire_t*)malloc(sizeof(wire_t));
    if( NULL == wire )
    {
        return ERR_MALLOC;
    }

    wiredata  = (byte*)malloc(pcf8563->time_reg_bytes);
    if( NULL == wiredata )
    {
        free(wire);
        return ERR_MALLOC;
    }

    wire->slave_addr = pcf8563->slave_addr;
    wire->reg_addr   = pcf8563->time_reg_addr;
    wire->bytes      = pcf8563->time_reg_bytes;
    wire->data       = wiredata;
    wire->send_stop  = pcf8563->send_stop;
    wire->dbg_print  = false;
    
    wire->data[0] = (pcf8563->seconds/10)<<4 | pcf8563->seconds%10; // seconds
    wire->data[1] = (pcf8563->minutes/10)<<4 | pcf8563->minutes%10; // minutes
    wire->data[2] = (pcf8563->hours  /10)<<4 | pcf8563->hours  %10; // hours
    wire->data[3] = (pcf8563->days   /10)<<4 | pcf8563->days   %10; // days
    wire->data[4] = 0x00;                                           // weekdays
    wire->data[5] = (pcf8563->months /10)<<4 | pcf8563->months %10; // months
    wire->data[6] = (pcf8563->years  /10)<<4 | pcf8563->years  %10; // years

    wire_set(wire);

    free(wiredata);
    free(wire);

    return ERR_OK;
}

/***********************************************************************
  Function : Wire(I2C)
 ***********************************************************************/
int wire_get(wire_t *wire)
{
    byte i, i_max;
    byte received_num;
    
    if( NULL == wire ){ return ERR_NULL; }

    if( wire->dbg_print )
    {
        Serial.print("!!! cmd_wire_get head !!!\r\n");
        Serial.print(    "Slave Addr   : 0x"); Serial.print(wire->slave_addr, HEX);
        Serial.print("\r\nReg   Addr   : 0x"); Serial.print(wire->reg_addr,   HEX);
        Serial.print("\r\nBytes        : 0d"); Serial.print(wire->bytes,      DEC);
        Serial.print("\r\nSend Stop    : "  ); Serial.print(wire->send_stop,  DEC);
        Serial.print("\r\n");
    }
    
    Wire.beginTransmission(wire->slave_addr>>1);
    Wire.write(wire->reg_addr);
    Wire.endTransmission(wire->send_stop);

    received_num = Wire.requestFrom(wire->slave_addr>>1, wire->bytes);
    if( wire->dbg_print )
    {
        Serial.print("received num : 0d");
        Serial.print(received_num, DEC);
        Serial.print("\r\n");
    }

    if( Wire.available() < wire->bytes )
    {
        i_max = Wire.available();
    }
    else
    {
        i_max = wire->bytes;
    }

    for(i=0; i<i_max; i++)
    {
        wire->data[i] = Wire.read();
        if( wire->dbg_print )
        {
            Serial.print("0x" ); Serial.print(wire->data[i], HEX);
            Serial.print(" 0d"); Serial.print(wire->data[i], DEC);
            Serial.print(" 0b"); Serial.print(wire->data[i], BIN);
            Serial.print("\r\n");
        }
    }

    if( wire->dbg_print )
    {
        Serial.print("!!! cmd_wire_get tail !!!\r\n");
    }

    return received_num;
}

int wire_set(wire_t *wire)
{
    int i;
    
    if( NULL == wire ){ return ERR_NULL; }

    if( wire->dbg_print )
    {
        Serial.print("!!! cmd_wire_set head !!!\r\n");
        Serial.print(    "Slave Addr : 0x"); Serial.print(wire->slave_addr, HEX);
        Serial.print("\r\nReg   Addr : 0x"); Serial.print(wire->reg_addr,   HEX);
        Serial.print("\r\nBytes      : 0d"); Serial.print(wire->bytes,      DEC);
        for(i=0; i<wire->bytes; i++)
        {
            Serial.print("\r\nData       : 0x");
            Serial.print(wire->data[i], HEX);
        }
        Serial.print("\r\n");
    }

    Wire.beginTransmission(wire->slave_addr>>1);
    Wire.write(wire->reg_addr);
    for(i=0; i<wire->bytes; i++)
    {
        Wire.write(wire->data[i]);
    }
    Wire.endTransmission();

    if( wire->dbg_print )
    {
        Serial.print("!!! cmd_wire_set tail !!!\r\n");
    }

    return ERR_OK;
}


/***********************************************************************
  Function : Command Mode
 ***********************************************************************/
void cmd_print_help(void)
{
    Serial.print("Available Command:\r\n");
    Serial.print("help, ?    : print Help Messages\r\n");
    Serial.print("ver        : print Version Information\r\n");
    Serial.print("typesize   : print type's size\r\n");
    Serial.print("wireget    : get data from I2C device\r\n");
    Serial.print("wireset    : set data to   I2C device\r\n");
    Serial.print("pcf8563    : Real-time Clock PCF8563 function\r\n");
    Serial.print("  pcf8563 print - print clock value\r\n");
    Serial.print("  pcf8563 set   - set   clock value\r\n");
    Serial.print("quit, exit : Quit Command Control Mode\r\n");
}
void cmd_print_ver(void)
{
    Serial.print("This is ");
    Serial.print(__FILE__);
    Serial.print(" ");
    Serial.print("Build at ");
    Serial.print(__DATE__);
    Serial.print(" ");
    Serial.print(__TIME__);
    Serial.print("\r\n");
}

int cmd_getstr(char *buf)
{
    int i;

    i=0;
    while(1)
    {
        if(Serial.available())
        {
            buf[i] = Serial.read();
            Serial.print(buf[i]); //echo-back

            if ( (buf[i] == 0x08) or (buf[i] == 0x7f) ) //BackSpace, Delete
            {
                buf[i] = '\0';
                if(i) i--;
            }
            else if( (buf[i] == '\r') or (buf[i] == '\n') )
            {
                for(i; i<CMD_BUF_LENGTH; i++) buf[i] = '\0';
                return CMD_OK;
            }
            else
            {
                i++;
                if(i>=CMD_BUF_LENGTH)
                {
                    Serial.print("### CMD BUFFER FULL, CLEAR. ###\r\n");
                    for(i=0; i<CMD_BUF_LENGTH; i++) buf[i] = '\0';
                    i=0;
                    return ERR_INVALID;
                }
            }
        }
    }// while
}
int cmd_getnum(int type)
{
    int  i;
    int  return_val;
    char buf[CMD_BUF_LENGTH];

    if( DEC == type || HEX == type )
    {
        return_val = cmd_getstr(&buf[0]);
        if( CMD_OK != return_val )
        {
            return return_val;
        }

        if( DEC == type ){ sscanf(buf, "%d", &i); }
        if( HEX == type ){ sscanf(buf, "%x", &i); }
        return i;
    }
    else
    {
        return ERR_INVALID;
    }
}

int cmd_wire_get(void)
{
    byte i;
    wire_t *wire;
    int send_stop;

    wire = (wire_t*)malloc(sizeof(wire_t));
    if( NULL == wire )
    {
        return ERR_MALLOC;
    }
    
    Serial.print(    "Slave Addr         : 0x"); wire->slave_addr = cmd_getnum(HEX);
    Serial.print("\r\nReg   Addr         : 0x"); wire->reg_addr   = cmd_getnum(HEX);
    Serial.print("\r\nBytes              : 0d"); wire->bytes      = cmd_getnum(DEC);
    Serial.print("\r\nSend Stop (0 or 1) : "  ); send_stop        = cmd_getnum(DEC);
    if(send_stop)
    {
        wire->send_stop = true;
    }
    else
    {
        wire->send_stop = false;
    }
    Serial.print("\r\n");

    wire->data = (byte*)malloc(wire->bytes);
    if( NULL == wire->data )
    {
        Serial.print("ERR_MALLOC\r\n");
        free(wire);
        return ERR_MALLOC;
    }

    wire->dbg_print = false;

    wire_get(wire);
    for(i=0; i<wire->bytes; i++)
    {
        Serial.print("0x" ); Serial.print(wire->data[i], HEX);
        Serial.print(" 0d"); Serial.print(wire->data[i], DEC);
        Serial.print(" 0b"); Serial.print(wire->data[i], BIN);
        Serial.print("\r\n");
    }
    
    free(wire->data);
    free(wire);

    return ERR_OK;
}
int cmd_wire_set(void)
{
    byte i;
    wire_t *wire;

    wire = (wire_t*)malloc(sizeof(wire_t));
    if( NULL == wire )
    {
        return ERR_MALLOC;
    }

    Serial.print(    "Slave Addr : 0x"); wire->slave_addr = cmd_getnum(HEX);
    Serial.print("\r\nReg   Addr : 0x"); wire->reg_addr   = cmd_getnum(HEX);
    Serial.print("\r\nBytes      : 0d"); wire->bytes      = cmd_getnum(DEC);
    Serial.print("\r\n");

    wire->data = (byte*)malloc(wire->bytes);
    if( NULL == wire->data )
    {
        Serial.print("ERR_MALLOC\r\n");
        free(wire);
        return ERR_MALLOC;
    }
    for(i=0; i< wire->bytes; i++)
    {
        Serial.print("Data       : 0x");
        wire->data[i] = cmd_getnum(HEX);
        Serial.print("\r\n");
    }
    
    wire->dbg_print = false;

    wire_set(wire);

    free(wire->data);
    free(wire);

    return ERR_OK;
}

int cmd_execute(char *buf)
{
    int i, x, y;
    unsigned int ux;
    int return_val = CMD_OK;
    char cmd[CMD_MAX_LENGTH];
    char arg1[ARG_MAX_LENGTH];
    char arg2[ARG_MAX_LENGTH];

    strcpy(cmd, "");
    strcpy(arg1, "");
    strcpy(arg2, "");
    sscanf(buf, "%s %s %s", &cmd, &arg1, &arg2);

    if     (strcmp(cmd, "help")==0){ cmd_print_help(); }
    else if(strcmp(cmd, "?"   )==0){ cmd_print_help(); }
    else if(strcmp(cmd, "ver" )==0){ cmd_print_ver();  }

    else if((strcmp(cmd, "quit")==0) or (strcmp(cmd, "exit")==0))
    {
        return CMD_QUIT;
    }

    else if(strcmp(cmd, "wireget")==0)
    {
        return_val = cmd_wire_get();
    }
    else if(strcmp(cmd, "wireset")==0)
    {
        return_val = cmd_wire_set();
    }
    else if(strcmp(cmd, "pcf8563")==0)
    {
        if(strcmp(arg1, "print")==0)
        {
            int i;
            pcf8563_t *pcf8563;
            pcf8563 = pcf8563_open();
            if( NULL == pcf8563 ){ return ERR_NULL; }

            for(i=0; i<10; i++)
            {
                pcf8563_get_time(pcf8563);
                pcf8563_print(pcf8563);
                delay(1000);
            }

            pcf8563_close(pcf8563);
        }
        else if(strcmp(arg1, "set")==0)
        {
            pcf8563_t *pcf8563;
            pcf8563 = pcf8563_open();
            if( NULL == pcf8563 ){ return ERR_NULL; }
    
            Serial.print(    "YY : "); pcf8563->years   = cmd_getnum(DEC);
            Serial.print("\r\nMM : "); pcf8563->months  = cmd_getnum(DEC);
            Serial.print("\r\nDD : "); pcf8563->days    = cmd_getnum(DEC);
            Serial.print("\r\nhh : "); pcf8563->hours   = cmd_getnum(DEC);
            Serial.print("\r\nmm : "); pcf8563->minutes = cmd_getnum(DEC);
            Serial.print("\r\nss : "); pcf8563->seconds = cmd_getnum(DEC);
            Serial.print("\r\n");
            pcf8563_set_time(pcf8563);
    
            pcf8563_close(pcf8563);
        }
        else
        {
            return ERR_INVALID;
        }
    }

    else if(strcmp(cmd, "typesize")==0)
    {
        Serial.print(    "char      : "); Serial.print(sizeof(char));
        Serial.print("\r\nshort     : "); Serial.print(sizeof(short));
        Serial.print("\r\nint       : "); Serial.print(sizeof(int));
        Serial.print("\r\nlong      : "); Serial.print(sizeof(long));
        Serial.print("\r\nlong long : "); Serial.print(sizeof(long long));
        Serial.print("\r\nfloat     : "); Serial.print(sizeof(float));
        Serial.print("\r\ndouble    : "); Serial.print(sizeof(double));
        Serial.print("\r\n");
    }

    else
    {
        return ERR_INVALID;
    }

    return return_val;

}
void cmd_rx_data(void)
{
    int i;
    int return_val = CMD_OK;
    char buf[CMD_BUF_LENGTH];

    /* モード切替時の "Hit any key" のキー操作を捨てる */
    while(Serial.available()){ Serial.read(); }

    Serial.print("\r\n### Command Mode. ###\r\n");
    Serial.print("### Hit ? to help.###\r\n");
    Serial.print("$");

    i=0;
    while(1)
    {
        if(Serial.available())
        {
            buf[i] = Serial.read();
            Serial.print(buf[i]); //echo-back

            if ( (buf[i] == 0x08) or (buf[i] == 0x7f) ) //BackSpace, Delete
            {
                buf[i] = '\0';
                if(i) i--;
            }
            else if( (buf[i] == '\r') or (buf[i] == '\n') )
            {
                Serial.print( F("\r\n") );
                buf[i] = '\0';
                return_val = cmd_execute(&buf[0]);
                for(i=0; i<CMD_BUF_LENGTH; i++) buf[i] = '\0';
                i=0;

                if(return_val == CMD_QUIT)
                {
                    Serial.print("### Quit Command Mode. ###\r\n");
                    return;
                }
                else if(return_val == ERR_INVALID)
                {
                    Serial.print("?\r\n");
                    Serial.print("$");
                }
                else
                {
                    Serial.print("OK\r\n$");
                }
            }
            else
            {
                i++;
                if(i>=CMD_BUF_LENGTH)
                {
                    Serial.print("### CMD BUFFER FULL, CLEAR. ###\r\n");
                    for(i=0; i<CMD_BUF_LENGTH; i++) buf[i] = '\0';
                    i=0;
                }
            }
        }
    }// while
}
```