SPRESENSE の RTC をGPS 時計に設定して時報アプリを作ってみた! [SPRESENSE]
SPRESENSEはGPSを受信できるので、正確な時間を取得できます。それならその時間をRTCに設定してみて、時報アプリを作れないかなと思い作ってみました。
時報の音源は次のURLの「Sine波 予報音3回」のMP3ファイルを拝借させていただきました。
時報音の音源
https://ultra.zone/jihou.ja
スケッチは、メインループと "pthread" で構成されています。メインループはGPS時間を取得をしてRTCを更新し、"pthread" は RTCの時間を監視して1分毎に時報を鳴らすというものです。
もともとは、task_create でスレッドを生成していたのですが、なぜかPANICが起きるので pthread に置きかえました。^^;
わざわざスレッドにしなくてもいいんじゃないかというコードではありますが、スレッドにすることでRTC の効用を少し分かりやすくしてみたつもりです。^^;
SPRESENSEはGPS時刻を取得できるので、複数のカメラ付きSPRESENSEを時刻同期で撮影すれば全球カメラができそうですね。ヘルメットにつけたりしたら面白いかも。
(^^)
時報の音源は次のURLの「Sine波 予報音3回」のMP3ファイルを拝借させていただきました。
時報音の音源
https://ultra.zone/jihou.ja
スケッチは、メインループと "pthread" で構成されています。メインループはGPS時間を取得をしてRTCを更新し、"pthread" は RTCの時間を監視して1分毎に時報を鳴らすというものです。
もともとは、task_create でスレッドを生成していたのですが、なぜかPANICが起きるので pthread に置きかえました。^^;
#include <GNSS.h> #include <RTC.h> #include <SDHCI.h> #include <Audio.h> #include <pthread.h> static SDClass theSD; static SpGnss Gnss; AudioClass *theAudio = AudioClass::getInstance(); #define JST_IN_SECONDS (9 * 60 * 60); static File myFile; static bool bTimeFixed = false; static void time_signal_thread(void *arg) { while(true) { usleep(100000); if (bTimeFixed == false) continue; RtcTime now = RTC.getTime(); if (now.second() != 56) continue; myFile = theSD.open("jihou.mp3"); if (!myFile) { Serial.println("File open error"); while(1); } theAudio->writeFrames(AudioClass::Player0, myFile); theAudio->setVolume(-60); theAudio->startPlayer(AudioClass::Player0); while (true) { sleep(1); // To ensure not re-entering the audio routine. int err = theAudio->writeFrames(AudioClass::Player0, myFile); if (err == AUDIOLIB_ECODE_FILEEND) { theAudio->stopPlayer(AudioClass::Player0); myFile.close(); break; } } } return 0; } void setup() { int result; Serial.begin(115200); theSD.begin(); sleep(3); /* Wait HW initialization done. */ theAudio->begin(); theAudio->setRenderingClockMode(AS_CLKMODE_NORMAL); theAudio->setPlayerMode(AS_SETPLAYER_OUTPUTDEVICE_SPHP, AS_SP_DRV_MODE_LINEOUT); theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_MP3, "/mnt/sd0/BIN" , AS_SAMPLINGRATE_AUTO, AS_CHANNEL_MONO); Serial.println("Audio initialized"); Gnss.begin(); Gnss.select(GPS); Gnss.start(COLD_START); Serial.println("Gnss start..."); int policy; struct sched_param param; pthread_t m_pid = pthread_self(); pthread_getschedparam(m_pid, &policy, &param); Serial.println("Main Priority is : " + String(param.sched_priority)); pthread_t t_pid; pthread_create(&t_pid, NULL, time_signal_thread, NULL); param.sched_priority = 130; pthread_setschedparam(t_pid, policy, &param); Serial.println("time_signal_thread start: " + String(param.sched_priority)); } void loop() { if (!Gnss.waitUpdate(-1)) return; SpNavData NavData; Gnss.getNavData(&NavData); Serial.println("numSat: " + String(NavData.numSatellites)); if (NavData.time.year == 1980) return; bTimeFixed = true; /* Create RTC time */ RtcTime gtime(NavData.time.year, NavData.time.month, NavData.time.day , NavData.time.hour, NavData.time.minute, NavData.time.sec , NavData.time.usec * 1000 /* RtcTime requires nsec */); gtime += JST_IN_SECONDS; /* convert UTC to JST */ printf("%04d/%02d/%02d %02d:%02d:%02d\n" , gtime.year(), gtime.month(), gtime.day() , gtime.hour(), gtime.minute(), gtime.second()); /* When time is different more than 1 sec, update RTC time */ if (abs(RTC.getTime() - gtime) >= 1) { RTC.setTime(gtime); Serial.println(" * Updated RTC time * "); } }
わざわざスレッドにしなくてもいいんじゃないかというコードではありますが、スレッドにすることでRTC の効用を少し分かりやすくしてみたつもりです。^^;
SPRESENSEはGPS時刻を取得できるので、複数のカメラ付きSPRESENSEを時刻同期で撮影すれば全球カメラができそうですね。ヘルメットにつけたりしたら面白いかも。
(^^)
SPRESENSE のNMEA 出力をNMEAモニターで確認してみた! [徒然日記]
SPRESENSEのNMEA出力が Arduino のスケッチできるようになったので、その出力を解析できるツールを探してみました。せっかくなら”みちびき”も見れたほうがいいなと思って探し出したのがこちら。
NMEA Monitor for windows
http://4river.a.la9.jp/gps/indexj.htm#001
次の記事のスケッチをSPRESENSEのメインボードに書き込み、上記アプリでコンソールを指定すれば、リアルタイムで衛星信号の受信状態を見ることができます。
SPRESENSEでArduinoのスケッチでNMEAをシリアル出力してみた!
https://makers-with-myson.blog.ss-blog.jp/2019-11-10
すごく簡単です。実際に受信している状態の画面です。
一番右の「QZGSV」が”みちびき”の信号ですね。このアプリで静止状態での精度を見ることができます。
まぁ、部屋の中から窓に向けて受信したので、精度はイマイチですねぇ。でも、これでSPRESENSEの衛星受信状態を確認できるようになりました。
GPSの受信状態は刻一刻と変わっていくので、見ているだけでも楽しいですね。
(^^)
NMEA Monitor for windows
http://4river.a.la9.jp/gps/indexj.htm#001
次の記事のスケッチをSPRESENSEのメインボードに書き込み、上記アプリでコンソールを指定すれば、リアルタイムで衛星信号の受信状態を見ることができます。
SPRESENSEでArduinoのスケッチでNMEAをシリアル出力してみた!
https://makers-with-myson.blog.ss-blog.jp/2019-11-10
すごく簡単です。実際に受信している状態の画面です。
一番右の「QZGSV」が”みちびき”の信号ですね。このアプリで静止状態での精度を見ることができます。
まぁ、部屋の中から窓に向けて受信したので、精度はイマイチですねぇ。でも、これでSPRESENSEの衛星受信状態を確認できるようになりました。
GPSの受信状態は刻一刻と変わっていくので、見ているだけでも楽しいですね。
(^^)
SPRESENSEでArduinoのスケッチでNMEAをシリアル出力してみた! [SPRESENSE]
SPRESENSEには、実はあまり使ってないのですけどGPSの機能があったります。しかも「みちびき」対応です。最近、Twitterで”みちびき”の災害危険情報を皆さんがデコードをするを見て、私も流行りに乗ってGPSを使ってみることにしました。
話は変わりますが、みちびき打上げ用ロゴって、エバンゲリオンの制作スタッフが作ったものなんですね。ブログに貼り付けたいなと思ったのですが、使用についてうるさいことが書いてあったのでやめておきました。
SPRESENSE の Arduino用のライブラリは、便利なAPIは用意しているのですが、GPSの解析アプリ等と連携しようとするとNMEA出力のほうが便利そうです。
そこで、SPRESENSEのGPSの出力を、Arduino を使ってNMEAで出力する方法を探してみました。参考にしたのは、Spresense公式サポートページと、SDKサンプルアプリの「gnss_atcmd」と、Arduino 用のGNSSライブラリの実装です。
いろいろ試行錯誤した結果、以下のスケッチでNMEAをコンソールに出力することができました!最初の情報取得でこけているのは謎ですが、出力ログもマスクを変更することで変更可能です。
出力はこんな感じです。詳しい緯度・経度は秘密です!( ー`дー´)
NMEAの出力の詳細は、次のページで確認できます。
GPSのNMEAフォーマット
https://www.hiramine.com/physicalcomputing/general/gps_nmeaformat.html
”みちびき”の情報はどうしたらええねん?と思っていたら、すでに”みちびき”の災危情報をデコードするためのライブラリを作られている猛者がいました。もちろんSPRESENSE用です。
みちびき災危通報をデコードしてみる
https://qiita.com/baggio/items/497b654cd4ec1bfd74fc
ただ私のコンソール出力に、$QZQSVって、みちびきっぽい出力は出ているけど、でもこれ災危情報の出力じゃないような。$QZQSMって出るときと出ないときがあるのか、そもそも設定出きていないのか?…悩む…。GPSの世界はまだまだ奥が深く難しいなぁ。
σ( ̄ε ̄ )
話は変わりますが、みちびき打上げ用ロゴって、エバンゲリオンの制作スタッフが作ったものなんですね。ブログに貼り付けたいなと思ったのですが、使用についてうるさいことが書いてあったのでやめておきました。
SPRESENSE の Arduino用のライブラリは、便利なAPIは用意しているのですが、GPSの解析アプリ等と連携しようとするとNMEA出力のほうが便利そうです。
そこで、SPRESENSEのGPSの出力を、Arduino を使ってNMEAで出力する方法を探してみました。参考にしたのは、Spresense公式サポートページと、SDKサンプルアプリの「gnss_atcmd」と、Arduino 用のGNSSライブラリの実装です。
いろいろ試行錯誤した結果、以下のスケッチでNMEAをコンソールに出力することができました!最初の情報取得でこけているのは謎ですが、出力ログもマスクを変更することで変更可能です。
#include <GNSS.h> #include <GNSSPositionData.h> #include <gpsutils/cxd56_gnss_nmea.h> static SpGnss Gnss; static GnssPositionData posdat; static char nmea_buf[NMEA_SENTENCE_MAX_LEN]; /* output NMEA */ FAR static char *reqbuf(uint16_t size) { return size > sizeof(nmea_buf) ? NULL : nmea_buf; } static void freebuf(FAR char *buf) { // do nothing... } static int outnmea(FAR char *buf){ return Serial.println(buf); } static int outbin(FAR char *buf, uint32_t len) { return len; // do nothing... } void setup() { Serial.begin(115200); Gnss.begin(); Gnss.select(GPS); Gnss.select(QZ_L1CA); Gnss.select(QZ_L1S); /** * Set NMEA sentence 32bit mask * - Default value 0x000000ef. * |bit |sentence| * |:----:|:---:| * |bit0|GGA| * |bit1|GLL| * |bit2|GSA| * |bit3|GSV| * |bit4|GNS| * |bit5|RMC| * |bit6|VTG| * |bit7|ZDA| * |bit14|QZQSM| */ uint32_t mask = 0x000040ff; // set the full mask NMEA_OUTPUT_CB funcs; NMEA_InitMask(); NMEA_SetMask(mask); funcs.bufReq = reqbuf; funcs.out = outnmea; funcs.outBin = outbin; funcs.bufFree = freebuf; NMEA_RegistOutputFunc(&funcs); Serial.println("NMEA initialized"); int ret = Gnss.start(COLD_START); if (ret != 0) { Serial.println("Gnss start error!"); while(1); } } void loop() { memset(&posdat, 0, sizeof(posdat)); Gnss.getPositionData((char*)&posdat); NMEA_Output((struct cxd56_gnss_positiondata_s*)&(posdat.Data)); sleep(1); }
出力はこんな感じです。詳しい緯度・経度は秘密です!( ー`дー´)
$GPGGA,143132.00,35xx.xxxx,N,139xx.xxxx,E,1,09,1.1,70.6,M,39.1,M,,*53 $GNGLL,35xx.xxxx,N,139xx.xxxx,E,143132.00,A,A*7B $GNGSA,A,3,08,09,16,21,23,26,27,31,194,,,,1.8,1.1,1.5*1A $GPGSV,3,1,11,04,15,305,,07,01,318,,08,34,239,31,09,18,293,25*7B $GPGSV,3,2,11,16,62,015,26,21,32,058,26,23,25,263,28,26,51,067,28*70 $GPGSV,3,3,11,27,70,260,32,31,26,145,32,194,88,077,30,,,,*76 $QZGSV,1,1,01,02,88,077,30,,,,,,,,,,,,*55 $GNGNS,143132.00,35xx.xxxx,N,139xx.xxxx,E,AN,09,1.1,70.6,M,39.1,M,,*68 $GNRMC,143132.00,A,35xx.xxxx,N,139xx.xxxx,E,0.0,14.4,101119,,,A*74 $GNVTG,14.4,T,,M,0.0,N,0.0,K,A*22 $GNZDA,143132.00,10,11,2019,,*75
NMEAの出力の詳細は、次のページで確認できます。
GPSのNMEAフォーマット
https://www.hiramine.com/physicalcomputing/general/gps_nmeaformat.html
”みちびき”の情報はどうしたらええねん?と思っていたら、すでに”みちびき”の災危情報をデコードするためのライブラリを作られている猛者がいました。もちろんSPRESENSE用です。
みちびき災危通報をデコードしてみる
https://qiita.com/baggio/items/497b654cd4ec1bfd74fc
ただ私のコンソール出力に、$QZQSVって、みちびきっぽい出力は出ているけど、でもこれ災危情報の出力じゃないような。$QZQSMって出るときと出ないときがあるのか、そもそも設定出きていないのか?…悩む…。GPSの世界はまだまだ奥が深く難しいなぁ。
σ( ̄ε ̄ )
SPRESENSE で pthread でLEDを制御してみた! [SPRESENSE]
NuttX の "task_create"、"マルチコア"と来たら、次はやはり本家 POSIX のスレッド "pthread" でしょう。ということで、今回は "pthread" で LED を制御してみました。
相変わらず画像は使いまわしで失礼します…^^;
pthread はご存じの方が多いと思いますが、Arduino IDE から使う人はあまりいないのではないでしょうか?ということで、今回もスケッチでご紹介します!
pthread そのものの使い方は知っている人も多いと思うので解説は省きます。Arduino IDE からも使えますよ~ということを言いたかっただけなので。
(^^)/~
相変わらず画像は使いまわしで失礼します…^^;
pthread はご存じの方が多いと思いますが、Arduino IDE から使う人はあまりいないのではないでしょうか?ということで、今回もスケッチでご紹介します!
#include <pthread.h> #define MAXLOOP 60 static int gMainLoop = 0; struct led_settings { int durationH; int durationL; }; inline void led_process(int led_num, int durationH, int durationL, String funcname) { int pin_num = 0; switch (pin_num) { case 0: pin_num = LED0; break; case 1: pin_num = LED1; break; case 2: pin_num = LED2; break; case 3: pin_num = LED3; break; } Serial.println("========================================"); Serial.println("Task name : " + funcname); Serial.println("LED" + String(led_num) + " blink interval"); Serial.println("High duration: " + String(durationH) + " msec"); Serial.println("Low duration: " + String(durationL) + " msec"); Serial.println("========================================\n"); while (gMainLoop < MAXLOOP) { digitalWrite(pin_num, HIGH); usleep(durationH * 1000); // msec to usec digitalWrite(pin_num, LOW); usleep(durationL * 1000); // msec to usec /* You cannot use "delay(msec)" because it is * * a busywait that will not yield the process */ } Serial.println("Exit: " + funcname); return; } // pthread static void led0_thread(void *arg) { struct led_settings *param = (struct led_settings*)arg; led_process(0, param->durationH, param->durationL, __FUNCTION__); return; } static void led1_thread(void *arg) { struct led_settings *param = (struct led_settings*)arg; led_process(1, param->durationH, param->durationL, __FUNCTION__); return; } static void led2_thread(void *arg) { struct led_settings *param = (struct led_settings*)arg; led_process(2, param->durationH, param->durationL, __FUNCTION__); return; } static void led3_thread(void *arg) { struct led_settings *param = (struct led_settings*)arg; led_process(3, param->durationH, param->durationL, __FUNCTION__); return; } void setup() { Serial.begin(115200); pinMode(LED0, OUTPUT); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pthread_t led0, led1, led2, led3; struct led_settings param0, param1, param2, param3; param0.durationH = 100; param0.durationL = 200; pthread_create(&led0, NULL, led0_thread, (void*)&param0); param1.durationH = 200; param1.durationL = 200; pthread_create(&led1, NULL, led1_thread, (void*)&param1); param2.durationH = 500; param2.durationL = 100; pthread_create(&led2, NULL, led2_thread, (void*)&param2); param3.durationH = 100; param3.durationL = 500; pthread_create(&led3, NULL, led3_thread, (void*)&param3); } void loop() { ++gMainLoop; Serial.println("MainLoop: " + String(gMainLoop)); sleep(1); if (gMainLoop >= MAXLOOP) { sleep(1); // wait for the end of all threads Serial.println("End of all threads"); while (1); } }
pthread そのものの使い方は知っている人も多いと思うので解説は省きます。Arduino IDE からも使えますよ~ということを言いたかっただけなので。
(^^)/~