diff --git a/examples/Sensor/QMC63xx_GetDataExample/QMC63xx_GetDataExample.ino b/examples/Sensor/QMC63xx_GetDataExample/QMC63xx_GetDataExample.ino index 6bba26e..c815827 100644 --- a/examples/Sensor/QMC63xx_GetDataExample/QMC63xx_GetDataExample.ino +++ b/examples/Sensor/QMC63xx_GetDataExample/QMC63xx_GetDataExample.ino @@ -31,6 +31,9 @@ #include #include #include "LoRaBoards.h" +#include "QmcServices.h" + +#define Serial QmcSerial void calibrate(); MagnetometerBase *magnetometer; @@ -41,6 +44,7 @@ void setup() while (!Serial); setupBoards(); + qmcServicesBegin(); // The desired output data rate in Hz. Allowed values are 1.0, 10.0, 50.0, 100.0 and 200.0HZ. float data_rate_hz = 200.0f; @@ -184,6 +188,7 @@ void setup() void loop() { + qmcServicesUpdate(); MagnetometerData data; @@ -194,6 +199,20 @@ void loop() float y = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.y); float z = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.z); + float deg_xy = atan2(y, x) * 180.0f / PI; + float deg_xny = atan2(-y, x) * 180.0f / PI; + float deg_yx = atan2(x, y) * 180.0f / PI; + float deg_nxy = atan2(y, -x) * 180.0f / PI; + float deg_xz = atan2(z, x) * 180.0f / PI; + float deg_zx = atan2(x, z) * 180.0f / PI; + float deg_yz = atan2(z, y) * 180.0f / PI; + float deg_zy = atan2(y, z) * 180.0f / PI; + auto norm360 = [](float d) { + while (d < 0) d += 360.0f; + while (d >= 360.0f) d -= 360.0f; + return d; + }; + Serial.print("Mag:"); Serial.print(" X:"); Serial.print(x); Serial.print(" Y:"); Serial.print(y); @@ -222,10 +241,20 @@ void loop() Serial.print(strength, 2); Serial.println(" μT"); + Serial.print(" XY:"); Serial.print(norm360(deg_xy), 2); + Serial.print(" XnY:"); Serial.print(norm360(deg_xny), 2); + Serial.print(" YX:"); Serial.print(norm360(deg_yx), 2); + Serial.print(" nXY:"); Serial.print(norm360(deg_nxy), 2); + Serial.print(" XZ:"); Serial.print(norm360(deg_xz), 2); + Serial.print(" ZX:"); Serial.print(norm360(deg_zx), 2); + Serial.print(" YZ:"); Serial.print(norm360(deg_yz), 2); + Serial.print(" ZY:"); Serial.println(norm360(deg_zy), 2); + if (data.overflow) { Serial.println("\tWarning: Data Overflow occurred!"); } } + qmcServicesUpdate(); delay(10); } diff --git a/examples/Sensor/QMC63xx_GetDataExample/QmcServices.cpp b/examples/Sensor/QMC63xx_GetDataExample/QmcServices.cpp new file mode 100644 index 0000000..2f82764 --- /dev/null +++ b/examples/Sensor/QMC63xx_GetDataExample/QmcServices.cpp @@ -0,0 +1,174 @@ +#include "QmcServices.h" + +#include +#include +#include + +#ifndef BOARD_ID +#define BOARD_ID "NODE" +#endif + +#ifndef NODE_LABEL +#define NODE_LABEL BOARD_ID +#endif + +#ifndef LOG_AP_IP_OCTET +#define LOG_AP_IP_OCTET 25 +#endif + +namespace { + +tbeam::TBeamClock clockService(Wire1); +tbeam::TBeamStorage storageService(::Serial); +tbeam::TBeamWeb webService(::Serial); + +bool servicesStarted = false; +uint32_t lastFlushMs = 0; +uint32_t lastClockUpdateMs = 0; +char logPath[128] = {}; + +void openRunLog() +{ + if (!storageService.ready()) { + return; + } + + char runId[64] = {}; + tbeam::DateTime rtc; + int64_t epoch = 0; + if (clockService.readValidRtc(rtc, &epoch)) { + tbeam::TBeamClock::makeRunId(rtc, BOARD_ID, runId, sizeof(runId)); + } else { + snprintf(runId, sizeof(runId), "%s_%lu", BOARD_ID, static_cast(millis() / 1000)); + } + + snprintf(logPath, sizeof(logPath), "/logs/qmc63xx/%s.log", runId); + if (!storageService.openLog(logPath)) { + ::Serial.printf("[qmc-services] log open failed: %s\n", storageService.lastError()); + logPath[0] = '\0'; + return; + } + + storageService.println("# test: qmc63xx"); + storageService.print("# board_id: "); + storageService.println(BOARD_ID); + storageService.print("# node_label: "); + storageService.println(NODE_LABEL); + storageService.print("# log_path: "); + storageService.println(logPath); + if (clockService.valid()) { + char iso[32] = {}; + tbeam::TBeamClock::formatIsoUtc(clockService.lastRtc(), iso, sizeof(iso)); + storageService.print("# rtc_utc: "); + storageService.println(iso); + } + storageService.println("# serial_tee: enabled"); + storageService.flush(); +} + +} // namespace + +QmcSerialTee QmcSerial; + +void QmcSerialTee::begin(unsigned long baud) +{ + ::Serial.begin(baud); +} + +size_t QmcSerialTee::write(uint8_t value) +{ + const size_t serialWritten = ::Serial.write(value); + if (servicesStarted && storageService.isLogOpen()) { + storageService.write(&value, 1); + } + return serialWritten; +} + +size_t QmcSerialTee::write(const uint8_t* buffer, size_t size) +{ + const size_t serialWritten = ::Serial.write(buffer, size); + if (servicesStarted && storageService.isLogOpen()) { + storageService.write(buffer, size); + } + return serialWritten; +} + +void QmcSerialTee::flush() +{ + ::Serial.flush(); + if (servicesStarted && storageService.isLogOpen()) { + storageService.flush(); + } +} + +void qmcServicesBegin() +{ + if (servicesStarted) { + return; + } + + tbeam::ClockConfig clockConfig; + clockConfig.beginWire = false; + clockService.begin(clockConfig); + clockService.update(); + + tbeam::StorageConfig storageConfig; + storageConfig.logDir = "/logs/qmc63xx"; + storageConfig.enablePinDumps = false; + storageService.begin(storageConfig); + + openRunLog(); + + tbeam::WebConfig webConfig; + webConfig.ssidPrefix = "GPSQA"; + webConfig.boardId = BOARD_ID; + webConfig.password = nullptr; + webConfig.ipOctet = LOG_AP_IP_OCTET; + webConfig.enableDelete = true; + webService.begin(storageService, webConfig); + + servicesStarted = true; + + ::Serial.printf("[qmc-services] board=%s label=%s\n", BOARD_ID, NODE_LABEL); + if (clockService.valid()) { + char iso[32] = {}; + tbeam::TBeamClock::formatIsoUtc(clockService.lastRtc(), iso, sizeof(iso)); + ::Serial.printf("[qmc-services] rtc=%s\n", iso); + } else { + ::Serial.printf("[qmc-services] rtc invalid: %s\n", clockService.lastError()); + } + ::Serial.printf("[qmc-services] log=%s\n", logPath[0] ? logPath : "(not open)"); + if (webService.ready()) { + ::Serial.printf("[qmc-services] web=http://%s/ ssid=%s\n", + webService.ip().toString().c_str(), + webService.ssid()); + } else { + ::Serial.printf("[qmc-services] web unavailable: %s\n", webService.lastError()); + } +} + +void qmcServicesUpdate() +{ + if (!servicesStarted) { + return; + } + + const uint32_t now = millis(); + storageService.update(); + webService.update(); + + if (now - lastClockUpdateMs >= 1000) { + lastClockUpdateMs = now; + clockService.update(); + } + + if (now - lastFlushMs >= 2000) { + lastFlushMs = now; + storageService.flush(); + } +} + +const char* qmcServicesLogPath() +{ + return logPath; +} diff --git a/examples/Sensor/QMC63xx_GetDataExample/QmcServices.h b/examples/Sensor/QMC63xx_GetDataExample/QmcServices.h new file mode 100644 index 0000000..53e82fc --- /dev/null +++ b/examples/Sensor/QMC63xx_GetDataExample/QmcServices.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class QmcSerialTee : public Print { + public: + void begin(unsigned long baud); + explicit operator bool() const { return true; } + + size_t write(uint8_t value) override; + size_t write(const uint8_t* buffer, size_t size) override; + void flush(); +}; + +extern QmcSerialTee QmcSerial; + +void qmcServicesBegin(); +void qmcServicesUpdate(); +const char* qmcServicesLogPath(); diff --git a/examples/platformio.ini b/examples/platformio.ini index 9466bd8..5e07652 100644 --- a/examples/platformio.ini +++ b/examples/platformio.ini @@ -16,7 +16,9 @@ monitor_speed = 115200 ; Reuse LilyGO's bundled libraries: ; SensorLib, ESP8266_SSD1306, XPowersLib, RadioLib, etc. -lib_extra_dirs = /usr/local/src/LilyGo-LoRa-Series/lib +lib_extra_dirs = + /usr/local/src/LilyGo-LoRa-Series/lib + /usr/local/src/microreticulum/microReticulumTbeam/lib monitor_filters = default @@ -26,6 +28,12 @@ monitor_filters = build_flags = -DARDUINO_USB_CDC_ON_BOOT=1 -DCORE_DEBUG_LEVEL=0 + -I /usr/local/src/microreticulum/microReticulumTbeam/shared/boards + -I /usr/local/src/microreticulum/microReticulumTbeam/external/microReticulum_Firmware + -D BOARD_MODEL=BOARD_TBEAM_S_V1 + -D BOARD_ID=\"CY\" + -D NODE_LABEL=\"Cy\" + -D LOG_AP_IP_OCTET=25 [env:T_BEAM_S3_SUPREME_SX1262] board = t-beams3-supreme