結局 Wio-LTE には時計が二つ入っている。一つは EC21-J のモデムに、もう一つは STM32F4 の SoC 上にある。基本的には電源が切れると、両方とも時刻を忘れる。
時刻の同期元となるものは、携帯網自体か、あるいはNTPによるものか、GPSによるものか、と種類がある。
SoC の時計から時刻を取得する
これは単純に Date()
で取得できる。
>console.log(new Date().toString());
Tue Apr 3 2018 02:10:16 GMT+0000
=undefined
>console.log(getTime()); // get in unix epoch seconds
1522721416.42934918403
=undefined
時刻合わせをするときには setTime()
で行う。CPU の時刻を使うメリットは、モデムとの通信を待たないでいいことで、他の通信を妨げないのが最大のメリット。モデムの時計をコピーして時刻をセットしておくと便利だろう。あるいはあえて時刻をセットせず、カレンダーとしてではなく、単純に起動時からの経過時間として使うという手もある。
>setTime(1522721463)
=undefined
>console.log(new Date().toString());
Tue Apr 3 2018 02:11:06 GMT+0000
=undefined
E.setTimeZone()
で CPU のタイムゾーンを設定すると new Date().toString()
の出力を現地時刻に変更できる。
>E.setTimeZone(9)
=undefined
>console.log(new Date().toString());
Tue Apr 3 2018 11:13:39 GMT+0900
=undefined
モデムの時計から時刻を取得する
EC21-J は LTE モデムなので、ネットワークに接続できると、携帯網の機能を使って自動的に時刻が設定される。
モデムの時計からは AT+CCLK コマンドで取得できる。
function clockNow(board){
var pad2 = function(n){ return ("00" + n).slice(-2); };
var normalize_datetime = function(s){
return s.replace("/","-").replace("/","-").replace(",","T");
};
return new Promise((resolve,reject)=>{
board.at.cmd("AT+CCLK?\r\n", 1000, L=>{
if(L===undefined){
reject("timeout");
}else if(parseInt(L[8]) >= 7){ // 1970
reject("uninitialized");
}else{
var dt = normalize_datetime("20" + L.slice(8, 25));
var offset = parseInt(L.slice(26, 28));
dt += L[25] + pad2(offset/4) + pad2((offset%4) * 15);
resolve(new Date(dt));
}
});
});
}
// ここから下はボードの起動手順。後の例では省略。
var board;
function onConnect(err){
if(err){
board = require("wiolte").connect(onConnect);
}else{
clockNow(board).then(T=>{
console.log("clockNow", T);
});
}
}
function onInit(){
board = require("wiolte").connect(onConnect);
}
onInit();
携帯網の時刻を取得する
モデムの時計からではなく、網から明示的に取得する方法。
function networkNow(board, utc){
var pad2 = function(n){ return ("00" + n).slice(-2); }
var normalize_datetime = function(s){
return s.replace("/","-").replace("/","-").replace(",","T");
}
var cmd = "AT+QLTS=2\r\n";
if(utc){
cmd = "AT+QLTS=1\r\n";
}
return new Promise((resolve,reject)=>{
board.at.cmd(cmd, 1000, L=>{
if(L===undefined){
reject("timeout");
}else{
// +QLTS: "2018/04/02,17:31:55+36,0"
var dt = normalize_datetime(L.slice(8, 27));
// Espruino Date accepts 2018-04-02T17:31:55Z
if(utc){
resolve(new Date(dt + "Z"));
}else{
var offset = parseInt(L.slice(28, 30));
dt += L[27] + pad2(offset/4) + pad2((offset%4) * 15);
resolve(new Date(dt));
}
}
});
});
}
NTP で時刻を取得する
NTPで現在時刻を取得する。ネットワークパケットを使う。このコマンドはモデムの AT コマンドを使うのだが、取得と同時にモデムの時計をセットすることもできる。
function ntpNow(board, server){
var pad2 = function(n){ return ("00" + n).slice(-2); };
var normalize_datetime = function(s){
return s.replace("/","-").replace("/","-").replace(",","T");
};
return new Promise((resolve,reject)=>{
var handler = function(L){
if(L===undefined){
reject("QNTP timeout");
}else if(L=="OK"){
return handler;
}else if(L.slice(0,9)=="+QNTP: 0,"){
// +QNTP: 0,"2018/04/03,02:52:26"
var offset = parseInt(L.slice(30,32));
var dt = normalize_datetime(L.slice(10,29));
dt += L[29] + pad2(offset/4) + pad2((offset%4) * 15);
// Espruino Date accepts 2018-04-02T17:31:55Z
resolve(new Date(dt));
}else{
reject(L);
}
};
board.at.cmd("AT+QNTP=1,\""+server+"\"\r\n", 10000, handler);
});
}
GPS
実は Wio-LTE の JP バージョンでのみ GPS が削除されているということに、今気が付きました。日本版だけ劣化してる……。それならそれで soracom のキットを最初から買えばよかった。残念。