【実験】PaPeRo i用RTCを自作する

RTC(リアルタイムクロック)モジュールとArduino Nano互換機でUSB接続のRTCを自作してPaPeRo iで使えるようにしてみました。
RTCから読み出した時刻をシステム時刻に反映させるスクリプトと組み合わせれば、ネットワーク接続なしのPaPeRo iで電源OFF-ONしても時刻を保持できる様になります。

RTCモジュール

RTCモジュールはamazonなどで安価に購入できる、DS3231が載っているモジュールを使いました。
このモジュールの注意点は回路が充電池用になっていて、普通のCR2032を使うには改造が必要なことです。
ジャンパカットか抵抗またはダイオードを外す必要があります。今回は抵抗を外しました。

Arduino Nano

RTCの制御とUSBインタフェースとして、これもamazonなどで安価に購入できるArduino Nano互換機を使いました。
PaPeRo iでこれを認識するためには別途カーネルモジュールをロードする必要があります。

RTCモジュールとArduinoの接続

今回は簡単にブレッドボードに組みました。結線は4本で、写真の通りです。

Arduinoのプログラム

追加ライブラリーとして、Arduino DS3232RTC Library v1.0Arduino Time Libraryを使用しています。

// Serial RTC
// (c) 2017 Sophia Planning Inc.
// License: MIT
//#include <Time.h>
//#include <TimeLib.h>
//#include <Wire.h>
#include <DS3232RTC.h>

#define BPS 115200
#define YEAR_OFS 1970  // DS3231 years offset

char buf[20];
int idx;

void setup() {
  Serial.begin(BPS);
  idx = 0;
}

// make date char array
void make_date_chars(tmElements_t *tm, char *buf, int len)
{
  sprintf(buf, "%02d.%02d.%02d-%02d:%02d:%02d",
          tm->Year + YEAR_OFS, tm->Month,tm->Day,
          tm->Hour, tm->Minute, tm->Second);
  return;
}

// get dec value from sub string in buf
int get_dec(const char *buf, int start, int width)
{
  int j;
  char tmp[5], *wp;
  const char *rp;
  if (5 <= width) {
    return -1;
  }
  wp = &tmp[0];
  rp = &buf[start];
  for (j = 0; j < width; j++)
    *wp++ = *rp++;
  *wp = 0;
  return atof(tmp);
}

// the loop routine runs over and over again forever:
void loop() {
  int j, c;
  tmElements_t tm;

  if (!Serial.available()) {
    return;
  }
  c = Serial.read();
  if ('\r' == c) {
    // receive line
    buf[idx] = 0;
    c = buf[0];
    if ('?' == c) {
      Serial.println("RTC DS3232RTC");
    } else if ('v' == c) {
      // version string
      Serial.println("0.0.0");
    } else if ('r' == c) {
      // read RTC and send to serial
      RTC.read(tm);
      //print_tm(&tm);
      make_date_chars(&tm, buf, sizeof(buf));
      Serial.println(buf);
    } else if ('w' == c) {
      // write RTC by received date
      if (14 <= idx) {
        tm.Year = get_dec(buf, 1+0, 4) - YEAR_OFS;
        tm.Month = get_dec(buf, 1+4, 2);
        tm.Day = get_dec(buf, 1+6, 2);
        tm.Hour = get_dec(buf, 1+8, 2);
        tm.Minute = get_dec(buf, 1+10, 2);
        tm.Second = get_dec(buf, 1+12, 2);
        RTC.write(tm);
        Serial.println("ok");
        RTC.read(tm);
        //print_tm(&tm);
        make_date_chars(&tm, buf, sizeof(buf));
        Serial.println(buf);
      }
    } else if ('t' == c) {
      // return temperature (*4) value
      int t = RTC.temperature();
      sprintf(buf, "%d", t);
      Serial.println(buf);
    } else if ('f' == c) {
      // stop wave output
      if ('1' == buf[1]) {
        RTC.squareWave(SQWAVE_1_HZ);
        Serial.println("sqwave 1hz");
      } else {
        RTC.squareWave(SQWAVE_NONE);
        Serial.println("sqwave stop");
      }
    }
    idx = 0;
  } else {
    if (idx < (sizeof(buf)-1)) {
      // buffering
      buf[idx] = c;
      idx++;
    } else {
      // overflow
    }
  }
}

シリアル通信で”r\n”を送るとRTCの時刻を返します。
“wYYYYMMDDhhmmss\n”を送るとRTCの時刻を設定します。
他にバージョンや温度も読み出せるようにしてあります。

動作確認

(1) PaPeRo iのUSBポートに接続します。
(2) カーネルモジュールをロードします。

# insmod ch341.ko

(3) /dev/ttyUSB0が存在することを確認します。
(4) picocomで接続してみます。
“r” + ENTER入力でRTCの時刻が表示されます(入力は表示されません)。

root@aterm:~# picocom -b 115200 /dev/ttyUSB0
picocom v1.7

port is        : /dev/ttyUSB0
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv
imap is        :
omap is        :
emap is        : crcrlf,delbs,

Terminal ready
2017.11.16-18:34:35
2017.11.16-18:34:38

(5) picocomから抜けるにはCTRL-a CTRL-qを入力します。

RTCの時刻をパペロのシステム時刻に反映させるスクリプト

RTCから読み出した文字列をdateコマンドにそのまま渡してやれば、PaPeRo iのシステム時刻に反映されます。
これをシェルスクリプトで行うにはpicocomとemptyが必要です。

#!/bin/sh
cd /tmp
empty -f -i ififo -o ofifo -p pidfile -L logfile \
     picocom -b 115200 --imap igncr /dev/ttyUSB0 > /dev/null 2>&1 &
sleep 4
empty -s -o ififo "r\r\n"
sleep 1
date `tail -1 logfile | cut -b4-`
empty -k `cat pidfile`

pythonであればpyserialパッケージを使えば同じ事が可能です。

import serial
import subprocess
import time

ser = serial.Serial('/dev/ttyUSB0', 115200)
time.sleep(4)
ser.write("r\r\n".encode())
time.sleep(1)
rd = ser.readline().decode()[0:-2]
subprocess.call(["date", rd])
ser.close()

内蔵できる様にする

aitendoのラズパイサイズユニバーサル基板に組み付けて見ました。
Nanoのmini Bメスをmicro Bメスに変換する変換コネクタを挟むことでPaPeRo iのお腹の中のUSBケーブルと接続できる様になりますし、基板の穴位置はラズパイと同じになっているので、このRTCを内蔵できる様になりました。


0