diff --git a/.github/workflows/build-iotnode-lorawan.yml b/.github/workflows/build-iotnode-lorawan.yml index 161dbe1..86fbfad 100644 --- a/.github/workflows/build-iotnode-lorawan.yml +++ b/.github/workflows/build-iotnode-lorawan.yml @@ -1,15 +1,12 @@ name: IoT Node - LoRaWAN - Firmware build Action on: push: - - workflow_dispatch: branches: - - 'main' - - 'develop' - - 'feature/**' - - 'release/**' - - 'hotfix/**' + - main + workflow_dispatch: + + env: ARDUINO_RP2040_DIR: .arduino15/packages/rp2040/hardware/rp2040/4.3.1 IOT_NODE_LORAWAN_APP_KEY: ${{ secrets.IOT_NODE_LORAWAN_APP_KEY }} @@ -27,7 +24,7 @@ jobs: # checkout flux-sdk - name: Checkout the flux-sdk run: | - git clone --branch release/iot-node-lorawan-release-v1.0.0 https://github.com/sparkfun/flux-sdk.git + git clone --branch release/iot-node-lorawan-v1.0.1 https://github.com/sparkfun/flux-sdk.git echo "FLUX_SDK_PATH=`pwd`/flux-sdk" >> $GITHUB_ENV # Run cmake - this will build a custom SparkFun_Flux library we can use with @@ -52,15 +49,15 @@ jobs: # Install RP2040 - 4.0.3 (Nov, 2024) - name: Arduino - Install rp2040 platform - run: arduino-cli core install rp2040:rp2040@4.3.1 + run: arduino-cli core install rp2040:rp2040@4.4.4 - - name: Patch in our IOT Node boards - run: | - cd patch - cp boards.txt $HOME/$ARDUINO_RP2040_DIR/boards.txt - cp -R sparkfun_iotnode_lorawanrp2350 $HOME/$ARDUINO_RP2040_DIR/variants/ - cp sparkfun_iotnode_lorawan_rp2350.h $HOME/$ARDUINO_RP2040_DIR//pico-sdk/src/boards/include/boards/ - cd .. + # - name: Patch in our IOT Node boards + # run: | + # cd patch + # cp boards.txt $HOME/$ARDUINO_RP2040_DIR/boards.txt + # cp -R sparkfun_iotnode_lorawanrp2350 $HOME/$ARDUINO_RP2040_DIR/variants/ + # cp sparkfun_iotnode_lorawan_rp2350.h $HOME/$ARDUINO_RP2040_DIR//pico-sdk/src/boards/include/boards/ + # cd .. # install the libraries Flux uses - name: Install Flux dependant libraries @@ -68,27 +65,33 @@ jobs: # currently using a local copy of the library that removed some warning messages - source is here: # arduino-cli lib install --git-url "https://github.com/felixgalindo/XBeeArduino.git" + # arduino-cli lib install --git-url "https://github.com/sparkfun/XBeeArduino.git" - name: Install The XBee LoRaWAN library run: | arduino-cli config set library.enable_unsafe_install true - arduino-cli lib install --git-url "https://github.com/sparkfun/XBeeArduino.git" + arduino-cli lib install --git-url "https://github.com/felixgalindo/XBeeArduino.git" arduino-cli lib install FastLED # Compile time - build the Firmware for the data logger. # Note: # - The use of a full path to flux - this is needed or the build fails (relative paths get merged). # - ** Nov 25 - for build testing, using the pro micro board definition until new board added + # + # May 2025 - + # Add filesystem params for the flash layout - 1MB+ for prefs, 4MB for filesystem, the rest (11MB) for firmware - name: Compile DataLogger firmware binary run: arduino-cli compile --fqbn rp2040:rp2040:sparkfun_iotnode_lorawanrp2350 ./sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWAN.ino --build-property "compiler.cpp.extra_flags=\"-DIOT_NODE_LORAWAN_APP_KEY=$IOT_NODE_LORAWAN_APP_KEY\" \"-DFLX_SPARKFUN_LORAWAN_APP_EUI=$FLX_SPARKFUN_LORAWAN_APP_EUI\" \"-DBUILD_NUMBER=$GITHUB_RUN_NUMBER\"" + --build-property build.flash_length=11526144 --build-property upload.maximum_size=11526144 --build-property build.eeprom_start=284155904 --build-property build.fs_end=284155904 --build-property build.fs_start=279961600 --build-property build.flash_total=16777216 --export-binaries --clean --library `pwd`/SparkFun_IoTNodeLoRaWAN - # Upload the build files - bootloader, paritions, firmware - - uses: actions/upload-artifact@v4 + # Upload the build files - bootloader, partitions, firmware + - name: Upload Build Artifacts + uses: actions/upload-artifact@v4 with: - name: Upload Build - path: sfeIoTNodeLoRaWAN/build/rp2040.rp2040.sparkfun_iotnode_lorawanrp2350/sfeIoTNodeLoRaWAN.ino.uf2 + name: Upload Builds + path: sfeIoTNodeLoRaWAN/build/rp2040.rp2040.sparkfun_iotnode_lorawanrp2350/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4acb341 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +build/ + +SparkFun_IoTNodeLoRaWAN/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 37d83d1..17afe91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,8 +28,12 @@ flux_sdk_add_module( flux_logging flux_system flux_prefs + flux_prefs_json flux_prefs_serial flux_network + flux_sdcard + flux_file + flux_firmware device_bme280 device_bme68x device_bmp384 @@ -57,7 +61,8 @@ flux_sdk_add_module( device_vcnl4040 device_veml6075 device_veml7700 - device_vl53l1x) + device_vl53l1x + device_soilmoisture) # now call the init function/macro - this will build the Arduino Library SparkFun_Flux under this # main directory diff --git a/sfeIoTNodeLoRaWAN/flxLoRaWANDigi.cpp b/sfeIoTNodeLoRaWAN/flxLoRaWANDigi.cpp index 4d26be5..6c33153 100644 --- a/sfeIoTNodeLoRaWAN/flxLoRaWANDigi.cpp +++ b/sfeIoTNodeLoRaWAN/flxLoRaWANDigi.cpp @@ -391,7 +391,7 @@ bool flxLoRaWANDigi::setupModule(void) for (int i = 0; i < 3; i++) { flxLog_N_(F(".")); - if (_pXBeeLR->getLoRaWANDevEUI((uint8_t *)_devEUI, sizeof(_devEUI))) + if (_pXBeeLR->getLoRaWANDevEUI(_devEUI, sizeof(_devEUI))) { status = true; break; @@ -408,13 +408,13 @@ bool flxLoRaWANDigi::setupModule(void) // note: // TODO: this wasn't working in 12/2024 -- need to revisit in the future as the XBee LR library is updated - // // Set the region - // if (!_pXBeeLR->setLoRaWANRegion(_lora_region)) - // flxLog_W(F("%s: Error setting the LoRaWAN Region to `%s`"), name(), getRegionName()); - // else - // flxLog_V(F("%s: Set the LoRaWAN Region to `%s`"), name(), getRegionName()); + // Set the region + if (!_pXBeeLR->setLoRaWANRegion(_lora_region)) + flxLog_W(F("%s: Error setting the LoRaWAN Region to `%s`"), name(), getRegionName()); + else + flxLog_V_(F("%s: Set the LoRaWAN Region to `%s`"), name(), getRegionName()); - // flxLog_N_(F(".")); + flxLog_N_(F(".")); // Do we need to configure the module? if (_moduleConfigured() == false) diff --git a/sfeIoTNodeLoRaWAN/flxLoRaWANLogger.cpp b/sfeIoTNodeLoRaWAN/flxLoRaWANLogger.cpp index 59c7a7d..1c47006 100644 --- a/sfeIoTNodeLoRaWAN/flxLoRaWANLogger.cpp +++ b/sfeIoTNodeLoRaWAN/flxLoRaWANLogger.cpp @@ -9,6 +9,7 @@ */ #include "flxLoRaWANLogger.h" +#include //--------------------------------------------------------------------------- // flxLoRaWANLogger Class - outputs data to the lorawan during a log event @@ -52,7 +53,46 @@ void flxLoRaWANLogger::logObservation(void) // we don't process arrays. We only process scalar values if ((param->flags() & kParameterOutFlagArray) == kParameterOutFlagArray) { - flxLog_V("Array parameters not supported by LoRaWAN driver. Parameter: %s", param->name()); + flxParameterOutArray *pArray = (flxParameterOutArray *)param->accessor(); + // look for known values - if not, skip + switch (pArray->valueType()) + { + case kParamValueLocation: { + + flxDataArrayFloat *parrData = (flxDataArrayFloat *)pArray->get(); + + // is the data array sane? + if (parrData->size() != 2) + { + flxLog_W("Location array size is not 2. Size: %d", parrData->size()); + continue; + } + + // location is a two element float array, that we want to send in the same packet. + // So first, flush the buffer, then send the data + _pLoRaWAN->flushBuffer(); + + // trick the system into thinking we have a float array, which the LoRaWAN driver + // can handle. We need to do this because the LoRaWAN driver is expecting a float array[2] + float *pfData = parrData->get(); + float tmparr[2] = {pfData[0], pfData[1]}; + + if (flxIsLoggingVerbose()) + { + // dump out details for the parameter. Note - for packed value, this depends on + // verbose output from the packer. + flxLog_V_("LoRa Packing [%s::%s] Type: Location Value ID: 0x%02X Value: (%f,%f)", + pDevice->name(), param->name(), pArray->valueType(), tmparr[0], tmparr[1]); + } + + // send the data + status = _pLoRaWAN->sendData(pArray->valueType(), tmparr); + break; + } + default: + flxLog_V("Array parameters not supported by LoRaWAN driver. Parameter: %s", param->name()); + break; + } continue; } flxParameterOutScalar *pScalar = (flxParameterOutScalar *)param->accessor(); diff --git a/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWAN.cpp b/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWAN.cpp index f1a53a5..1b902e4 100644 --- a/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWAN.cpp +++ b/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWAN.cpp @@ -9,6 +9,7 @@ *--------------------------------------------------------------------------------- */ #include "sfeIoTNodeLoRaWAN.h" +#include "sfeNLBoard.h" #include "sfeNLCommands.h" #include "sfeNLLed.h" #include "sfeNLVersion.h" @@ -48,6 +49,14 @@ const uint8_t kLoRaWANMsgLEDFlash = 0x05; // Set the brightness for the on-board LED const uint8_t kLoRaWANMsgLEDBrightness = 0x06; + +// For finding the firmware files on SD card +#define kLoRaWANFirmwareFilePrefix "sfeIoTNodeLoRaWAN_" +//--------------------------------------------------------------------------- + +// The default Soil Sensor pins +const uint8_t kSoilSensorVCCPin = 28; +const uint8_t kSoilSensorSensorPin = 29; //--------------------------------------------------------------------------- // Application keys - used to encrypt runtime secrets for the app. @@ -69,7 +78,8 @@ static const uint8_t _app_jump[] = {104, 72, 67, 51, 74, 67, 108, 99, 104, 11 #define kAppClassPrefix "INLW" //--------------------------------------------------------------------------- // -sfeIoTNodeLoRaWAN::sfeIoTNodeLoRaWAN() : _opFlags{0} +sfeIoTNodeLoRaWAN::sfeIoTNodeLoRaWAN() + : _logTypeSD{kAppLogTypeNone}, _logTypeSer{kAppLogTypeNone}, _opFlags{0}, _hasOnBoardFlashFS{false} { // Constructor } @@ -209,14 +219,18 @@ void sfeIoTNodeLoRaWAN::onInit() // flxLog_I("in onInit()"); _logTypeSer = kAppLogTypeNone; + _logTypeSD = kAppLogTypeNone; serialLogType.setTitle("Output"); flxRegister(serialLogType, "Serial Console Format", "Enable and set the output format"); + flxRegister(sdCardLogType, "SD Card Format", "Enable and set the output format"); flxRegister(jsonBufferSize, "JSON Buffer Size", "Output buffer size in bytes"); // Terminal Serial Baud Rate flxRegister(serialBaudRate, "Terminal Baud Rate", "Update terminal baud rate. Changes take effect on restart"); _terminalBaudRate = kDefaultTerminalBaudRate; + enableSoilSensor.setTitle("Devices"); + flxRegister(enableSoilSensor, "Soil Moisture Sensor", "Enable GPIO attached Soil Moisture Sensor"); // Advanced settings verboseDevNames.setTitle("Advanced"); flxRegister(verboseDevNames, "Device Names", "Name always includes the device address"); @@ -234,6 +248,12 @@ void sfeIoTNodeLoRaWAN::onInit() _sysStorageDevice.initialize(preStart, kSegmentSize, 10); _sysStorage.setStorageDevice(&_sysStorageDevice); flxSettings.setStorage(&_sysStorage); + flxSettings.setFallback(&_jsonStorage); + + _jsonStorage.setFileSystem(&_theSDCard); + _jsonStorage.setFilename("iot-node-lorawan.json"); + + _theSDCard.setCSPin(kNLBoardSDCardCSPin); // Did the user set a serial value? uint32_t theRate; @@ -316,6 +336,25 @@ bool sfeIoTNodeLoRaWAN::onSetup() // was list device divers set by startup commands? if (inOpMode(kAppOpStartListDevices)) flux.dumpDeviceAutoLoadTable(); + // setup SD card. Do this before calling start - so prefs can be read off SD if needed + if (!setupSDCard()) + { + flxLog_W(F("Unable to initialize the SD Card. Is an SD card installed on the board?")); + } + + // Filesystem to read firmware from + _sysUpdate.setFileSystem(&_theSDCard); + + // Serial UX - used to list files to select off the filesystem + _sysUpdate.setSerialSettings(_serialSettings); + + _sysUpdate.setFirmwareFilePrefix(kLoRaWANFirmwareFilePrefix); + + flxRegisterEventCB(flxEvent::kOnFirmwareLoad, this, &sfeIoTNodeLoRaWAN::onFirmwareLoad); + flux_add(&_sysUpdate); + + // check our on-board flash file system + _hasOnBoardFlashFS = checkOnBoardFS(); // Button events we're listening on _boardButton.on_momentaryPress.call(this, &sfeIoTNodeLoRaWAN::onLogEvent); @@ -354,6 +393,10 @@ void sfeIoTNodeLoRaWAN::onDeviceLoad() for (auto b : *buttons) b->on_clicked.call(this, &sfeIoTNodeLoRaWAN::onQwiicButtonEvent); + + // setup our soil sensor device + _soilSensor.vccPin = kSoilSensorVCCPin; + _soilSensor.sensorPin = kSoilSensorSensorPin; } //--------------------------------------------------------------------------- // @@ -396,8 +439,10 @@ bool sfeIoTNodeLoRaWAN::onStart() flxLog_N_(F(" %-20s - %-40s {"), device->name(), device->description()); if (device->getKind() == flxDeviceKindI2C) flxLog_N("%s x%x}", "qwiic", device->address()); - else + else if (device->getKind() == flxDeviceKindSPI) flxLog_N("%s p%u}", "SPI", device->address()); + else if (device->getKind() == flxDeviceKindGPIO) + flxLog_N("%s p%u}", "GPIO", device->address()); if (device->nOutputParameters() > 0) { @@ -495,6 +540,29 @@ void sfeIoTNodeLoRaWAN::set_jsonBufferSize(uint32_t new_size) _fmtJSON.setBufferSize(new_size); } +uint8_t sfeIoTNodeLoRaWAN::get_logTypeSD(void) +{ + return _logTypeSD; +} +//--------------------------------------------------------------------------- +void sfeIoTNodeLoRaWAN::set_logTypeSD(uint8_t logType) +{ + if (logType == _logTypeSD) + return; + + if (_logTypeSD == kAppLogTypeCSV) + _fmtCSV.remove(&_theOutputFile); + else if (_logTypeSD == kAppLogTypeJSON) + _fmtJSON.remove(&_theOutputFile); + + _logTypeSD = logType; + + if (_logTypeSD == kAppLogTypeCSV) + _fmtCSV.add(&_theOutputFile); + else if (_logTypeSD == kAppLogTypeJSON) + _fmtJSON.add(&_theOutputFile); +} + //--------------------------------------------------------------------------- uint8_t sfeIoTNodeLoRaWAN::get_logTypeSer(void) { @@ -547,6 +615,45 @@ void sfeIoTNodeLoRaWAN::set_local_name(std::string name) flux.setLocalName(name); } +//--------------------------------------------------------------------------- +// soil sensor enabled/disable +//--------------------------------------------------------------------------- +void sfeIoTNodeLoRaWAN::set_soil_enabled(bool enable) +{ + // Is the soil sensor in the system? + + bool active = flux.contains(_soilSensor); + if (active == enable) + return; // same + + if (enable) + { + // is the sensor initialized? + if (!_soilSensor.isInitialized()) + { + // init the sensor - this adds to the device list + if (_soilSensor.initialize() == false) + flxLog_W(F("%s: failed to initialize."), _soilSensor.name()); + } + else + flux.add(_soilSensor); + _loraWANLogger.add(_soilSensor); + _logger.add(_soilSensor); + } + else + { + flux.remove(_soilSensor); + _loraWANLogger.remove(_soilSensor); + _logger.remove(_soilSensor); + } + + _soilSensor.isEnabled(enable); +} + +bool sfeIoTNodeLoRaWAN::get_soil_enabled(void) +{ + return flux.contains(_soilSensor); +} //--------------------------------------------------------------------------- // Display things during settings edits //--------------------------------------------------------------------------- @@ -678,6 +785,15 @@ void sfeIoTNodeLoRaWAN::onSystemResetEvent(void) // The system is being reset - reset our settings flxSettings.reset(); } +//--------------------------------------------------------------------------- +void sfeIoTNodeLoRaWAN::onFirmwareLoad(bool bLoading) +{ + if (bLoading) + sfeLED.on(sfeLED.Yellow); + else + sfeLED.off(); +} + //--------------------------------------------------------------------------- // Callback for LoRaWAN receive events void sfeIoTNodeLoRaWAN::onLoRaWANReceiveEvent(uint32_t data) diff --git a/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWAN.h b/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWAN.h index f34a8c0..ca57bb3 100644 --- a/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWAN.h +++ b/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWAN.h @@ -17,17 +17,21 @@ #include #include +#include "flxLoRaWANDigi.h" +#include "flxLoRaWANLogger.h" +#include "sfeNLButton.h" #include +#include +#include +#include #include #include #include +#include #include +#include #include -#include "flxLoRaWANDigi.h" -#include "flxLoRaWANLogger.h" -#include "sfeNLButton.h" - // Buffer size of our JSON document output const uint16_t kAppJSONDocSize = 1600; // Default log interval in milli secs @@ -77,6 +81,13 @@ class sfeIoTNodeLoRaWAN : public flxApplication static constexpr uint8_t kAppLogTypeJSON = 0x2; static constexpr const char *kLogFormatNames[] = {"Disabled", "CSV Format", "JSON Format"}; + + //--------------------------------------------------------------------------- + uint8_t get_logTypeSD(void); + + //--------------------------------------------------------------------------- + void set_logTypeSD(uint8_t logType); + uint8_t _logTypeSD; uint8_t _logTypeSer; // type of serial log output format //--------------------------------------------------------------------------- uint8_t get_logTypeSer(void); @@ -110,6 +121,9 @@ class sfeIoTNodeLoRaWAN : public flxApplication bool get_verbose(void); void set_verbose(bool enable); + bool get_soil_enabled(void); + void set_soil_enabled(bool enable); + void onSettingsEdit(bool bLoading); void onSystemActivity(void); void onSystemActivityLow(void); @@ -120,6 +134,7 @@ class sfeIoTNodeLoRaWAN : public flxApplication void onErrorMessage(uint8_t); void onLogEvent(void); void onQwiicButtonEvent(bool); + void onFirmwareLoad(bool bLoading); void onLoRaWANSendEvent(bool); void onLoRaWANReceiveEvent(uint32_t); @@ -153,6 +168,12 @@ class sfeIoTNodeLoRaWAN : public flxApplication &sfeIoTNodeLoRaWAN::set_verbose_dev_name> verboseDevNames; + flxPropertyRWUInt8 + sdCardLogType = {kAppLogTypeCSV, + {{kLogFormatNames[kAppLogTypeNone], kAppLogTypeNone}, + {kLogFormatNames[kAppLogTypeCSV], kAppLogTypeCSV}, + {kLogFormatNames[kAppLogTypeJSON], kAppLogTypeJSON}}}; + flxPropertyRWUInt8 serialLogType = {kAppLogTypeCSV, {{kLogFormatNames[kAppLogTypeNone], kAppLogTypeNone}, @@ -176,6 +197,9 @@ class sfeIoTNodeLoRaWAN : public flxApplication flxPropertyRWBool verboseEnabled = {false}; + flxPropertyRWBool + enableSoilSensor = {false}; + private: friend class sfeNLCommands; void _displayAboutObjHelper(char, const char *, bool); @@ -197,6 +221,8 @@ class sfeIoTNodeLoRaWAN : public flxApplication // setup routines bool setupTime(); void setupENS160(void); + bool setupSDCard(void); + bool checkOnBoardFS(void); // Our LoRaWAN network/connection object flxLoRaWANDigi _loraWANConnection; @@ -213,6 +239,10 @@ class sfeIoTNodeLoRaWAN : public flxApplication flxFormatJSON _fmtJSON; flxFormatCSV _fmtCSV; + flxFSSDCard _theSDCard; + flxFileRotate _theOutputFile; + flxStorageJSONPrefFile _jsonStorage; + // Serial Settings editor flxSettingsSerial _serialSettings; @@ -226,6 +256,12 @@ class sfeIoTNodeLoRaWAN : public flxApplication // Fuel gauge flxDevMAX17048 *_fuelGauge; + // The soil moisture device object -- if a user connects it to the GPIO of the device. + flxDevSoilMoisture _soilSensor; + + // The system firmware object + flxSysFirmware _sysUpdate; + // for our button events of the board sfeNLButton _boardButton; @@ -233,4 +269,7 @@ class sfeIoTNodeLoRaWAN : public flxApplication std::unique_ptr _batteryJob; uint32_t _opFlags; + + // flag for the on-board flash file system (RP2350) + bool _hasOnBoardFlashFS = false; }; \ No newline at end of file diff --git a/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWANAbout.cpp b/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWANAbout.cpp index 0665534..3dd448e 100644 --- a/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWANAbout.cpp +++ b/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWANAbout.cpp @@ -17,6 +17,8 @@ #include #include +#include + void sfeIoTNodeLoRaWAN::_displayAboutObjHelper(char pre_ch, const char *szName, bool enabled) { flxLog_N_("%c %-20s : ", pre_ch, szName); @@ -115,24 +117,60 @@ void sfeIoTNodeLoRaWAN::displayAppStatus(bool useInfo) flxSerial.textToWhite(); flxLog_N(" System:"); flxSerial.textToNormal(); + flxSerial.flush(); } - // if (_theSDCard.enabled()) - // { + if (_theSDCard.enabled()) + { + + char szSize[32]; + char szCap[32]; + + uint64_t sd_size = _theSDCard.size(); + uint64_t sd_total = _theSDCard.total(); - // char szSize[32]; - // char szCap[32]; - // char szAvail[32]; + flx_utils::formatByteString(sd_size, 2, szSize, sizeof(szSize)); + flx_utils::formatByteString(sd_total, 2, szCap, sizeof(szCap)); + + flxLog___(logLevel, "%cSD Card - Type: %s Size: %s Capacity: %s ", pre_ch, _theSDCard.type(), szSize, szCap); + + // Getting about used can take time -- so only do if about is called (use info is false) + if (!useInfo) + { + flxLog_N_("..."); + flxSerial.flush(); + char szAvail[32]; + // This call can take some time .. .so + uint64_t sd_used = _theSDCard.used(); + flx_utils::formatByteString(sd_total - sd_used, 2, szAvail, sizeof(szAvail)); + flxLog_N("Free: %s (%.1f%%)", szAvail, 100. - (sd_used / (float)sd_total * 100.)); + } + else + flxLog_N(""); + } + else + flxLog__(logLevel, "%cSD card not available", pre_ch); - // flx_utils::formatByteString(_theSDCard.size(), 2, szSize, sizeof(szSize)); - // flx_utils::formatByteString(_theSDCard.total(), 2, szCap, sizeof(szCap)); - // flx_utils::formatByteString(_theSDCard.total() - _theSDCard.used(), 2, szAvail, sizeof(szAvail)); + flxLog___(logLevel, "%cSystem File System - ", pre_ch); - // flxLog__(logLevel, "%cSD Card - Type: %s Size: %s Capacity: %s Free: %s (%.1f%%)", pre_ch, - // _theSDCard.type(), - // szSize, szCap, szAvail, 100. - (_theSDCard.used() / (float)_theSDCard.total() * 100.)); - // } - // else - // flxLog__(logLevel, "%cSD card not available", pre_ch); + if (_hasOnBoardFlashFS) + { + FSInfo fs_info; + if (LittleFS.info(fs_info)) + { + char szSize[32]; + char szUsed[32]; + char szAvail[32]; + flx_utils::formatByteString(fs_info.totalBytes, 2, szSize, sizeof(szSize)); + flx_utils::formatByteString(fs_info.usedBytes, 2, szUsed, sizeof(szUsed)); + flx_utils::formatByteString(fs_info.totalBytes - fs_info.usedBytes, 2, szAvail, sizeof(szAvail)); + + flxLog_N("size: %s, used: %s, free: %s", szSize, szUsed, szAvail); + } + else + flxLog_N(F("unable to access info")); + } + else + flxLog_N(F("not available")); // show heap level flxLog__(logLevel, "%cSystem Heap - Total: %dB Free: %dB (%.1f%%)", pre_ch, flxPlatform::heap_size(), @@ -167,15 +205,15 @@ void sfeIoTNodeLoRaWAN::displayAppStatus(bool useInfo) flxLog__(logLevel, "%cJSON Buffer - Size: %dB Max Used: %dB", pre_ch, jsonBufferSize(), _fmtJSON.getMaxSizeUsed()); flxLog__(logLevel, "%cSerial Output: %s", pre_ch, kLogFormatNames[serialLogType()]); flxLog_N("%c Baud Rate: %d", pre_ch, serialBaudRate()); + flxLog__(logLevel, "%cSD Card Output: %s", pre_ch, kLogFormatNames[sdCardLogType()]); // flxLog__(logLevel, "%cSD Card Output: %s", pre_ch, kLogFormatNames[sdCardLogType()]); // at startup, useInfo == true, the file isn't known, so skip output - // if (!useInfo) - // flxLog_N("%c Current Filename: \t%s", pre_ch, - // _theOutputFile.currentFilename().length() == 0 ? "" : - // _theOutputFile.currentFilename().c_str()); - // flxLog_N("%c Rotate Period: %d Hours", pre_ch, _theOutputFile.rotatePeriod()); + if (!useInfo) + flxLog_N("%c Current Filename: \t%s", pre_ch, + _theOutputFile.currentFilename().length() == 0 ? "" : _theOutputFile.currentFilename().c_str()); + flxLog_N("%c Rotate Period: %d Hours", pre_ch, _theOutputFile.rotatePeriod()); flxLog_N(""); @@ -196,8 +234,11 @@ void sfeIoTNodeLoRaWAN::displayAppStatus(bool useInfo) flxLog_N_(F("%c %-20s - %-40s {"), pre_ch, device->name(), device->description()); if (device->getKind() == flxDeviceKindI2C) flxLog_N("%s x%x}", "qwiic", device->address()); - else + + else if (device->getKind() == flxDeviceKindSPI) flxLog_N("%s p%u}", "SPI", device->address()); + else if (device->getKind() == flxDeviceKindGPIO) + flxLog_N("%s p%u}", "GPIO", device->address()); } flxLog_N(""); diff --git a/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWANSetup.cpp b/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWANSetup.cpp index c4023cf..65a66ae 100644 --- a/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWANSetup.cpp +++ b/sfeIoTNodeLoRaWAN/sfeIoTNodeLoRaWANSetup.cpp @@ -21,6 +21,13 @@ #include #include +#include + +// on board flash file system bounds + +extern uint8_t _FS_start; +extern uint8_t _FS_end; + //--------------------------------------------------------------------------- // setupTime() // @@ -88,4 +95,60 @@ void sfeIoTNodeLoRaWAN::setupENS160(void) flxLog_I(F("%s: compensation values applied from %s"), pENS160->name(), pSHTC3->name()); return; } +} + +//--------------------------------------------------------------------------- +// setupSDCard() +// +// Set's up the SD card subsystem and the objects/systems that use it. +bool sfeIoTNodeLoRaWAN::setupSDCard(void) +{ + + // setup output to the SD card + if (_theSDCard.initialize()) + { + + _theOutputFile.setName("Data File", "Output file rotation manager"); + + // SD card is available - lets setup output for it + // Add the filesystem to the file output/rotation object + _theOutputFile.setFileSystem(_theSDCard); + + // setup our file rotation parameters + _theOutputFile.filePrefix = "sfe"; + _theOutputFile.startNumber = 1; + _theOutputFile.rotatePeriod(24); // one day + + // add the file output to the CSV output. + //_fmtCSV.add(_theOutputFile); + + // have the CSV format driver listen to the new file event. This + // will cause a header to be written next cycle. + flxRegisterEventCB(flxEvent::kOnNewFile, &_fmtCSV, &flxFormatCSV::output_header); + + return true; + } + return false; +} +//--------------------------------------------------------------------------- +// checkOnBoardFS() +// +// Do we have an onboard FS? + +bool sfeIoTNodeLoRaWAN::checkOnBoardFS(void) +{ + + // Was a filesystem set for the on-board flash? + if (&_FS_end - &_FS_start <= 0) + { + flxLog_W(F("No onboard flash file system detected")); + return false; + } + // Startup little fs + if (LittleFS.begin() == false) + { + flxLog_W(F("Unable to mount flash file system")); + return false; + } + return true; } \ No newline at end of file diff --git a/sfeIoTNodeLoRaWAN/sfeNLBoard.h b/sfeIoTNodeLoRaWAN/sfeNLBoard.h index 6de910c..8396c52 100644 --- a/sfeIoTNodeLoRaWAN/sfeNLBoard.h +++ b/sfeIoTNodeLoRaWAN/sfeNLBoard.h @@ -19,6 +19,9 @@ const uint8_t kNLBoardUserButton = 24; // 3v3 pin // const uint8_t kNLBoardEn3v3_SW = 32; +// SD Card CS Ping +const uint8_t kNLBoardSDCardCSPin = 13; + // LED Built in const uint8_t kNLBoardLEDBuiltin = 25; diff --git a/sfeIoTNodeLoRaWAN/sfeNLCommands.h b/sfeIoTNodeLoRaWAN/sfeNLCommands.h index 90c5634..bd9de78 100644 --- a/sfeIoTNodeLoRaWAN/sfeNLCommands.h +++ b/sfeIoTNodeLoRaWAN/sfeNLCommands.h @@ -120,32 +120,29 @@ class sfeNLCommands /// @param theApp Pointer to the DataLogger App /// @retval bool indicates success (true) or failure (!true) /// - // bool loadJSONSettings(sfeIoTNodeLoRaWAN *theApp) - // { - // if (!theApp) - // return false; - - // flxLog_I(F("Load JSON Settings - Not Implemented")); + bool loadJSONSettings(sfeIoTNodeLoRaWAN *theApp) + { + if (!theApp) + return false; - // // // Create a JSON prefs serial object and read in the settings - // // flxStorageJSONPrefSerial prefsSerial(flxSettings.fallbackBuffer() > 0 ? flxSettings.fallbackBuffer() : - // 2000); + // Create a JSON prefs serial object and read in the settings + flxStorageJSONPrefSerial prefsSerial(flxSettings.fallbackBuffer() > 0 ? flxSettings.fallbackBuffer() : 2000); - // // // restore the settings from serial - // // bool status = flxSettings.restoreObjectFromStorage(&flux, &prefsSerial); - // // if (!status) - // // return false; + // restore the settings from serial + bool status = flxSettings.restoreObjectFromStorage(&flux, &prefsSerial); + if (!status) + return false; - // // flxLog_I_(F("Settings restored from serial...")); + flxLog_I_(F("Settings restored from serial...")); - // // // now save the new settings in primary storage - // // status = flxSettings.save(&flux, true); - // // if (status) - // // flxLog_N(F("saved locally")); + // now save the new settings in primary storage + status = flxSettings.save(&flux, true); + if (status) + flxLog_N(F("saved locally")); - // // return status; - // return true; - // } + // return status; + return true; + } //--------------------------------------------------------------------- /// /// @brief Saves the current system to preferences/Settings @@ -232,35 +229,37 @@ class sfeNLCommands /// @param theApp Pointer to the DataLogger App /// @retval bool indicates success (true) or failure (!true) /// - // bool sdCardStats(sfeIoTNodeLoRaWAN *theApp) - // { - // if (!theApp) - // return false; + bool sdCardStats(sfeIoTNodeLoRaWAN *theApp) + { + if (!theApp) + return false; - // flxLog_I(F("SD Stats - Not Implemented")); + if (theApp->_theSDCard.enabled()) + { + char szSize[32]; + char szCap[32]; + char szAvail[32]; - // // if (theApp->_theSDCard.enabled()) - // // { + uint64_t sd_size = theApp->_theSDCard.size(); + uint64_t sd_total = theApp->_theSDCard.total(); - // // char szSize[32]; - // // char szCap[32]; - // // char szAvail[32]; + flx_utils::formatByteString(sd_size, 2, szSize, sizeof(szSize)); + flx_utils::formatByteString(sd_total, 2, szCap, sizeof(szCap)); - // // flx_utils::formatByteString(theApp->_theSDCard.size(), 2, szSize, sizeof(szSize)); - // // flx_utils::formatByteString(theApp->_theSDCard.total(), 2, szCap, sizeof(szCap)); - // // flx_utils::formatByteString(theApp->_theSDCard.total() - theApp->_theSDCard.used(), 2, szAvail, - // // sizeof(szAvail)); + flxLog_I_("SD Card - Type: %s Size: %s Capacity: %s ", theApp->_theSDCard.type(), szSize, szCap); - // // flxLog_I(F("SD Card - Type: %s Size: %s Capacity: %s Free: %s (%.1f%%)"), theApp->_theSDCard.type(), - // // szSize, - // // szCap, szAvail, 100. - (theApp->_theSDCard.used() / (float)theApp->_theSDCard.total() * - // 100.)); - // // } - // // else - // // flxLog_I(F("SD card not available")); + flxLog_N_("..."); + flxSerial.flush(); + // This call can take some time .. .so + uint64_t sd_used = theApp->_theSDCard.used(); + flx_utils::formatByteString(sd_total - sd_used, 2, szAvail, sizeof(szAvail)); + flxLog_N("Free: %s (%.1f%%)", szAvail, 100. - (sd_used / (float)sd_total * 100.)); + } + else + flxLog_I(F("SD card not available")); - // return true; - // } + return true; + } //--------------------------------------------------------------------- /// @@ -429,11 +428,11 @@ class sfeNLCommands {"clear-settings-forced", &sfeNLCommands::clearDeviceSettingsForced}, {"restart", &sfeNLCommands::restartDevice}, {"restart-forced", &sfeNLCommands::restartDeviceForced}, - // {"json-settings", &sfeNLCommands::loadJSONSettings}, + {"json-settings", &sfeNLCommands::loadJSONSettings}, {"log-rate", &sfeNLCommands::logRateStats}, {"log-rate-toggle", &sfeNLCommands::logRateToggle}, {"log-now", &sfeNLCommands::logObservationNow}, - // {"sdcard", &sfeNLCommands::sdCardStats}, + {"sdcard", &sfeNLCommands::sdCardStats}, {"devices", &sfeNLCommands::listLoadedDevices}, {"save-settings", &sfeNLCommands::saveSettings}, {"heap", &sfeNLCommands::heapStatus}, @@ -472,6 +471,7 @@ class sfeNLCommands flxLog_N(F("Unknown Command: `%s`"), sBuffer.c_str()); status = false; } + flxLog_N(""); return status; } }; diff --git a/sfeIoTNodeLoRaWAN/sfeNLVersion.h b/sfeIoTNodeLoRaWAN/sfeNLVersion.h index 35f66d5..c49cc05 100644 --- a/sfeIoTNodeLoRaWAN/sfeNLVersion.h +++ b/sfeIoTNodeLoRaWAN/sfeNLVersion.h @@ -16,13 +16,13 @@ #define kDLVersionNumberMajor 1 // Minor version number -#define kDLVersionNumberMinor 0 +#define kDLVersionNumberMinor 1 // Point version number #define kDLVersionNumberPoint 0 // Version string description -#define kDLVersionDescriptor "Version 1.0.0" +#define kDLVersionDescriptor "" // app name/class ID string #define kDLAppClassNameID "SFE-IOT-NODE_LORAWAN"