ESP-WROOM-32でやってみました。
調査結果により書き直したので、記事タイトルと内容に
ズレがあります。
調べてわかったこと
起動時のNTP時刻取得後は、1時間毎のgetLocalTime()時に
NTP時刻同期していました。
よって、提供される時刻の精度は問題ないと思われます。
つまり、NTP又はネットワーク内タイムサーバに接続できる
環境なら、RTCモジュール追加の必要はない?
かもしれません。タイムサーバ接続不可能な環境で使うなら
RTCは有ったほうが良いと思います。
getLocalTime()
ローカル時刻を取得する。
失敗時10ms毎に取得繰返し5000mS(デフォルト)でタイムアウト
取得成功でtrue返す。失敗でfalse返す。
例 getLocalTime(&timeinfo, 5000)
getLocalTime()後のsntp_get_sync_statusをシリアルモニタ
表示してみた。
前半 10分間隔 後半 11分間隔でgetLocalTime()してます。
約60分毎に sntp_get_sync_status = COMPLETED とあります。
スケッチ例 「SimpleTime」で実験中
(ESP32 Dev Module用のスケッチ例 ESP32→TIME→SimpleTime)
コメントとテストを追記したスケッチ(手直し中)
WiFi.disconnect(true);とWiFi.mode(WIFI_OFF);の2行は
接続を継続するため無効にしました。
include行は < から < へ修正して下さい。#include <WiFi.h>
#include "time.h"
const char* ssid = "my-ssid";
const char* password = "my-password";
const char* ntpServer = "pool.ntp.org"; // NTPサーバ,IP Address指定できるか?
const long gmtOffset_sec = 9 * 3600; // 時差を秒で設定・・・9時間
const int daylightOffset_sec = 0; // サマータイム設定・・・0
void printLocalTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo, 5000)) {
// コメント追記 タイムアウトデフォルト5000mS
Serial.println("Failed to obtain time");
return;
}
//追記 NTP同期確認のため ・・・COMPLETEDで同期完了と思う
if (sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) {
Serial.println("sntp_get_sync_status = COMPLETED");
Serial.println(&timeinfo, "%A, %B %m %d %Y %H:%M:%S");
// コメント追記 A 曜日英語表記,B 月英語表記,m 月の数字,d 日,Y 年,H時:M分:S秒
}
}
void setup() {
Serial.begin(115200);
//connect to WiFi
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" CONNECTED");
//init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
printLocalTime();
//disconnect WiFi as it's no longer needed
//WiFi.disconnect(true); //無効にしました
//WiFi.mode(WIFI_OFF); // 無効にしました
}
void loop() {
//delay(1000); // 無効にしました
printLocalTime();
delay(660000); // 追記しました
}
(スケッチ例を公開している方々に感謝します。)
RTC時刻合わせするスケッチもよかったらどうぞ#include <WiFi.h>
#include "time.h"
#include <Wire.h>
#include <RTClib.h>
const char* ssid = "my-ssid";
const char* password = "my-password";
const char* ntpServer = "pool.ntp.org"; // pool.ntp.org
const long gmtOffset_sec = 9 * 3600; // 時差を秒で設定・・・9時間
const int daylightOffset_sec = 0; // サマータイム設定・・・0
unsigned long interval_t = 60000 * 61 * 1; // 60000(mS)*60*1=1h 60000(1min)
unsigned long previousMillis_t = 0;
unsigned long previous_t = 0;
char date_ymdhms[21]; // yyyy/mm/dd,hh:mm:ssn0
RTC_DS3231 rtc;//select use RTC DS3231
void printLocalTime() {
//sntp_get_sync_status()は時間同期のステータスを取得します。
//更新が完了すると、ステータスはSNTP_SYNC_STATUS_COMPLETEDとして返されます。
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return;
}
if (sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) {
Serial.println("sntp_get_sync_status = COMPLETED");
Serial.println(&timeinfo, "%A, %B %m %d %Y %H:%M:%S");
}
}
void setup() {
Serial.begin(115200);
Wire.begin();
//connect to WiFi
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" CONNECTED");
//init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
printLocalTime(); // NTP時刻取得、同期ステータス表示、システム時刻表示
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
}
// RTCが電源を失った時(電池切れ)getLocalTime()して取得成功の場合取得時刻をRTCにセットし、
// 失敗の場合コンパイル時刻をRTCにセットする。
if (rtc.lostPower()) {
Serial.println("RTC lost power, let's set the time!");
struct tm timeInfo;
if (!getLocalTime(&timeInfo)) {
/* 失敗でメッセージを表示しコンパイル時刻をRTCへセット 取得成功で取得時刻をRTCへセット */
Serial.println("Failed to obtain time Set to compile time"); // 時間の取得に失敗しましたコンパイル時刻をセットします
rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // コンパイル日時を仮設定
DateTime now = rtc.now();
sprintf(date_ymdhms, "%04d/%02d/%02d,%02d:%02d:%02d", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
Serial.println(date_ymdhms);
} else {
rtc.adjust(DateTime(timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec));
//tm_year:1900年からの年数 tm_mon:1月からの月数
Serial.println("adjust RTC !!");
DateTime now = rtc.now();
sprintf(date_ymdhms, "%04d/%02d/%02d,%02d:%02d:%02d", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
Serial.println(date_ymdhms);
}
}
//disconnect WiFi as it's no longer needed
//WiFi.disconnect(true);
//WiFi.mode(WIFI_OFF);
}
void loop() {
// 定期的にgetLocalTime()して同期できたらRTCをセットする
unsigned long currentMillis_t = millis();
if (currentMillis_t - previousMillis_t >= interval_t) { //interval_t 61分はOK60分はNG
previousMillis_t = currentMillis_t;
struct tm timeInfo;
if (getLocalTime(&timeInfo)) {
if (sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) {
rtc.adjust(DateTime(timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec));
//tm_year:1900年からの年数 tm_mon:1月からの月数
Serial.println("adjust RTC !!");
} // else { Serial.println("NOT COMPLET"); }
} else {
Serial.println("Failed to obtain time");
}
}
//delay(1000);
// 10分毎にRTC時刻を取得して表示する。
unsigned long interval = 60000 * 10 *1; // 60000(1min)
unsigned long current_t = millis();
if (current_t - previous_t >= interval) {
previous_t = current_t;
//printLocalTime(); // このスケッチオリジナルの関数
DateTime now = rtc.now();
sprintf(date_ymdhms, "%04d/%02d/%02d,%02d:%02d:%02d", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
Serial.println(date_ymdhms);
}
}
(スケッチ例を公開している方々に感謝します。)
無保証自己責任でよろしくおねがいします。
ESP32 WiFi SSID,passwordをSDメモリから設定する。
その他の機能
・SDから変数を代入することも可能です。
・セキュリティ対策として、
設定ファイルだけwebから見えなくしました。
参考記事 特定ファイルを開かせない・・・を見て下さい。
作例をWeb公開している方々に感謝します。
設定ファイルについて
例えば、SDメモリカードにsetupというフォルダを作り、
setup.txtというファイルを作って保存します。
(ファイル、フォルダ名は、スケッチと合わせて下さい。
また、必ずフォルダに入れる必要はありません。)
setup.txt例
##以降はコメントとして扱われます。
#SSのあとにSSID、paのあとにpasswordを入力してください
#SS your-SSID #SSID
#pa your-password #password
#
SS ********** #SSID
pa ********** #password
#
#gw 192,168, 1, 1 #gateway
#sn 255,255,255, 0 #subnet
#dn 192,168, 1, 1 #dnS1
#ip 192,168, 1, 10 #ip address
#
gw 192,168, 1, 1 #gateway
sn 255,255,255, 0 #subnet
dn 192,168, 1, 1 #dnS1
ip 192,168, 1, 10 #ip address
#
# interval time 測定間隔
it 30000 #mSec 1000=1sec
スケッチ
スケッチ例SDWebServerをベースに、追加変更部分を下記に
載せます。追加変更位置がわかるように前後スケッチを表示
していますので、目印にしてオリジナルのスケッチへコピペ
してください。
ip address,gateway,sabnet,dns 固定不要ならIPAdressと
WiFi.config 行頭に // つけてコメントアウトしてください。
#include行のライブラリ名を半角<>で囲んで下さい。
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <SPI.h>
#include <SD.h>
#define DBG_OUTPUT_PORT Serial
//const char* ssid = "**********";
//const char* password = "**********";
char ssid[30]; // 追加
char password[30]; // 追加
const char* host = "esp32sd";
int gw[4], sn[4], dn[4], ip[4]; // 追加
int interval_t; // 追加 例 SDファイルから読み込む変数
WebServer server(80);
〜中略〜
else if (path.endsWith(".zip")) {
dataType = "application/zip";
}
//追加 setup.txtをwebから開こうとした時「ページが見つかりません」表示する
if (path == "/setup/setup.txt") {
server.send(404, "text/plain", ""); // 追加
} else { // 追加
File dataFile = SD.open(path.c_str());
if (dataFile.isDirectory()) {
path += "/index.htm";
dataType = "text/html";
dataFile = SD.open(path.c_str());
}
〜中略〜
dataFile.close();
return true;
} // 追加
}
void handleFileUpload() {
if (server.uri() != "/edit") {
return;
}
〜中略〜
void setup(void) {
if (SD.begin(SS)) { // 場所変更 変更後の場所
DBG_OUTPUT_PORT.println("SD Card initialized.");
hasSD = true; // 場所変更
} // 場所変更
DBG_OUTPUT_PORT.begin(115200);
DBG_OUTPUT_PORT.setDebugOutput(true);
DBG_OUTPUT_PORT.print("\n");
// 追加 ここから〜終了まで
File dataFile = SD.open("/setup/setup.txt", FILE_READ); // 設定ファイルをフォルダ/ファイル名でオープン
if (dataFile) {
while (dataFile.available()) {//dataFileを1行づつ評価していく
String line = dataFile.readStringUntil('\n');//終端文字'\n'が検出されるまで読んで文字列全体をlineへ入れる
line = line.substring(0, line.indexOf('#'));//インデックス0(行頭)から(#までのインデックス数)の文字列取得
if (line.substring(0, 2) == "SS") {
String ss = line.substring(line.indexOf(' '));//スペースから行末まで
ss.trim();//スペースを除去
ss.toCharArray(ssid, ss.length() + 1);
} else if (line.substring(0, 2) == "pa") {
String pass = line.substring(line.indexOf(' '));
pass.trim();
pass.toCharArray(password, pass.length() + 1);
} else if (line.substring(0, 2) == "gw") {
String gw0 = line.substring(3, 6); String gw1 = line.substring(7, 10);
String gw2 = line.substring(11, 14); String gw3 = line.substring(15, 18);
gw[0] = gw0.toInt(); gw[1] = gw1.toInt(); gw[2] = gw2.toInt(); gw[3] = gw3.toInt();
} else if (line.substring(0, 2) == "sn") {
String sn0 = line.substring(3, 6); String sn1 = line.substring(7, 10);
String sn2 = line.substring(11, 14); String sn3 = line.substring(15, 18);
sn[0] = sn0.toInt(); sn[1] = sn1.toInt(); sn[2] = sn2.toInt(); sn[3] = sn3.toInt();
} else if (line.substring(0, 2) == "dn") {
String dn0 = line.substring(3, 6); String dn1 = line.substring(7, 10);
String dn2 = line.substring(11, 14); String dn3 = line.substring(15, 18);
dn[0] = dn0.toInt(); dn[1] = dn1.toInt(); dn[2] = dn2.toInt(); dn[3] = dn3.toInt();
} else if (line.substring(0, 2) == "ip") {
String ip0 = line.substring(3, 6); String ip1 = line.substring(7, 10);
String ip2 = line.substring(11, 14); String ip3 = line.substring(15, 18);
ip[0] = ip0.toInt(); ip[1] = ip1.toInt(); ip[2] = ip2.toInt(); ip[3] = ip3.toInt();
} else if (line.substring(0, 2) == "it") {//例setup.txtのitという項目を変数interval_tへ設定
String it = line.substring(line.indexOf(' '));//スペースから行末まで
interval_t = it.toInt(); // データ型変換
}
}
dataFile.close();
} else {
Serial.println("error opening setup.txt");
}
IPAddress local_IP(ip[0], ip[1], ip[2], ip[3]);
IPAddress gateway(gw[0], gw[1], gw[2], gw[3]);
IPAddress subnet(sn[0], sn[1], sn[2], sn[3]);
IPAddress dns1(dn[0], dn[1], dn[2], dn[3]);
WiFi.mode(WIFI_STA);
WiFi.config(local_IP, gateway, subnet, dns1);
// 追加 終了(ここまで)
WiFi.begin(ssid, password);
DBG_OUTPUT_PORT.print("Connecting to ");
DBG_OUTPUT_PORT.println(ssid);
〜中略〜
server.begin();
DBG_OUTPUT_PORT.println("HTTP server started");
/*場所変更 元の場所
if (SD.begin(SS)) {
DBG_OUTPUT_PORT.println("SD Card initialized.");
hasSD = true;
}*/
}
void loop(void) {
server.handleClient();
delay(2);//allow the cpu to switch to other tasks
}
・ArduinoIDEにコピペしたらメニューの「ツール」「自動整形」
で見やすくなります。
・#includeライブラリ名<>を半角に直して下さい。
・SDに入れておくファイルはsetup.txt以外にもあります。
SDWebServer開いておき、「スケッチ」「スケッチのフォルダを
表示」、sdRootのフォルダを開き、中のファイルをSDにコピ
ーして下さい。オリジナルのコメントより
無保証自己責任でよろしくお願いします。
お気に入りアイテムSeeeduinoXIAO
CPU: 256KB Flashと2KB SRAMを搭載したARM® Cortex®-M0+ 32bit 48MHz マイクロコントローラー(SAMD21G18)
2023.04.22追記します。<br />RasberryPi OS(Debian Bullseye)で書き込み時の問題は発生しなくなりました。<br />時々「エラー 操作がキャンセルされました」出ますが以前のような困った状態には陥りません。<br />2021.12.13追記しておきます。
しばらくぶりにRasPiでXIAOを書いてみると、少し状況が変わっていました。
RasberryPi OS(Debian Buster-based version)で書き込み時の問題発生が軽減されているようです。
シリアルモニタを閉じてもスケッチは消えませんでした。
書き込み失敗、ポートの消失、USB抜き挿し要は変わらず
因みにBullseyeではどのスケッチもコンパイルエラーでした。
問題発生は我が家の環境だけかも知れませんので…
Windows10 PCでは何も問題なく使えます。
Raspberry Pi OSや、Raspberry Pi Desktop(intelCPU PC)で
書き込み中エラーや、シリアルポートが認識されなくなる。
シリアルモニタを閉じると書き込んだスケッチが消える?
困っています。
結論
WindowsPCを使う
継続調査の情報です。
Raspberry Pi OSでのスケッチ消失は、
ポート/dev/ttyACMx Seeeduino XIAOをシリアルモニタで
使わなければ大丈夫のようです。
シリアルモニタをみたいときは
①Serial1を使う(SerialUSBをSerial1に書き換える)
②USB-SERIAL変換をつなぐ
6pin(TX)→RXD 7pin(RX)→TXD GND→GND
③別のシリアルターミナルを使う
(GtkTermというのがあったので使いました。)
Raspberry Pi Desktop(intelCPU PC)では
USBケーブルの抜き挿しで回復できました。?
たまたまかも知れません。
RaspbrryPiでの不思議な現象
シリアルポートが認識されなくなった時
media/PiにArduinoという空フォルダがでていて解除できない。
SeeeduinoXIAOのUSBを抜いてもそのまま残る。
回復できた方法
①もう一度書き込んでみる。
②USBケーブルを抜き挿ししてみる。
③USBポートを変えてみる。
④SeeeduinoXIAOのリセットしてみる
(リセットパッドを2回ショート)
⑤RaspberryPiを再起動する。
再起動で一旦書き込めるようになるが、次の書き込みで
また発生することがあるので作業性悪い。
環境
Raspberry 4B 4GB Raspberry OS
NEC PCVJ27 OS:Raspberry Pi Desktop
Arduino IDE 1.8.15(1.8.13でも発生)
ボードマネージャ1.8.1
(1.7.9等他のバージョンでも発生していたようだ。)
スクリーンショット
「書き込み中にエラーが発生しました」
「マイコンボードへ書き込んでいます」で
ArduinoIDEがしばらくフリーズし回復後
XIAOのシリアルポートが認識されなくなる。
オレンジLED速い点滅 リセットしても変わらない
RaspbrryPi再起動したらオレンジLEDゆっくり点滅へ変わった。
ArduinoIDEを起動してシリアルポート確認すると、
XIAOのシリアルポートが認識されている。
書き込みができた。
正常に書き込めた時のスクリーンショット