From a6970612ce5d97a442dc25343016030699dd2424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=B6r=C3=B6k=20Istv=C3=A1n?= Date: Tue, 7 Jan 2025 13:41:02 +0100 Subject: [PATCH] Copy from old project - Initial state --- .vscode/extensions.json | 10 + .vscode/settings.json | 13 + compile_commands.json | 232 +++ include/README | 39 + lib/README | 46 + platformio.ini | 22 + src/main.cpp | 2940 +++++++++++++++++++++++++++++++++++++++ src/mybluetooth.h | 76 + src/settings.h | 428 ++++++ test/README | 11 + 10 files changed, 3817 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 compile_commands.json create mode 100644 include/README create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/main.cpp create mode 100644 src/mybluetooth.h create mode 100644 src/settings.h create mode 100644 test/README diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..97bd8ae --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "sonarlint.pathToCompileCommands": "${workspaceFolder}\\compile_commands.json", + "outline.showConstants": false, + "outline.showArrays": false, + "outline.showBooleans": false, + "outline.showEnumMembers": false, + "outline.showFields": false, + "outline.showFiles": false, + "outline.showNamespaces": false, + "outline.showProperties": false, + "outline.showTypeParameters": false, + "outline.showVariables": false +} \ No newline at end of file diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..00c1edb --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,232 @@ +[ + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp9k_kbse1.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\Esp.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp6q394xjq.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\FirmwareMSC.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpg01t_jf3.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\FunctionalInterrupt.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpge6kqzh6.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\HWCDC.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp2mfm4_w0.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\HardwareSerial.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmphcld15xj.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\IPAddress.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpnnf495as.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\IPv6Address.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmprhe56ba3.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\MD5Builder.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp3c8tm3t9.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\Print.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpbvk5_8hh.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\Stream.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpami9vacs.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\StreamString.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmphq5tfvuo.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\Tone.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpxqjyql9g.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\USB.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp4lgmm9rz.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\USBCDC.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp27qpawx3.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\USBMSC.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp9szwrax5.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\WMath.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp5s7914ld.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\WString.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpy4ou81av.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\base64.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpfaqtb5uf.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\cbuf.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmppkeps5pj.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-adc.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp44ybcjwa.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-bt.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpefdpmd0i.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-cpu.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmplulj0ps1.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-dac.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpt1mk4c6m.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-gpio.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpj4k79v3c.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-i2c-slave.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmphts3byyn.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-i2c.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpyk1mbp68.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-ledc.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp3zz3ax35.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-matrix.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmphj0rmx_5.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-misc.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp1ubf4id2.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-psram.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp2m6gr80o.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-rmt.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpistd5r5z.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-sigmadelta.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpc7pylylf.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-spi.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpzk0i73a3.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-time.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp7ldc2891.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-timer.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpkgbx7cfr.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-tinyusb.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpp8z6fpzz.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-touch.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpnmdalrmd.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\esp32-hal-uart.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpb8d8hvg4.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\firmware_msc_fat.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpps1j4b0w.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\libb64\\cdecode.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp9ehksjdt.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\libb64\\cencode.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmph4z5png5.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\main.cpp" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp0yarrh26.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\stdlib_noniso.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmppmwrvqf5.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\wiring_pulse.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-gcc.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmp3maj5eaj.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "C:\\Users\\rakoc\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32\\wiring_shift.c" + }, + { + "command": "C:\\Users\\rakoc\\.platformio\\packages\\toolchain-xtensa-esp32\\bin\\xtensa-esp32-elf-g++.exe @D:\\vscode\\vibrator\\Vibrator\\.pio\\build\\heltec_wifi_lora_32_V2\\tmpvwga35ng.tmp", + "directory": "D:\\vscode\\vibrator\\Vibrator", + "file": "D:\\vscode\\vibrator\\Vibrator\\src\\main.cpp" + } +] \ No newline at end of file diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..c07d713 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,22 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +monitor_speed = 115200 +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = + sparkfun/SparkFun u-blox GNSS Arduino Library@^2.2.13 + adafruit/Adafruit SSD1306@^2.5.7 + adafruit/Adafruit GFX Library@^1.11.3 + greiman/SdFat@^2.2.0 + jchristensen/JC_Button@^2.1.2 + fbiego/ESP32Time@^2.0.0 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e0bd026 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,2940 @@ +#include +#include +#include +#include +#include +#include + +const int FIRMWARE_VERSION_MAJOR = 2; +const int FIRMWARE_VERSION_MINOR = 4; + +#define COMPILE_WIFI //Comment out to remove WiFi functionality +#define COMPILE_AP //Requires WiFi. Comment out to remove Access Point functionality +#define COMPILE_ESPNOW //Requires WiFi. Comment out to remove ESP-Now functionality. +#define COMPILE_BT //Comment out to remove Bluetooth functionality +#define COMPILE_L_BAND //Comment out to remove L-Band functionality +//#define ENABLE_DEVELOPER //Uncomment this line to enable special developer modes (don't check power button at startup) + +//Define the RTK board identifier: +// This is an int which is unique to this variant of the RTK Surveyor hardware which allows us +// to make sure that the settings stored in flash (LittleFS) are correct for this version of the RTK +// (sizeOfSettings is not necessarily unique and we want to avoid problems when swapping from one variant to another) +// It is the sum of: +// the major firmware version * 0x10 +// the minor firmware version +#define RTK_IDENTIFIER (FIRMWARE_VERSION_MAJOR * 0x10 + FIRMWARE_VERSION_MINOR) + +#include + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +//LittleFS for storing settings for different user profiles +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +#include +#include +#include +#include "SdFat.h" //http://librarymanager/All#sdfat_exfat by Bill Greiman. Currently uses v2.1.1 + +void beginI2C(); +void beginBoard(); +void beginSD(); +void beginFS(); +void beginGNSS(); +void beginUART2(); +void configureGNSS(); +void i2cScan(TwoWire *wire, char channel); +void printZEDInfo(); +void storePVTdata(UBX_NAV_PVT_data_t *ubxDataStruct); +void storeHPdata(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct); +bool getPortSettings(uint8_t portID); +bool disableNMEASentences(uint8_t portType); +uint32_t getSerialRate(uint8_t portID); +bool configureMessageRate(uint8_t portID, ubxMsg localMessage); +bool configureUbloxModule(); +bool configureConstellations(); +uint8_t getNMEASettings(uint8_t msgID, uint8_t portID); +uint8_t getMessageRate(uint8_t msgClass, uint8_t msgID, uint8_t portID); +bool setConstellation(uint8_t constellation, bool enable); +bool getConstellation(uint8_t constellationID); +bool configureGNSSMessageRates(uint8_t portType, ubxMsg *localMessage); +void pinUART2Task( void *pvParameters ); +void powerDown(bool displayInfo); +void powerOnCheck(); +void setMuxport(int channelNumber); +bool sdPresent(void); +void deselectCard(void); +byte sdSendCommand(byte command, unsigned long arg); +void selectCard(void); +byte xchg(byte val); +bool createTestFile(); +void displaySDFail(uint16_t displayTime); +void displayMessage(const char *buf); +void reportHeapNow(); +void startUART2Tasks(); +void stopUART2Tasks(); +void F9PSerialReadTask(void *e); +void F9PSerialWriteTask(void *e); +int bluetoothWriteBytes(const uint8_t * buffer, int length); +void bluetoothTest(bool runTest); +void bluetoothStart(); +void endSD(bool alreadyHaveSemaphore, bool releaseSemaphore); +void recordSystemSettingsToFileSD(char *fileName); +void updateRTC(); +void updateDataFileCreate(SdFile *dataFile); +void recordSystemSettingsToFileLFS(char *fileName); +void updateDataFileAccess(SdFile *dataFile); +void recordSystemSettingsToFile(File * settingsFile); +void setSettingsFileName(); +void beginLogging(const char *customFileName); +void beginLogging(); +void endLogging(bool gotSemaphore, bool releaseSemaphore); +bool findLastLog(char *lastLogName); +void createNMEASentence(customNmeaType_e textID, char *nmeaMessage, char *textMessage); +void updateLogs(); +void setLoggingType(); +uint8_t getActiveMessageCount(); +uint8_t getMessageRateByName(const char *msgName); + +//Handy library for setting ESP32 system time to GNSS time +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +#include //http://librarymanager/All#ESP32Time +ESP32Time rtc; + +//Define commands for the SD card +#define SD_GO_IDLE (0x40 + 0) // CMD0 - go to idle state +#define SD_INIT (0x40 + 1) // CMD1 - start initialization +#define SD_SEND_IF_COND (0x40 + 8) // CMD8 - send interface (conditional), works for SDHC only +#define SD_SEND_STATUS (0x40 + 13) // CMD13 - send card status +#define SD_SET_BLK_LEN (0x40 + 16) // CMD16 - set length of block in bytes +#define SD_LOCK_UNLOCK (0x40 + 42) // CMD42 - lock/unlock card +#define CMD55 (0x40 + 55) // multi-byte preface command +#define SD_READ_OCR (0x40 + 58) // read OCR +#define SD_ADV_INIT (0xc0 + 41) // ACMD41, for SDHC cards - advanced start initialization + +//Define options for accessing the SD card's PWD (CMD42) +#define MASK_ERASE 0x08 //erase the entire card +#define MASK_LOCK_UNLOCK 0x04 //lock or unlock the card with password +#define MASK_CLR_PWD 0x02 //clear password +#define MASK_SET_PWD 0x01 //set password + +//Define bit masks for fields in the lock/unlock command (CMD42) data structure +#define SET_PWD_MASK (1<<0) +#define CLR_PWD_MASK (1<<1) +#define LOCK_UNLOCK_MASK (1<<2) +#define ERASE_MASK (1<<3) + +SdFat * sd; + +SdFile * ubxFile; //File that all GNSS ubx messages sentences are written to +char settingsFileName[60]; //Contains the %s_Settings_%d.txt with current profile number set + +int profileNumber=1; +uint64_t lastLogSize = 0; +int startCurrentLogTime_minutes = 0; //Mark when we start this specific log file so we can close it after x minutes and start a new one +unsigned long lastUBXLogSyncTime = 0; //Used to record to SD every half second +uint32_t lastFileReport = 0; //When logging, print file record stats every few seconds + +uint32_t totalWriteTime = 0; //Used to calculate overall write speed using SdFat library +bool logIncreasing = false; //Goes true when log file is greater than lastLogSize +long fileSize = 0; //Size of ubx file in bytes + + +//System crashes if two tasks access a file at the same time +//So we use a semaphore to see if file system is available +SemaphoreHandle_t sdCardSemaphore; +const TickType_t fatSemaphore_shortWait_ms = 10 / portTICK_PERIOD_MS; +TickType_t loggingSemaphore_shortWait_ms = 10 / portTICK_PERIOD_MS; +const TickType_t fatSemaphore_longWait_ms = 200 / portTICK_PERIOD_MS; + +//Controls Logging Icon type +typedef enum LoggingType { + LOGGING_UNKNOWN = 0, + LOGGING_STANDARD, + LOGGING_PPP, + LOGGING_CUSTOM +} LoggingType; +LoggingType loggingType = LOGGING_UNKNOWN; + +// GNSS configuration +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#include //http://librarymanager/All#SparkFun_u-blox_GNSS + +char zedFirmwareVersion[20]; // The string looks like 'HPG 1.12'. Output to system status menu and settings file. +uint8_t zedFirmwareVersionInt = 0; // Controls which features (constellations) can be configured (v1.12 doesn't support SBAS) +uint8_t zedModuleType = PLATFORM_F9P; // Controls which messages are supported and configured +char neoFirmwareVersion[20]; //Output to system status menu. + +// Extend the class for getModuleInfo. Used to diplay ZED-F9P firmware version in debug menu. +class SFE_UBLOX_GNSS_ADD : public SFE_UBLOX_GNSS +{ +public: + boolean getModuleInfo(uint16_t maxWait = 1100); // Queries module, texts + + struct minfoStructure // Structure to hold the module info (uses 341 bytes of RAM) + { + char swVersion[30]; + char hwVersion[10]; + uint8_t extensionNo = 0; + char extension[10][30]; + } minfo; +}; + +SFE_UBLOX_GNSS_ADD i2cGNSS; + +//Used for config ZED for things not supported in library: getPortSettings, getSerialRate, getNMEASettings, getRTCMSettings +//This array holds the payload data bytes. Global so that we can use between config functions. +#ifdef MAX_PAYLOAD_SIZE +#undef MAX_PAYLOAD_SIZE +#define MAX_PAYLOAD_SIZE 384 // Override MAX_PAYLOAD_SIZE for getModuleInfo which can return up to 348 bytes +#endif +uint8_t settingPayload[MAX_PAYLOAD_SIZE]; + +//Hardware connections +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//These pins are set in beginBoard() +int pin_batteryLevelLED_Red; +int pin_batteryLevelLED_Green; +int pin_positionAccuracyLED_1cm; +int pin_positionAccuracyLED_10cm; +int pin_positionAccuracyLED_100cm; +int pin_baseStatusLED; +int pin_bluetoothStatusLED; +int pin_microSD_CS; +int pin_zed_tx_ready; +int pin_zed_reset; +int pin_batteryLevel_alert; + +int pin_muxA; +int pin_muxB; +int pin_powerSenseAndControl; +int pin_setupButton; +int pin_powerFastOff; +int pin_dac26; +int pin_adc39; +int pin_peripheralPowerControl; + +int pin_radio_rx; +int pin_radio_tx; +int pin_radio_rst; +int pin_radio_pwr; +int pin_radio_cts; +int pin_radio_rts; + +int pin_display_i2c_sda; +int pin_display_i2c_scl; +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels +#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) +#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 + +Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire1, OLED_RESET); + +#define OnboardLed 25 +#define Band 868e6 + +#define lbandMACAddress btMACAddress +uint8_t wifiMACAddress[6]; //Display this address in the system menu +uint8_t btMACAddress[6]; //Display this address when Bluetooth is enabled, otherwise display wifiMACAddress +char deviceName[70]; //The serial string that is broadcast. Ex: 'Surveyor Base-BC61' +bool inMainMenu = false; //Set true when in the serial config menu system. + +uint32_t lastHeapReport = 0; //Report heap every 1s if option enabled + +bool reuseLastLog = false; //Goes true if we have a reset due to software (rather than POR) + +//These globals are updated regularly via the storePVTdata callback +bool pvtUpdated = false; +double latitude; +double longitude; +float altitude; +float horizontalAccuracy; +bool validDate; +bool validTime; +bool confirmedDate; +bool confirmedTime; +uint8_t gnssDay; +uint8_t gnssMonth; +uint16_t gnssYear; +uint8_t gnssHour; +uint8_t gnssMinute; +uint8_t gnssSecond; +uint16_t mseconds; +uint8_t numSV; +uint8_t fixType; +uint8_t carrSoln; + +unsigned long startTime = 0; //Used for checking longest running functions +uint32_t powerPressedStartTime = 0; //Times how long user has been holding power button, used for power down +int systemTime_minutes = 0; //Used to test if logging is less than max minutes +int startLogTime_minutes = 0; //Mark when we start any logging so we can stop logging after maxLogTime_minutes +uint32_t lastRTCAttempt = 0; //Wait 1000ms between checking GNSS for current date/time + +char platformFilePrefix[40] = "SFE_Surveyor"; //Sets the prefix for logs and settings files +char platformPrefix[55] = "TiGNSSSys"; //Sets the prefix for broadcast names + +#define SERIAL_SIZE_RX (1024 * 4) //Must be large enough to handle incoming ZED UART traffic. See F9PSerialReadTask(). +TaskHandle_t F9PSerialReadTaskHandle = NULL; //Store handles so that we can kill them if user goes into WiFi NTRIP Server mode +const uint8_t F9PSerialReadTaskPriority = 1; //3 being the highest, and 0 being the lowest + +#define SERIAL_SIZE_TX (1024 * 1) +uint8_t wBuffer[SERIAL_SIZE_TX]; //Buffer for writing from incoming SPP to F9P +TaskHandle_t F9PSerialWriteTaskHandle = NULL; //Store handles so that we can kill them if user goes into WiFi NTRIP Server mode +const uint8_t F9PSerialWriteTaskPriority = 1; //3 being the highest, and 0 being the lowest + +TaskHandle_t pinUART2TaskHandle = NULL; //Dummy task to start UART2 on core 0. +volatile bool uart2pinned = false; //This variable is touched by core 0 but checked by core 1. Must be volatile. + +//Reduced stack size from 10,000 to 2,000 to make room for WiFi/NTRIP server capabilities +const int readTaskStackSize = 2500; +const int writeTaskStackSize = 2000; + +bool zedUartPassed = false; //Goes true during testing if ESP can communicate with ZED over UART + +volatile int counter = 0; +long lastTime = 0; + +#include + +// BluetoothSerial SerialBT; +BTSerialInterface *bluetoothSerial; +static volatile byte bluetoothState = BT_OFF; + +bool bluetoothIncomingRTCM = false; +bool bluetoothOutgoingRTCM = false; + +HardwareSerial serialGNSS(2); + +#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) +#error Bluetooth is not enabled! Please run make menuconfig to and enable it +#endif + +boolean SFE_UBLOX_GNSS_ADD::getModuleInfo(uint16_t maxWait) +{ + i2cGNSS.minfo.hwVersion[0] = 0; + i2cGNSS.minfo.swVersion[0] = 0; + for (int i = 0; i < 10; i++) + i2cGNSS.minfo.extension[i][0] = 0; + i2cGNSS.minfo.extensionNo = 0; + + // Let's create our custom packet + uint8_t customPayload[MAX_PAYLOAD_SIZE]; // This array holds the payload data bytes + + // The next line creates and initialises the packet information which wraps around the payload + ubxPacket customCfg = {0, 0, 0, 0, 0, customPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED}; + + customCfg.cls = UBX_CLASS_MON; // This is the message Class + customCfg.id = UBX_MON_VER; // This is the message ID + customCfg.len = 0; // Setting the len (length) to zero let's us poll the current settings + customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing) + + // Now let's send the command. The module info is returned in customPayload + + if (sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) + return (false); //If command send fails then bail + + // Now let's extract the module info from customPayload + + uint16_t position = 0; + for (int i = 0; i < 30; i++) + { + minfo.swVersion[i] = customPayload[position]; + position++; + } + for (int i = 0; i < 10; i++) + { + minfo.hwVersion[i] = customPayload[position]; + position++; + } + + while (customCfg.len >= position + 30) + { + for (int i = 0; i < 30; i++) + { + minfo.extension[minfo.extensionNo][i] = customPayload[position]; + position++; + } + minfo.extensionNo++; + if (minfo.extensionNo > 9) + break; + } + + return (true); //Success! +} + +void beginI2C(){ + Wire.begin(); //Start I2C on core 1 + //Wire.setClock(400000); + + //begin/end wire transmission to see if bus is responding correctly + //All good: 0ms, response 2 + //SDA/SCL shorted: 1000ms timeout, response 5 + //SCL/VCC shorted: 14ms, response 5 + //SCL/GND shorted: 1000ms, response 5 + //SDA/VCC shorted: 1000ms, reponse 5 + //SDA/GND shorted: 14ms, response 5 + Wire.beginTransmission(0x15); //Dummy address + int endValue = Wire.endTransmission(); + if (endValue == 2){ + online.i2c = true; + Serial.println("Info: I2C Bus (0) is online"); + } + else{ + Serial.println("Error: I2C Bus (0) Not Responding"); + } +} + +//Based on hardware features, determine if this is RTK Surveyor or RTK Express hardware +//Must be called after Wire.begin so that we can do I2C tests +void beginBoard() +{ + //Use ADC to check resistor divider + // int pin_adc_rtk_facet = 35; + // uint16_t idValue = analogReadMilliVolts(pin_adc_rtk_facet); + // log_d("Board ADC ID: %d", idValue); + + // if (idValue > (3300 / 2 * 0.9) && idValue < (3300 / 2 * 1.1)) + // { + productVariant = RTK_FACET; + // } + // else if (idValue > (3300 * 2 / 3 * 0.9) && idValue < (3300 * 2 / 3 * 1.1)) + // { + // productVariant = RTK_FACET_LBAND; + // } + // else if (idValue > (3300 * 3.3 / 13.3 * 0.9) && idValue < (3300 * 3.3 / 13.3 * 1.1)) + // { + // productVariant = RTK_EXPRESS; + // } + // else if (idValue > (3300 * 10 / 13.3 * 0.9) && idValue < (3300 * 10 / 13.3 * 1.1)) + // { + // productVariant = RTK_EXPRESS_PLUS; + // } + // else if (isConnected(0x19) == true) //Check for accelerometer + // { + // if (zedModuleType == PLATFORM_F9P) productVariant = RTK_EXPRESS; + // else if (zedModuleType == PLATFORM_F9R) productVariant = RTK_EXPRESS_PLUS; + // } + // else + // { + // productVariant = RTK_SURVEYOR; + // } + + //Setup hardware pins + + if (productVariant == RTK_FACET || productVariant == RTK_FACET_LBAND) + { + //v11 + pin_muxA = 2; + pin_muxB = 0; + pin_powerSenseAndControl = 13; + pin_peripheralPowerControl = 14; + pin_microSD_CS = 25; + pin_dac26 = 26; + pin_powerFastOff = 27; + pin_adc39 = 39; + + pin_radio_rx = 33; + pin_radio_tx = 32; + pin_radio_rst = 15; + pin_radio_pwr = 4; + pin_radio_cts = 5; + //pin_radio_rts = 255; //Not implemented + pin_display_i2c_scl=33; + pin_display_i2c_sda=32; + + // pinMode(pin_powerSenseAndControl, INPUT_PULLUP); + // pinMode(pin_powerFastOff, INPUT); + + // if (esp_reset_reason() == ESP_RST_POWERON) + // { + // powerOnCheck(); //Only do check if we POR start + // } + + // pinMode(pin_peripheralPowerControl, OUTPUT); + // digitalWrite(pin_peripheralPowerControl, HIGH); //Turn on SD, ZED, etc + + // setMuxport(settings.dataPortChannel); //Set mux to user's choice: NMEA, I2C, PPS, or DAC + + //CTS is active low. ESP32 pin 5 has pullup at POR. We must drive it low. + // pinMode(pin_radio_cts, OUTPUT); + // digitalWrite(pin_radio_cts, LOW); + + if (productVariant == RTK_FACET) + { + strcpy(platformFilePrefix, "SFE_Facet"); + strcpy(platformPrefix, "Facet"); + } + else if (productVariant == RTK_FACET_LBAND) + { + strcpy(platformFilePrefix, "SFE_Facet_LBand"); + strcpy(platformPrefix, "Facet L-Band"); + } + } + + Serial.printf("TiGNSS RTK %s v%d.%d-%s\r\n", platformPrefix, FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, __DATE__); + + //Get unit MAC address + esp_read_mac(wifiMACAddress, ESP_MAC_WIFI_STA); + memcpy(btMACAddress, wifiMACAddress, sizeof(wifiMACAddress)); + btMACAddress[5] += 2; //Convert MAC address to Bluetooth MAC (add 2): https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/system.html#mac-address + + //For all boards, check reset reason. If reset was due to wdt or panic, append last log + //TODO implement loadSettingPartial function + // loadSettingsPartial(); //Get resetCount + // if (esp_reset_reason() == ESP_RST_POWERON) + // { + // reuseLastLog = false; //Start new log + // settings.resetCount = 0; + // } + // else + // { + // reuseLastLog = true; //Attempt to reuse previous log + // settings.resetCount++; + + // Serial.print("Reset reason: "); + // switch (esp_reset_reason()) + // { + // case ESP_RST_UNKNOWN: Serial.println("ESP_RST_UNKNOWN"); break; + // case ESP_RST_POWERON : Serial.println("ESP_RST_POWERON"); break; + // case ESP_RST_SW : Serial.println("ESP_RST_SW"); break; + // case ESP_RST_PANIC : Serial.println("ESP_RST_PANIC"); break; + // case ESP_RST_INT_WDT : Serial.println("ESP_RST_INT_WDT"); break; + // case ESP_RST_TASK_WDT : Serial.println("ESP_RST_TASK_WDT"); break; + // case ESP_RST_WDT : Serial.println("ESP_RST_WDT"); break; + // case ESP_RST_DEEPSLEEP : Serial.println("ESP_RST_DEEPSLEEP"); break; + // case ESP_RST_BROWNOUT : Serial.println("ESP_RST_BROWNOUT"); break; + // case ESP_RST_SDIO : Serial.println("ESP_RST_SDIO"); break; + // default : Serial.println("Unknown"); + // } + // } + + //TODO implement recordSystemSettings function + // recordSystemSettings(); //Record resetCount to NVM +} + +void beginFS() +{ + if (online.fs == false) + { + if (LittleFS.begin(true) == false) //Format LittleFS if begin fails + { + Serial.println("Error: LittleFS not online"); + } + else + { + Serial.println("LittleFS Started"); + online.fs = true; + } + } +} + +void beginSD() +{ + bool gotSemaphore; + + online.microSD = false; + gotSemaphore = false; + while (settings.enableSD == true) + { + if (sdCardSemaphore == NULL) + { + sdCardSemaphore = xSemaphoreCreateMutex(); + } + else if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_shortWait_ms) != pdPASS) + { + // This is OK since a retry will occur next loop + log_d("sdCardSemaphore failed to yield, main.cpp line %d", __LINE__); + displayMessage("sdCardSemaphore failed to yield"); + delay(200); + break; + } + gotSemaphore = true; + displayMessage("gotSemaphore = true"); + delay(200); + + pinMode(pin_microSD_CS, OUTPUT); + digitalWrite(pin_microSD_CS, HIGH); + displayMessage("pin_microSD_CS"); + Serial.println("pin_microSD_CS"); + + // Allocate the data structure that manages the microSD card + if (!sd) + { + sd = new SdFat(); + Serial.println("sd = new SdFat();"); + if (!sd) + { + log_d("Failed to allocate the SdFat structure!"); + Serial.println("Failed to allocate the SdFat structure!"); + displayMessage("Faild to allocate the SdFat structure!"); + delay(200); + break; + } + } + + // Do a quick test to see if a card is present + int tries = 0; + int maxTries = 5; + while (tries < maxTries) + { + if (sdPresent() == true) + break; + // log_d("SD present failed. Trying again %d out of %d", tries + 1, maxTries); + + // Max power up time is 250ms: https://www.kingston.com/datasheets/SDCIT-specsheet-64gb_en.pdf + // Max current is 200mA average across 1s, peak 300mA + delay(10); + tries++; + } + + if (tries == maxTries) + return; + + // If an SD card is present, allow SdFat to take over + log_d("SD card detected"); + displayMessage("SD card detected ..."); + + if (settings.spiFrequency > 16) + { + Serial.println("Error: SPI Frequency out of range. Default to 16MHz"); + settings.spiFrequency = 16; + } + + if (sd->begin(SdSpiConfig(pin_microSD_CS, SHARED_SPI, SD_SCK_MHZ(settings.spiFrequency))) == false) + { + tries = 0; + maxTries = 1; + for (; tries < maxTries; tries++) + { + log_d("SD init failed. Trying again %d out of %d", tries + 1, maxTries); + displayMessage("SD init failed."); + + delay(250); // Give SD more time to power up, then try again + if (sd->begin(SdSpiConfig(pin_microSD_CS, SHARED_SPI, SD_SCK_MHZ(settings.spiFrequency))) == true){ + displayMessage("SD init failed (2) ..."); + break; + } + } + + if (tries == maxTries) + { + Serial.println("SD init failed. Is card present? Formatted?"); + displayMessage("SD init failed. Present? Formatted?"); + digitalWrite(pin_microSD_CS, HIGH); // Be sure SD is deselected + return; + } + } + + // Change to root directory. All new file creation will be in root. + if (sd->chdir() == false) + { + Serial.println("SD change directory failed"); + displayMessage("SD change dir failed."); + return; + } + + if (createTestFile() == false) + { + Serial.println("Failed to create test file. Format SD card with 'SD Card Formatter'."); + displaySDFail(5000); + displayMessage("Failed to create test file."); + return; + } + + // TODO implement scanForFirmware + // Load firmware file from the microSD card if it is present + // scanForFirmware(); + + Serial.println("microSD: Online"); + displayMessage("microSd: online"); + online.microSD = true; + return; + } + + //Free the semaphore + if (sdCardSemaphore && gotSemaphore) + xSemaphoreGive(sdCardSemaphore); //Make the file system available for use +} + +void endSD(bool alreadyHaveSemaphore, bool releaseSemaphore) +{ + //Disable logging + endLogging(alreadyHaveSemaphore, false); + + //Done with the SD card + if (online.microSD) + { + sd->end(); + online.microSD = false; + Serial.println("microSD: Offline"); + } + + //Free the caches for the microSD card + if (sd) + { + delete sd; + sd = NULL; + } + + //Release the semaphore + if (releaseSemaphore) + xSemaphoreGive(sdCardSemaphore); +} + +//Stop writing to the log file on the microSD card +void endLogging(bool gotSemaphore, bool releaseSemaphore) +{ + if (online.logging == true) + { + //Attempt to write to file system. This avoids collisions with file writing from other functions like recordSystemSettingsToFile() + //Wait up to 1000ms + if (gotSemaphore + || (xSemaphoreTake(sdCardSemaphore, 1000 / portTICK_PERIOD_MS) == pdPASS)) + { + if (sdPresent()) + { + //Close down file system + ubxFile->sync(); + ubxFile->close(); + Serial.println("Log file closed"); + } + else + Serial.println("Log file - SD card not present, failed to close file"); + + //Done with the log file + delete ubxFile; + ubxFile = NULL; + online.logging = false; + + //Release the semaphore if requested + if (releaseSemaphore) + xSemaphoreGive(sdCardSemaphore); + } //End sdCardSemaphore + else + { + //This is OK because in the interim more data will be written to the log + //and the log file will eventually be closed by the next call in loop + log_d("sdCardSemaphore failed to yield, menuMessages.ino line %d", __LINE__); + } + } +} + +//Connect to ZED module and identify particulars +void beginGNSS() +{ + if (i2cGNSS.begin() == false) + { + log_d("GNSS Failed to begin. Trying again."); + + //Try again with power on delay + delay(1000); //Wait for ZED-F9P to power up before it can respond to ACK + if (i2cGNSS.begin() == false) + { + // displayGNSSFail(1000); + return; + } + } + + //Increase transactions to reduce transfer time + i2cGNSS.i2cTransactionSize = 128; + + //Check the firmware version of the ZED-F9P. Based on Example21_ModuleInfo. + if (i2cGNSS.getModuleInfo(1100) == true) // Try to get the module info + { + //i2cGNSS.minfo.extension[1] looks like 'FWVER=HPG 1.12' + strcpy(zedFirmwareVersion, i2cGNSS.minfo.extension[1]); + + //Remove 'FWVER='. It's extraneous and = causes settings file parsing issues + char *ptr = strstr(zedFirmwareVersion, "FWVER="); + if (ptr != NULL) + strcpy(zedFirmwareVersion, ptr + strlen("FWVER=")); + + //Convert version to a uint8_t + if (strstr(zedFirmwareVersion, "1.00") != NULL) + zedFirmwareVersionInt = 100; + else if (strstr(zedFirmwareVersion, "1.12") != NULL) + zedFirmwareVersionInt = 112; + else if (strstr(zedFirmwareVersion, "1.13") != NULL) + zedFirmwareVersionInt = 113; + else if (strstr(zedFirmwareVersion, "1.20") != NULL) //Mostly for F9R HPS 1.20, but also F9P HPG v1.20 Spartan future support + zedFirmwareVersionInt = 120; + else if (strstr(zedFirmwareVersion, "1.21") != NULL) //Future F9R HPS v1.21 + zedFirmwareVersionInt = 121; + else if (strstr(zedFirmwareVersion, "1.30") != NULL) //ZED-F9P released Dec, 2021 + zedFirmwareVersionInt = 130; + else if (strstr(zedFirmwareVersion, "1.32") != NULL) //ZED-F9P released May, 2022 + zedFirmwareVersionInt = 132; + else + { + Serial.printf("Unknown firmware version: %s\n\r", zedFirmwareVersion); + zedFirmwareVersionInt = 99; //0.99 invalid firmware version + } + + //Determine if we have a ZED-F9P (Express/Facet) or an ZED-F9R (Express Plus/Facet Plus) + if (strstr(i2cGNSS.minfo.extension[3], "ZED-F9P") != NULL) + zedModuleType = PLATFORM_F9P; + else if (strstr(i2cGNSS.minfo.extension[3], "ZED-F9R") != NULL) + zedModuleType = PLATFORM_F9R; + else + { + Serial.printf("Unknown ZED module: %s\n\r", i2cGNSS.minfo.extension[3]); + zedModuleType = PLATFORM_F9P; + } + + printZEDInfo(); //Print module type and firmware version + } + + online.gnss = true; + Serial.println(F("GNSS module connection is ok")); +} + +//We want the UART2 interrupts to be pinned to core 0 to avoid competing with I2C interrupts +//We do not start the UART2 for GNSS->BT reception here because the interrupts would be pinned to core 1 +//We instead start a task that runs on core 0, that then begins serial +//See issue: https://github.com/espressif/arduino-esp32/issues/3386 +void beginUART2() +{ + if (pinUART2TaskHandle == NULL) xTaskCreatePinnedToCore( + pinUART2Task, + "UARTStart", //Just for humans + 2000, //Stack Size + NULL, //Task input parameter + 0, // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest + &pinUART2TaskHandle, //Task handle + 0); //Core where task should run, 0=core, 1=Arduino + + while (uart2pinned == false) //Wait for task to run once + delay(1); +} + +//Assign UART2 interrupts to the core 0. See: https://github.com/espressif/arduino-esp32/issues/3386 +void pinUART2Task( void *pvParameters ) +{ + serialGNSS.setRxBufferSize(SERIAL_SIZE_RX); + serialGNSS.setTimeout(settings.serialTimeoutGNSS); + serialGNSS.begin(settings.dataPortBaud); //UART2 on pins 16/17 for SPP. The ZED-F9P will be configured to output NMEA over its UART1 at the same rate. + + uart2pinned = true; + + vTaskDelete( NULL ); //Delete task once it has run once +} + +void configureGNSS(){ + if(online.gnss==false) return; + + i2cGNSS.setAutoPVTcallbackPtr(&storePVTdata); // Enable automatic NAV PVT messages with callback to storePVTdata + i2cGNSS.setAutoHPPOSLLHcallbackPtr(&storeHPdata); // Enable automatic NAV HPPOSLLH messages with callback to storeHPdata + + //Configuring the ZED can take more than 2000ms. We save configuration to + //ZED so there is no need to update settings unless user has modified + //the settings file or internal settings. + if (settings.updateZEDSettings == false) + { + log_d("Skipping ZED configuration"); + return; + } + + bool response=configureUbloxModule(); + +} + +void setup() +{ + // put your setup code here, to run once: + Serial.begin(115200); + + setSettingsFileName(); + + beginI2C(); + i2cScan(&Wire, '1'); + + pin_display_i2c_scl=33; + pin_display_i2c_sda=32; + + Wire1.begin(pin_display_i2c_sda,pin_display_i2c_scl); + delay(10); + i2cScan(&Wire1, '2'); + delay(20); + + if(!oled.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)){ + Serial.println("No display"); + }else{ + online.display=true; + } + + if(online.display){ + oled.clearDisplay(); + oled.setTextSize(1); // Normal 1:1 pixel scale + oled.setTextColor(WHITE); // Draw white text + oled.setCursor(0,0); // Start at top-left corner + oled.println(F("GNSS is ok ...")); + oled.setCursor(0,11); // Start at top-left corner + oled.println(F("GNSS is ok ...")); + oled.setCursor(0,22); // Start at top-left corner + oled.println(F("GNSS is ok ...")); + + oled.display(); + } + + beginBoard(); + + beginGNSS(); + + beginFS(); + + beginSD(); + + beginUART2(); + + configureGNSS(); + + // SerialBT.begin("ESP32TestV2"); + // bluetoothTest(true); + + i2cGNSS.enableRTCMmessage(UBX_RTCM_1230, COM_PORT_UART2, 0); //Disable RTCM sentences + + bluetoothStart(); + startUART2Tasks(); + + // i2cGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + // i2cGNSS.i2cTransactionSize=128; + // i2cGNSS.setSerialRate(115200*4,COM_PORT_UART1); + + // i2cGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_UART1); //Several of these are on by default on ublox board so let's disable them + // i2cGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_UART1); + // i2cGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_UART1); + // i2cGNSS.disableNMEAMessage(UBX_NMEA_RMC, COM_PORT_UART1); + // i2cGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_UART1); //Only leaving GGA & VTG enabled at current navigation rate + // i2cGNSS.enableNMEAMessage(UBX_NMEA_VTG, COM_PORT_UART1); + + // i2cGNSS.setUART1Output(COM_TYPE_NMEA); //Turn off UBX and RTCM sentences on the UART1 interface + + // serialGNSS.begin(115200*4); + + // SFE_UBLOX_GNSS gnss; + // if(gnss.begin(serialGNSS)==false){ + // Serial.println(F("u-blox GNSS not detected over serial UART2.")); + // } + // else{ + // Serial.println(F("u-blox detected over ESP32 UART2 / u-blox UART1. BT connection is good.")); + // } + + updateRTC(); //The GNSS likely has time/date. Update ESP32 RTC to match. Needed for PointPerfect key expiration. + + Serial.flush(); //Complete any previous prints + + log_d("Boot time: %d", millis()); + +} + +void loop() +{ + int lastUpdateTimeOnOled=0; + if (online.gnss == true) + { + i2cGNSS.checkUblox(); //Regularly poll to get latest data and any RTCM + i2cGNSS.checkCallbacks(); //Process any callbacks: ie, eventTriggerReceived + } + + if (millis() - lastTime > 5000) + { + lastTime = millis(); // Update the timer + Serial.println(counter); + // if (SerialBT.hasClient()) + // { + // SerialBT.println(counter); + // } + counter++; + digitalWrite(OnboardLed, !digitalRead(OnboardLed)); + } + // put your main code here, to run repeatedly: + // if (serialGNSS.available()) + // { + // String data = serialGNSS.readString(); + // data.trim(); + // Serial.println(data); + // // SerialBT.println(data); + // } + + updateRTC(); + if(millis()-lastUpdateTimeOnOled>1000){ + lastUpdateTimeOnOled=millis(); + if(online.display){ + oled.clearDisplay(); + oled.setCursor(0,0); // Start at top-left corner + oled.println(rtc.getTime()); + + oled.setCursor(0,11); + oled.printf("%d bytes",fileSize); + + if(bluetoothSerial->hasClient()) + { + oled.setCursor(0,21); + oled.println("BT connected"); + } + + oled.display(); + } + } + + updateLogs(); + +} + +void printZEDInfo() +{ + if (zedModuleType == PLATFORM_F9P) + Serial.printf("ZED-F9P firmware: %s\n\r", zedFirmwareVersion); + else if (zedModuleType == PLATFORM_F9R) + Serial.printf("ZED-F9R firmware: %s\n\r", zedFirmwareVersion); + else + Serial.printf("Unknown module with firmware: %s\n\r", zedFirmwareVersion); +} + +void i2cScan(TwoWire *wire, char channel) +{ + + Serial.print("Scanning I2C addresses on channel "); + Serial.println(channel); + + byte error, address; + uint8_t nDevices = 0; // counter for found devices + + for (address = 0; address < 128; address++) + { + // The i2c_scanner uses the return value of + // the Write.endTransmission to see if + // a device did acknowledge to the address. + wire->beginTransmission(address); + error = wire->endTransmission(true); + if (error == 0) // device found with ACK + { + Serial.print("0x"); + if (address < 16) + { + Serial.print('0'); // add leading zero + } + Serial.print(address, HEX); + nDevices++; + } + else if (error == 4) // device found with error + { + Serial.print("ERR4"); + Serial.println(address, HEX); + } + else // no device found (error code 2 or 3) + { + Serial.print("...."); + } + Serial.print(' '); // spacer after every device + if ((address & 0x0f) == 0x0f) + { + Serial.println(); // line break after every 16 addresses + } + } + Serial.print("Scan completed, "); + Serial.print(nDevices); + Serial.print(" I2C device(s) found on channel "); + Serial.println(channel); +} + +//These are the callbacks that get regularly called, globals are updated +void storePVTdata(UBX_NAV_PVT_data_t *ubxDataStruct) +{ + altitude = ubxDataStruct->height / 1000.0; + + gnssDay = ubxDataStruct->day; + gnssMonth = ubxDataStruct->month; + gnssYear = ubxDataStruct->year; + + gnssHour = ubxDataStruct->hour; + gnssMinute = ubxDataStruct->min; + gnssSecond = ubxDataStruct->sec; + mseconds = ceil((ubxDataStruct->iTOW % 1000) / 10.0); //Limit to first two digits + + numSV = ubxDataStruct->numSV; + fixType = ubxDataStruct->fixType; + carrSoln = ubxDataStruct->flags.bits.carrSoln; + + validDate = ubxDataStruct->valid.bits.validDate; + validTime = ubxDataStruct->valid.bits.validTime; + confirmedDate = ubxDataStruct->flags2.bits.confirmedDate; + confirmedTime = ubxDataStruct->flags2.bits.confirmedTime; + + pvtUpdated = true; +} + +void storeHPdata(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct) +{ + horizontalAccuracy = ((float)ubxDataStruct->hAcc) / 10000.0; // Convert hAcc from mm*0.1 to m + + latitude = ((double)ubxDataStruct->lat) / 10000000.0; + latitude += ((double)ubxDataStruct->latHp) / 1000000000.0; + longitude = ((double)ubxDataStruct->lon) / 10000000.0; + longitude += ((double)ubxDataStruct->lonHp) / 1000000000.0; +} + +//Setup the u-blox module for any setup (base or rover) +//In general we check if the setting is incorrect before writing it. Otherwise, the set commands have, on rare occasion, become +//corrupt. The worst is when the I2C port gets turned off or the I2C address gets borked. +bool configureUbloxModule() +{ + if (online.gnss == false) return (false); + + boolean response = true; + int maxWait = 2000; + + //Wait for initial report from module + while (pvtUpdated == false) + { + i2cGNSS.checkUblox(); //Regularly poll to get latest data and any RTCM + i2cGNSS.checkCallbacks(); //Process any callbacks: ie, eventTriggerReceived + delay(10); + if (millis() - startTime > maxWait) + { + log_d("PVT Update failed"); + break; + } + } + + //The first thing we do is go to 1Hz to lighten any I2C traffic from a previous configuration + if (i2cGNSS.getNavigationFrequency(maxWait) != 1) + response &= i2cGNSS.setNavigationFrequency(1, maxWait); + if (response == false) + Serial.println("Set rate failed"); + + //Survey mode is only available on ZED-F9P modules + if (zedModuleType == PLATFORM_F9P) + { + response = i2cGNSS.setSurveyMode(0, 0, 0); //Disable Survey-In or Fixed Mode + if (response == false) + Serial.println("Disable TMODE3 failed"); + } + +#define OUTPUT_SETTING 14 +#define INPUT_SETTING 12 + + //UART1 will primarily be used to pass NMEA and UBX from ZED to ESP32 (eventually to cell phone) + //but the phone can also provide RTCM data and a user may want to configure the ZED over Bluetooth. + //So let's be sure to enable UBX+NMEA+RTCM on the input + getPortSettings(COM_PORT_UART1); //Load the settingPayload with this port's settings + if (settingPayload[OUTPUT_SETTING] != (COM_TYPE_NMEA | COM_TYPE_UBX | COM_TYPE_RTCM3)) + response &= i2cGNSS.setPortOutput(COM_PORT_UART1, COM_TYPE_NMEA | COM_TYPE_UBX | COM_TYPE_RTCM3); //Set the UART1 to output UBX+NMEA+RTCM + + if (settingPayload[INPUT_SETTING] != (COM_TYPE_NMEA | COM_TYPE_UBX | COM_TYPE_RTCM3)) + response &= i2cGNSS.setPortInput(COM_PORT_UART1, COM_TYPE_NMEA | COM_TYPE_UBX | COM_TYPE_RTCM3); //Set the UART1 to input UBX+NMEA+RTCM + + //Disable SPI port - This is just to remove some overhead by ZED + getPortSettings(COM_PORT_SPI); //Load the settingPayload with this port's settings + if (settingPayload[OUTPUT_SETTING] != 0) + response &= i2cGNSS.setPortOutput(COM_PORT_SPI, 0); //Disable all protocols + if (settingPayload[INPUT_SETTING] != 0) + response &= i2cGNSS.setPortInput(COM_PORT_SPI, 0); //Disable all protocols + + getPortSettings(COM_PORT_UART2); //Load the settingPayload with this port's settings + if (settingPayload[OUTPUT_SETTING] != COM_TYPE_RTCM3) + response &= i2cGNSS.setPortOutput(COM_PORT_UART2, COM_TYPE_RTCM3); //Set the UART2 to output RTCM (in case this device goes into base mode) + if (settingPayload[INPUT_SETTING] != COM_TYPE_RTCM3) + response &= i2cGNSS.setPortInput(COM_PORT_UART2, COM_TYPE_RTCM3); //Set the UART2 to input RTCM + + if (settingPayload[INPUT_SETTING] != COM_TYPE_UBX) + response &= i2cGNSS.setPortInput(COM_PORT_I2C, (COM_TYPE_NMEA | COM_TYPE_UBX | COM_TYPE_RTCM3)); //We don't want NMEA, but we will want to deliver RTCM over I2C + + //The USB port on the ZED may be used for RTCM to/from the computer (as an NTRIP caster or client) + //So let's be sure all protocols are on for the USB port + getPortSettings(COM_PORT_USB); //Load the settingPayload with this port's settings + if (settingPayload[OUTPUT_SETTING] != (COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3)) + response &= i2cGNSS.setPortOutput(COM_PORT_USB, (COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3)); //Set the USB port to everything + + if (settingPayload[INPUT_SETTING] != (COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3)) + response &= i2cGNSS.setPortInput(COM_PORT_USB, (COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3)); //Set the USB port to everything + + response &= configureConstellations(); //Enable the constellations the user has set + + response &= configureGNSSMessageRates(COM_PORT_UART1, settings.ubxMessages); //Make sure the appropriate messages are enabled + + response &= disableNMEASentences(COM_PORT_I2C); //Disable NMEA messages on all but UART1 + response &= disableNMEASentences(COM_PORT_UART2); + response &= disableNMEASentences(COM_PORT_SPI); + + if (zedModuleType == PLATFORM_F9R) + response &= i2cGNSS.setAutoESFSTATUS(true, false); //Tell the GPS to "send" each ESF Status, but do not update stale data when accessed + + if (getSerialRate(COM_PORT_UART1) != settings.dataPortBaud) + { + Serial.println("Updating UART1 rate"); + i2cGNSS.setSerialRate(settings.dataPortBaud, COM_PORT_UART1); //Defaults to 460800 to maximize message output support + } + if (getSerialRate(COM_PORT_UART2) != settings.radioPortBaud) + { + Serial.println("Updating UART2 rate"); + i2cGNSS.setSerialRate(settings.radioPortBaud, COM_PORT_UART2); //Defaults to 57600 to match SiK telemetry radio firmware default + } + + if (response == false) + { + Serial.println("Module failed initial config."); + } + + response &= i2cGNSS.saveConfiguration(); //Save the current settings to flash and BBR + if (response == false) + Serial.println("Module failed to save."); + + //Turn on/off debug messages + if (settings.enableI2Cdebug) + i2cGNSS.enableDebugging(Serial, true); //Enable only the critical debug messages over Serial + else + i2cGNSS.disableDebugging(); + + return (response); +} + +//Given a portID, load the settings associated +bool getPortSettings(uint8_t portID) +{ + ubxPacket customCfg = {0, 0, 0, 0, 0, settingPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED}; + + customCfg.cls = UBX_CLASS_CFG; // This is the message Class + customCfg.id = UBX_CFG_PRT; // This is the message ID + customCfg.len = 1; + customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing) + + uint16_t maxWait = 250; // Wait for up to 250ms (Serial may need a lot longer e.g. 1100) + + settingPayload[0] = portID; //Request the caller's portID from GPS module + + // Read the current setting. The results will be loaded into customCfg. + if (i2cGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + { + Serial.println("getPortSettings failed"); + return (false); + } + + return (true); +} + +//Updates the enabled constellations +bool configureConstellations() +{ + bool response = true; + + //If we have a corrupt constellation ID it can cause GNSS config to fail. + //Reset to factory defaults. + if (settings.ubxConstellations[0].gnssID == 255) + { + log_d("Constellation ID corrupt"); + //TODO implement factoryReset function + // factoryReset(); + } + + //long startTime = millis(); + for (int x = 0 ; x < MAX_CONSTELLATIONS ; x++) + { + //v1.12 ZED firmware does not allow for SBAS control + //Also, if we can't identify the version (0), skip SBAS enable + if (zedModuleType == PLATFORM_F9P && (zedFirmwareVersionInt == 112 || zedFirmwareVersionInt == 0) && x == 1) //SBAS + { + //Do nothing + } + else + { + //Standard UBX protocol method takes ~533-783ms + uint8_t currentlyEnabled = getConstellation(settings.ubxConstellations[x].gnssID); //Qeury the module for the current setting + if (currentlyEnabled != settings.ubxConstellations[x].enabled) + response &= setConstellation(settings.ubxConstellations[x].gnssID, settings.ubxConstellations[x].enabled); + } + + //Get/set val method takes ~642ms but does not work because we don't send additional sigCfg keys at same time + // uint8_t currentlyEnabled = i2cGNSS.getVal8(settings.ubxConstellations[x].configKey, VAL_LAYER_RAM, 1200); + // if (currentlyEnabled != settings.ubxConstellations[x].enabled) + // response &= i2cGNSS.setVal(settings.ubxConstellations[x].configKey, settings.ubxConstellations[x].enabled); + } + //long stopTime = millis(); + + //Serial.printf("setConstellation time delta: %ld ms\n\r", stopTime - startTime); + + return (response); +} + +//Given a payload, return the location of a given constellation +//This is needed because IMES is not currently returned in the query packet +//so QZSS and GLONAS are offset by -8 bytes. +uint8_t locateGNSSID(uint8_t *customPayload, uint8_t constellation) +{ + for (int x = 0 ; x < MAX_CONSTELLATIONS ; x++) + { + if (customPayload[4 + 8 * x] == constellation) //Test gnssid + return (4 + x * 8); + } + + Serial.print("locateGNSSID failed: "); + Serial.println(constellation); + return (0); +} + +//Returns true if constellation is enabled +bool getConstellation(uint8_t constellationID) +{ + uint8_t customPayload[MAX_PAYLOAD_SIZE]; // This array holds the payload data bytes + ubxPacket customCfg = {0, 0, 0, 0, 0, customPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED}; + + customCfg.cls = UBX_CLASS_CFG; // This is the message Class + customCfg.id = UBX_CFG_GNSS; // This is the message ID + customCfg.len = 0; // Setting the len (length) to zero lets us poll the current settings + customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing) + + uint16_t maxWait = 1250; // Wait for up to 1250ms (Serial may need a lot longer e.g. 1100) + + // Read the current setting. The results will be loaded into customCfg. + if (i2cGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + { + Serial.println("Get Constellation failed"); + return (false); + } + + if (customPayload[locateGNSSID(customPayload, constellationID) + 4] & (1 << 0)) return true; //Check if enable bit is set + return false; +} + +//The u-blox library doesn't directly support constellation control so let's do it manually +//Also allows the enable/disable of any constellation (BeiDou, Galileo, etc) +bool setConstellation(uint8_t constellation, bool enable) +{ + uint8_t customPayload[MAX_PAYLOAD_SIZE]; // This array holds the payload data bytes + ubxPacket customCfg = {0, 0, 0, 0, 0, customPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED}; + + customCfg.cls = UBX_CLASS_CFG; // This is the message Class + customCfg.id = UBX_CFG_GNSS; // This is the message ID + customCfg.len = 0; // Setting the len (length) to zero lets us poll the current settings + customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing) + + uint16_t maxWait = 1250; // Wait for up to 250ms (Serial may need a lot longer e.g. 1100) + + // Read the current setting. The results will be loaded into customCfg. + if (i2cGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + { + Serial.println("Set Constellation failed"); + return (false); + } + + if (enable) + { + if (constellation == SFE_UBLOX_GNSS_ID_GPS || constellation == SFE_UBLOX_GNSS_ID_QZSS) + { + //QZSS must follow GPS + customPayload[locateGNSSID(customPayload, SFE_UBLOX_GNSS_ID_GPS) + 4] |= (1 << 0); //Set the enable bit + customPayload[locateGNSSID(customPayload, SFE_UBLOX_GNSS_ID_QZSS) + 4] |= (1 << 0); //Set the enable bit + } + else + { + customPayload[locateGNSSID(customPayload, constellation) + 4] |= (1 << 0); //Set the enable bit + } + + //Set sigCfgMask as well + if (constellation == SFE_UBLOX_GNSS_ID_GPS || constellation == SFE_UBLOX_GNSS_ID_QZSS) + { + customPayload[locateGNSSID(customPayload, SFE_UBLOX_GNSS_ID_GPS) + 6] |= 0x11; //Enable GPS L1C/A, and L2C + + //QZSS must follow GPS + customPayload[locateGNSSID(customPayload, SFE_UBLOX_GNSS_ID_QZSS) + 6] = 0x11; //Enable QZSS L1C/A, and L2C - Follow u-center + //customPayload[locateGNSSID(customPayload, SFE_UBLOX_GNSS_ID_QZSS) + 6] = 0x15; //Enable QZSS L1C/A, L1S, and L2C + } + else if (constellation == SFE_UBLOX_GNSS_ID_SBAS) + { + customPayload[locateGNSSID(customPayload, constellation) + 6] |= 0x01; //Enable SBAS L1C/A + } + else if (constellation == SFE_UBLOX_GNSS_ID_GALILEO) + { + customPayload[locateGNSSID(customPayload, constellation) + 6] |= 0x21; //Enable Galileo E1/E5b + } + else if (constellation == SFE_UBLOX_GNSS_ID_BEIDOU) + { + customPayload[locateGNSSID(customPayload, constellation) + 6] |= 0x11; //Enable BeiDou B1I/B2I + } + else if (constellation == SFE_UBLOX_GNSS_ID_GLONASS) + { + customPayload[locateGNSSID(customPayload, constellation) + 6] |= 0x11; //Enable GLONASS L1 and L2 + } + } + else //Disable + { + //QZSS must follow GPS + if (constellation == SFE_UBLOX_GNSS_ID_GPS || constellation == SFE_UBLOX_GNSS_ID_QZSS) + { + customPayload[locateGNSSID(customPayload, SFE_UBLOX_GNSS_ID_GPS) + 4] &= ~(1 << 0); //Clear the enable bit + + customPayload[locateGNSSID(customPayload, SFE_UBLOX_GNSS_ID_QZSS) + 4] &= ~(1 << 0); //Clear the enable bit + } + else + { + customPayload[locateGNSSID(customPayload, constellation) + 4] &= ~(1 << 0); //Clear the enable bit + } + + } + + // Now we write the custom packet back again to change the setting + if (i2cGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_SENT) // This time we are only expecting an ACK + { + Serial.println("Constellation setting failed"); + return (false); + } + + return (true); +} + +//Disable all the NMEA sentences on a given com port +bool disableNMEASentences(uint8_t portType) +{ + bool response = true; + if (getNMEASettings(UBX_NMEA_GGA, portType) != 0) + response &= i2cGNSS.disableNMEAMessage(UBX_NMEA_GGA, portType); + if (getNMEASettings(UBX_NMEA_GSA, portType) != 0) + response &= i2cGNSS.disableNMEAMessage(UBX_NMEA_GSA, portType); + if (getNMEASettings(UBX_NMEA_GSV, portType) != 0) + response &= i2cGNSS.disableNMEAMessage(UBX_NMEA_GSV, portType); + if (getNMEASettings(UBX_NMEA_RMC, portType) != 0) + response &= i2cGNSS.disableNMEAMessage(UBX_NMEA_RMC, portType); + if (getNMEASettings(UBX_NMEA_GST, portType) != 0) + response &= i2cGNSS.disableNMEAMessage(UBX_NMEA_GST, portType); + if (getNMEASettings(UBX_NMEA_GLL, portType) != 0) + response &= i2cGNSS.disableNMEAMessage(UBX_NMEA_GLL, portType); + if (getNMEASettings(UBX_NMEA_VTG, portType) != 0) + response &= i2cGNSS.disableNMEAMessage(UBX_NMEA_VTG, portType); + + return (response); +} + +//Given a portID and a NMEA message type, load the settings associated +uint8_t getNMEASettings(uint8_t msgID, uint8_t portID) +{ + ubxPacket customCfg = {0, 0, 0, 0, 0, settingPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED}; + + customCfg.cls = UBX_CLASS_CFG; // This is the message Class + customCfg.id = UBX_CFG_MSG; // This is the message ID + customCfg.len = 2; + customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing) + + uint16_t maxWait = 250; // Wait for up to 250ms (Serial may need a lot longer e.g. 1100) + + settingPayload[0] = UBX_CLASS_NMEA; + settingPayload[1] = msgID; + + // Read the current setting. The results will be loaded into customCfg. + if (i2cGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + { + Serial.println("getNMEASettings failed"); + return (false); + } + + return (settingPayload[2 + portID]); //Return just the byte associated with this portID +} + +//Given a portID and a NMEA message type, load the settings associated +uint32_t getSerialRate(uint8_t portID) +{ + ubxPacket customCfg = {0, 0, 0, 0, 0, settingPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED}; + + customCfg.cls = UBX_CLASS_CFG; // This is the message Class + customCfg.id = UBX_CFG_PRT; // This is the message ID + customCfg.len = 1; + customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing) + + uint16_t maxWait = 250; // Wait for up to 250ms (Serial may need a lot longer e.g. 1100) + + settingPayload[0] = portID; + + // Read the current setting. The results will be loaded into customCfg. + if (i2cGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + { + Serial.println("getSerialRate failed"); + return (false); + } + + return (((uint32_t)settingPayload[10] << 16) | ((uint32_t)settingPayload[9] << 8) | settingPayload[8]); +} + +//Updates the message rates on the ZED-F9x for all supported messages +//Any port and messages by reference can be passed in. This allows us to modify the USB +//port settings a separate (not NVM backed) message struct for testing +bool configureGNSSMessageRates(uint8_t portType, ubxMsg *localMessage) +{ + bool response = true; + + for (int x = 0 ; x < MAX_UBX_MSG ; x++) + { + //Check to see if this ZED platform supports this message + if (settings.ubxMessages[x].supported & zedModuleType) + response &= configureMessageRate(portType, localMessage[x]); + } + + return (response); +} + +//Given a message, set the message rate on the ZED-F9P +bool configureMessageRate(uint8_t portID, ubxMsg localMessage) +{ + uint8_t currentSendRate = getMessageRate(localMessage.msgClass, localMessage.msgID, portID); //Qeury the module for the current setting + + bool response = true; + if (currentSendRate != localMessage.msgRate) + response &= i2cGNSS.configureMessage(localMessage.msgClass, localMessage.msgID, portID, localMessage.msgRate); //Update setting + return response; +} + +//Lookup the send rate for a given message+port +uint8_t getMessageRate(uint8_t msgClass, uint8_t msgID, uint8_t portID) +{ + ubxPacket customCfg = {0, 0, 0, 0, 0, settingPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED}; + + customCfg.cls = UBX_CLASS_CFG; // This is the message Class + customCfg.id = UBX_CFG_MSG; // This is the message ID + customCfg.len = 2; + customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing) + + uint16_t maxWait = 1250; // Wait for up to 1250ms (Serial may need a lot longer e.g. 1100) + + settingPayload[0] = msgClass; + settingPayload[1] = msgID; + + // Read the current setting. The results will be loaded into customCfg. + if (i2cGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + { + Serial.printf("getMessageSetting failed: Class-0x%02X ID-0x%02X\n\r", msgClass, msgID); + return (false); + } + + uint8_t sendRate = settingPayload[2 + portID]; + + return (sendRate); +} + +//User has pressed the power button to turn on the system +//Was it an accidental bump or do they really want to turn on? +//Let's make sure they continue to press for 500ms +void powerOnCheck() +{ + powerPressedStartTime = millis(); + if (digitalRead(pin_powerSenseAndControl) == LOW) + delay(500); + +#ifndef ENABLE_DEVELOPER + if (digitalRead(pin_powerSenseAndControl) != LOW) + powerDown(false); //Power button tap. Returning to off state. +#endif + + powerPressedStartTime = 0; //Reset var to return to normal 'on' state +} + +//If we have a power button tap, or if the display is not yet started (no I2C!) +//then don't display a shutdown screen +void powerDown(bool displayInfo) +{ + //Disable SD card use + //TODO implement endSD + // endSD(false, false); + + //Prevent other tasks from logging, even if access to the microSD card was denied + online.logging = false; + + if (displayInfo == true) + { + //TODO implement displayShutdown + // displayShutdown(); + delay(2000); + } + + pinMode(pin_powerSenseAndControl, OUTPUT); + digitalWrite(pin_powerSenseAndControl, LOW); + + pinMode(pin_powerFastOff, OUTPUT); + digitalWrite(pin_powerFastOff, LOW); + + while (1) + delay(1); +} + +//Set the port of the 1:4 dual channel analog mux +//This allows NMEA, I2C, PPS/Event, and ADC/DAC to be routed through data port via software select +void setMuxport(int channelNumber) +{ + if (productVariant == RTK_EXPRESS || productVariant == RTK_EXPRESS_PLUS || productVariant == RTK_FACET || productVariant == RTK_FACET_LBAND) + { + pinMode(pin_muxA, OUTPUT); + pinMode(pin_muxB, OUTPUT); + + if (channelNumber > 3) return; //Error check + + switch (channelNumber) + { + case 0: + digitalWrite(pin_muxA, LOW); + digitalWrite(pin_muxB, LOW); + break; + case 1: + digitalWrite(pin_muxA, HIGH); + digitalWrite(pin_muxB, LOW); + break; + case 2: + digitalWrite(pin_muxA, LOW); + digitalWrite(pin_muxB, HIGH); + break; + case 3: + digitalWrite(pin_muxA, HIGH); + digitalWrite(pin_muxB, HIGH); + break; + } + } +} + +//Begin initialization by sending CMD0 and waiting until SD card +//responds with In Idle Mode (0x01). If the response is not 0x01 +//within a reasonable amount of time, there is no SD card on the bus. +//Returns false if not card is detected +//Returns true if a card responds +bool sdPresent(void) +{ + byte response = 0; + + SPI.begin(); + SPI.setClockDivider(SPI_CLOCK_DIV2); + SPI.setDataMode(SPI_MODE0); + SPI.setBitOrder(MSBFIRST); + pinMode(pin_microSD_CS, OUTPUT); + + //Sending clocks while card power stabilizes... + deselectCard(); // always make sure + for (byte i = 0; i < 30; i++) // send several clocks while card power stabilizes + xchg(0xff); + + //Sending CMD0 - GO IDLE... + for (byte i = 0; i < 0x10; i++) //Attempt to go idle + { + response = sdSendCommand(SD_GO_IDLE, 0); // send CMD0 - go to idle state + if (response == 1) break; + } + if (response != 1) return (false); //Card failed to respond to idle + + return (true); +} + +//Select (enable) the SD card +void selectCard(void) +{ + digitalWrite(pin_microSD_CS, LOW); +} + +//Deselect (disable) the SD card +void deselectCard(void) +{ + digitalWrite(pin_microSD_CS, HIGH); +} + +//Exchange a byte of data with the SD card via host's SPI bus +byte xchg(byte val) +{ + byte receivedVal = SPI.transfer(val); + return receivedVal; +} + +/* + sdSendCommand send raw command to SD card, return response + + This routine accepts a single SD command and a 4-byte argument. It sends + the command plus argument, adding the appropriate CRC. It then returns + the one-byte response from the SD card. + + For advanced commands (those with a command byte having bit 7 set), this + routine automatically sends the required preface command (CMD55) before + sending the requested command. + + Upon exit, this routine returns the response byte from the SD card. + Possible responses are: + 0xff No response from card; card might actually be missing + 0x01 SD card returned 0x01, which is OK for most commands + 0x?? other responses are command-specific +*/ +byte sdSendCommand(byte command, unsigned long arg) +{ + byte response; + + if (command & 0x80) // special case, ACMD(n) is sent as CMD55 and CMDn + { + command &= 0x7f; // strip high bit for later + response = sdSendCommand(CMD55, 0); // send first part (recursion) + if (response > 1) return (response); + } + + deselectCard(); + xchg(0xFF); + selectCard(); // enable CS + xchg(0xFF); + + xchg(command | 0x40); // command always has bit 6 set! + xchg((byte)(arg >> 24)); // send data, starting with top byte + xchg((byte)(arg >> 16)); + xchg((byte)(arg >> 8)); + xchg((byte)(arg & 0xFF)); + + byte crc = 0x01; // good for most cases + if (command == SD_GO_IDLE) crc = 0x95; // this will be good enough for most commands + if (command == SD_SEND_IF_COND) crc = 0x87; // special case, have to use different CRC + xchg(crc); // send final byte + + for (int i = 0; i < 30; i++) // loop until timeout or response + { + response = xchg(0xFF); + if ((response & 0x80) == 0) break; // high bit cleared means we got a response + } + + /* + We have issued the command but the SD card is still selected. We + only deselectCard the card if the command we just sent is NOT a command + that requires additional data exchange, such as reading or writing + a block. + */ + if ((command != SD_READ_OCR) && + (command != SD_SEND_STATUS) && + (command != SD_SEND_IF_COND) && + (command != SD_LOCK_UNLOCK)) + { + deselectCard(); // all done + xchg(0xFF); // close with eight more clocks + } + + return (response); // let the caller sort it out +} + +bool createTestFile() +{ + SdFile testFile; + char testFileName[40] = "testfile.txt"; + if (sd->exists(testFileName)) + sd->remove(testFileName); + + delay(50); + + //Attempt to write to the file system + if (testFile.open(testFileName, O_CREAT | O_APPEND | O_WRITE) != true) + return (false); + + //File successfully created + testFile.close(); + // if (sd->exists(testFileName)) + // sd->remove(testFileName); + return (true); +} + +//TODO implement printTextCenter +//If the SD card is detected but is not formatted correctly, display warning +void displaySDFail(uint16_t displayTime) +{ + if (online.display == true) + { + oled.clearDisplay(); + + uint8_t fontHeight = 10; + uint8_t yPos = oled.height() / 2 - fontHeight; + + // printTextCenter("Format", yPos, QW_FONT_8X16, 1, false); //text, y, font type, kerning, inverted + // printTextCenter("SD Card", yPos + fontHeight, QW_FONT_8X16, 1, false); //text, y, font type, kerning, inverted + oled.println("Format SD Card error!"); + oled.display(); + + delay(displayTime); + } +} + +void displayMessage(const char *buf) +{ + oled.clearDisplay(); + oled.setCursor(0,0); + oled.println(buf); + oled.display(); +} + +//Call back for when BT connection event happens (connected/disconnect) +//Used for updating the bluetoothState state machine +void bluetoothCallback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { + if (event == ESP_SPP_SRV_OPEN_EVT) { + Serial.println("BT client Connected"); + bluetoothState = BT_CONNECTED; + } + + if (event == ESP_SPP_CLOSE_EVT ) { + Serial.println("BT client disconnected"); + bluetoothState = BT_NOTCONNECTED; + } +} + +//Return the Bluetooth state +byte bluetoothGetState() +{ + return bluetoothState; +} + +//Read data from the Bluetooth device +int bluetoothReadBytes(uint8_t * buffer, int length) +{ + + return bluetoothSerial->readBytes(buffer, length); + +} + +//Determine if data is available +bool bluetoothRxDataAvailable() +{ + + return bluetoothSerial->available(); +} + +//Get MAC, start radio +//Tack device's MAC address to end of friendly broadcast name +//This allows multiple units to be on at same time +void bluetoothStart() +{ + + if (bluetoothState == BT_OFF) + { + char stateName[10]; + if (systemState >= STATE_ROVER_NOT_STARTED && systemState <= STATE_ROVER_RTK_FIX) + strcpy(stateName, "Rover-"); + else if(systemState >= STATE_BASE_NOT_STARTED && systemState <= STATE_BASE_FIXED_TRANSMITTING) + strcpy(stateName, "Base-"); + + // sprintf(deviceName, "%s %s%02X%02X", platformPrefix, stateName, btMACAddress[4], btMACAddress[5]); + sprintf(deviceName, "%s %s%02X%02X", "TiGNSS", stateName, btMACAddress[4], btMACAddress[5]); + + // Select Bluetooth setup + if (settings.bluetoothRadioType == BLUETOOTH_RADIO_OFF) + return; + else if (settings.bluetoothRadioType == BLUETOOTH_RADIO_SPP) + bluetoothSerial = new BTClassicSerial(); + // else if (settings.bluetoothRadioType == BLUETOOTH_RADIO_BLE) + // bluetoothSerial = new BTLESerial(); + + if (bluetoothSerial->begin(deviceName) == false) + { + Serial.println("An error occurred initializing Bluetooth"); + + if (productVariant == RTK_SURVEYOR) + digitalWrite(pin_bluetoothStatusLED, LOW); + return; + } + + //Set PIN to 1234 so we can connect to older BT devices, but not require a PIN for modern device pairing + //See issue: https://github.com/sparkfun/SparkFun_RTK_Firmware/issues/5 + //https://github.com/espressif/esp-idf/issues/1541 + //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; + + esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_NONE; //Requires pin 1234 on old BT dongle, No prompt on new BT dongle + //esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_OUT; //Works but prompts for either pin (old) or 'Does this 6 pin appear on the device?' (new) + + esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); + + esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED; + esp_bt_pin_code_t pin_code; + pin_code[0] = '1'; + pin_code[1] = '2'; + pin_code[2] = '3'; + pin_code[3] = '4'; + esp_bt_gap_set_pin(pin_type, 4, pin_code); + //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + bluetoothSerial->register_callback(bluetoothCallback); //Controls BT Status LED on Surveyor + bluetoothSerial->setTimeout(250); + + Serial.print("Bluetooth broadcasting as: "); + Serial.println(deviceName); + + //Start task for controlling Bluetooth pair LED + // if (productVariant == RTK_SURVEYOR) + // { + // ledcWrite(ledBTChannel, 255); //Turn on BT LED + // btLEDTask.detach(); //Slow down the BT LED blinker task + // btLEDTask.attach(btLEDTaskPace2Hz, updateBTled); //Rate in seconds, callback + // } + + bluetoothState = BT_NOTCONNECTED; + reportHeapNow(); + } + +} + +//This function stops BT so that it can be restarted later +//It also releases as much system resources as possible so that WiFi/caster is more stable +void bluetoothStop() +{ + + if (bluetoothState == BT_NOTCONNECTED || bluetoothState == BT_CONNECTED) + { + bluetoothSerial->register_callback(NULL); + bluetoothSerial->flush(); //Complete any transfers + bluetoothSerial->disconnect(); //Drop any clients + bluetoothSerial->end(); //bluetoothSerial->end() will release significant RAM (~100k!) but a bluetoothSerial->start will crash. + + log_d("Bluetooth turned off"); + + bluetoothState = BT_OFF; + reportHeapNow(); + } + bluetoothIncomingRTCM = false; +} + +//Test the bidirectional communication through UART2 +void bluetoothTest(bool runTest) +{ + //Verify the ESP UART2 can communicate TX/RX to ZED UART1 + const char * bluetoothStatusText; + if (online.gnss == true) + { + if (runTest && (zedUartPassed == false)) + { + Serial.println("Start bluetooth test ..."); + stopUART2Tasks(); //Stop absoring ZED serial via task + + i2cGNSS.setSerialRate(460800, COM_PORT_UART1); //Defaults to 460800 to maximize message output support + serialGNSS.begin(460800); //UART2 on pins 16/17 for SPP. The ZED-F9P will be configured to output NMEA over its UART1 at the same rate. + + SFE_UBLOX_GNSS myGNSS; + if (myGNSS.begin(serialGNSS) == true) //begin() attempts 3 connections + { + zedUartPassed = true; + bluetoothStatusText = (settings.bluetoothRadioType == BLUETOOTH_RADIO_OFF) ? "Off" : "Online"; + } + else + bluetoothStatusText = "Offline"; + + i2cGNSS.setSerialRate(settings.dataPortBaud, COM_PORT_UART1); //Defaults to 460800 to maximize message output support + serialGNSS.begin(settings.dataPortBaud); //UART2 on pins 16/17 for SPP. The ZED-F9P will be configured to output NMEA over its UART1 at the same rate. + + startUART2Tasks(); //Return to normal operation + } + else + bluetoothStatusText = (settings.bluetoothRadioType == BLUETOOTH_RADIO_OFF) ? "Off" : "Online"; + } + else + bluetoothStatusText = "GNSS Offline"; + + //Display Bluetooth MAC address and test results + char macAddress[5]; + sprintf(macAddress, "%02X%02X", btMACAddress[4], btMACAddress[5]); + Serial.print("Bluetooth "); + if (settings.bluetoothRadioType == BLUETOOTH_RADIO_BLE) + Serial.print("Low Energy "); + Serial.print("("); + Serial.print(macAddress); + Serial.print("): "); + Serial.println(bluetoothStatusText); +} + +//Serial Read/Write tasks for the F9P must be started after BT is up and running otherwise SerialBT->available will cause reboot +void startUART2Tasks() +{ + //Start the tasks for handling incoming and outgoing BT bytes to/from ZED-F9P + if (F9PSerialReadTaskHandle == NULL) + xTaskCreate( + F9PSerialReadTask, + "F9Read", //Just for humans + readTaskStackSize, //Stack Size + NULL, //Task input parameter + F9PSerialReadTaskPriority, //Priority + &F9PSerialReadTaskHandle); //Task handle + + if (F9PSerialWriteTaskHandle == NULL) + xTaskCreate( + F9PSerialWriteTask, + "F9Write", //Just for humans + writeTaskStackSize, //Stack Size + NULL, //Task input parameter + F9PSerialWriteTaskPriority, //Priority + &F9PSerialWriteTaskHandle); //Task handle +} + +//Stop tasks - useful when running firmware update or WiFi AP is running +void stopUART2Tasks() +{ + //Delete tasks if running + if (F9PSerialReadTaskHandle != NULL) + { + vTaskDelete(F9PSerialReadTaskHandle); + F9PSerialReadTaskHandle = NULL; + } + if (F9PSerialWriteTaskHandle != NULL) + { + vTaskDelete(F9PSerialWriteTaskHandle); + F9PSerialWriteTaskHandle = NULL; + } + + //Give the other CPU time to finish + //Eliminates CPU bus hang condition + delay(100); +} + +//If the ZED has any new NMEA data, pass it out over Bluetooth +//Task for reading data from the GNSS receiver. +void F9PSerialReadTask(void *e) +{ + static uint8_t rBuffer[1024 * 6]; //Buffer for reading from F9P to SPP + static uint16_t dataHead = 0; //Head advances as data comes in from GNSS's UART + static uint16_t btTail = 0; //BT Tail advances as it is sent over BT + static uint16_t sdTail = 0; //SD Tail advances as it is recorded to SD + + int btBytesToSend; //Amount of buffered Bluetooth data + int sdBytesToRecord; //Amount of buffered microSD card logging data + int availableBufferSpace; //Distance between head and furthest away tail + + int btConnected; //Is the RTK in a state to send Bluetooth data? + int newBytesToRecord; //Size of data from GNSS + + while (true) + { + while (serialGNSS.available()) + { + if (settings.enableTaskReports == true) + Serial.printf("SerialReadTask High watermark: %d\n\r", uxTaskGetStackHighWaterMark(NULL)); + + //---------------------------------------------------------------------- + //The ESP32<->ZED-F9P serial connection is default 460,800bps to facilitate + //10Hz fix rate with PPP Logging Defaults (NMEAx5 + RXMx2) messages enabled. + //ESP32 UART2 is begun with SERIAL_SIZE_RX size buffer. The circular buffer + //is 1024*6. At approximately 46.1K characters/second, a 6144 * 2 + //byte buffer should hold 267ms worth of serial data. Assuming SD writes are + //250ms worst case, we should record incoming all data. Bluetooth congestion + //or conflicts with the SD card semaphore should clear within this time. + // + //Ring buffer empty when (dataHead == btTail) and (dataHead == sdTail) + // + // +---------+ + // | | + // | | + // | | + // | | + // +---------+ <-- dataHead, btTail, sdTail + // + //Ring buffer contains data when (dataHead != btTail) or (dataHead != sdTail) + // + // +---------+ + // | | + // | | + // | yyyyyyy | <-- dataHead + // | xxxxxxx | <-- btTail (1 byte in buffer) + // +---------+ <-- sdTail (2 bytes in buffer) + // + // +---------+ + // | yyyyyyy | <-- btTail (1 byte in buffer) + // | xxxxxxx | <-- sdTail (2 bytes in buffer) + // | | + // | | + // +---------+ <-- dataHead + // + //Maximum ring buffer fill is sizeof(rBuffer) - 1 + //---------------------------------------------------------------------- + + availableBufferSpace = sizeof(rBuffer); + + //Determine BT connection state + btConnected = (bluetoothGetState() == BT_CONNECTED) + && (systemState != STATE_BASE_TEMP_SETTLE) + && (systemState != STATE_BASE_TEMP_SURVEY_STARTED); + + //Determine the amount of Bluetooth data in the buffer + btBytesToSend = 0; + if (btConnected) + { + btBytesToSend = dataHead - btTail; + if (btBytesToSend < 0) + btBytesToSend += sizeof(rBuffer); + } + + //Determine the amount of microSD card logging data in the buffer + sdBytesToRecord = 0; + if (online.logging) + { + sdBytesToRecord = dataHead - sdTail; + if (sdBytesToRecord < 0) + sdBytesToRecord += sizeof(rBuffer); + } + + //Determine the free bytes in the buffer + if (btBytesToSend >= sdBytesToRecord) + availableBufferSpace = sizeof(rBuffer) - btBytesToSend; + else + availableBufferSpace = sizeof(rBuffer) - sdBytesToRecord; + + //Don't fill the last byte to prevent buffer overflow + if (availableBufferSpace) + availableBufferSpace -= 1; + + //Fill the buffer to the end and then start at the beginning + if ((dataHead + availableBufferSpace) >= sizeof(rBuffer)) + availableBufferSpace = sizeof(rBuffer) - dataHead; + + //If we have buffer space, read data from the GNSS into the buffer + newBytesToRecord = 0; + if (availableBufferSpace) + { + //Add new data into circular buffer in front of the head + //availableBufferSpace is already reduced to avoid buffer overflow + newBytesToRecord = serialGNSS.readBytes(&rBuffer[dataHead], availableBufferSpace); + } + + //Account for the byte read + if (newBytesToRecord > 0) + { + //Set the next fill offset + dataHead += newBytesToRecord; + if (dataHead >= sizeof(rBuffer)) + dataHead -= sizeof(rBuffer); + + //Account for the new data + if (btConnected) + btBytesToSend += newBytesToRecord; + if (online.logging) + sdBytesToRecord += newBytesToRecord; + } + + //---------------------------------------------------------------------- + //Send data over Bluetooth + //---------------------------------------------------------------------- + + //If we are actively survey-in then do not pass NMEA data from ZED to phone + if (!btConnected) + //Discard the data + btTail = dataHead; + else + { + //Reduce bytes to send if we have more to send then the end of the buffer + //We'll wrap next loop + if ((btTail + btBytesToSend) >= sizeof(rBuffer)) + btBytesToSend = sizeof(rBuffer) - btTail; + + //Push new data to BT SPP if not congested or not throttling + btBytesToSend = bluetoothWriteBytes(&rBuffer[btTail], btBytesToSend); + if (btBytesToSend > 0) + { + //If we are in base mode, assume part of the outgoing data is RTCM + if (systemState >= STATE_BASE_NOT_STARTED && systemState <= STATE_BASE_FIXED_TRANSMITTING) + bluetoothOutgoingRTCM = true; + } + else + log_w("BT failed to send"); + + + //Account for the sent or dropped data + btTail += btBytesToSend; + if (btTail >= sizeof(rBuffer)) + btTail -= sizeof(rBuffer); + } + + //---------------------------------------------------------------------- + //Log data to the SD card + //---------------------------------------------------------------------- + + //If user wants to log, record to SD + if (!online.logging) + //Discard the data + sdTail = dataHead; + else + { + //Check if we are inside the max time window for logging + if ((systemTime_minutes - startLogTime_minutes) < settings.maxLogTime_minutes) + { + //Attempt to gain access to the SD card, avoids collisions with file + //writing from other functions like recordSystemSettingsToFile() + if (xSemaphoreTake(sdCardSemaphore, loggingSemaphore_shortWait_ms) == pdPASS) + { + //Reduce bytes to send if we have more to send then the end of the buffer + //We'll wrap next loop + if ((sdTail + sdBytesToRecord) >= sizeof(rBuffer)) + sdBytesToRecord = sizeof(rBuffer) - sdTail; + + //Write the data to the file + sdBytesToRecord = ubxFile->write(&rBuffer[sdTail], sdBytesToRecord); + xSemaphoreGive(sdCardSemaphore); + + //Account for the sent data or dropped + sdTail += sdBytesToRecord; + if (sdTail >= sizeof(rBuffer)) + sdTail -= sizeof(rBuffer); + } //End sdCardSemaphore + else + { + //Retry the semaphore a little later if possible + if (sdBytesToRecord >= (sizeof(rBuffer) - 1)) + { + //Error - no more room in the buffer, drop a buffer's worth of data + sdTail = dataHead; + log_e("ERROR - sdCardSemaphore failed to yield, Tasks.ino line %d", __LINE__); + Serial.printf("ERROR - Dropped %d bytes: GNSS --> log file\r\n", sdBytesToRecord); + } + else + log_w("sdCardSemaphore failed to yield, Tasks.ino line %d", __LINE__); + } + } //End maxLogTime + } //End logging + } //End Serial.available() + + //---------------------------------------------------------------------- + //Let other tasks run, prevent watch dog timer (WDT) resets + //---------------------------------------------------------------------- + + delay(1); + taskYIELD(); + } +} + +//If the phone has any new data (NTRIP RTCM, etc), read it in over Bluetooth and pass along to ZED +//Task for writing to the GNSS receiver +void F9PSerialWriteTask(void *e) +{ + while (true) + { + //Receive RTCM corrections or UBX config messages over bluetooth and pass along to ZED + if (bluetoothGetState() == BT_CONNECTED) + { + while (bluetoothRxDataAvailable()) + { + //Pass bytes to GNSS receiver + int s = bluetoothReadBytes(wBuffer, sizeof(wBuffer)); + + //TODO - control if this RTCM source should be listened to or not + serialGNSS.write(wBuffer, s); + bluetoothIncomingRTCM = true; + if (!inMainMenu) log_d("Bluetooth received %d RTCM bytes, sent to ZED", s); + + if (settings.enableTaskReports == true) + Serial.printf("SerialWriteTask High watermark: %d\n\r", uxTaskGetStackHighWaterMark(NULL)); + } + delay(1); //Poor man's way of feeding WDT. Required to prevent Priority 1 tasks from causing WDT reset + taskYIELD(); + } + + delay(1); //Poor man's way of feeding WDT. Required to prevent Priority 1 tasks from causing WDT reset + taskYIELD(); + } +} + +//Write data to the Bluetooth device +int bluetoothWriteBytes(const uint8_t * buffer, int length) +{ + //Push new data to BT SPP + return bluetoothSerial->write(buffer, length); +} + +//If debug option is on, print available heap +void reportHeapNow() +{ + if (settings.enableHeapReport == true) + { + lastHeapReport = millis(); + Serial.printf("FreeHeap: %d / HeapLowestPoint: %d / LargestBlock: %d\n\r", ESP.getFreeHeap(), xPortGetMinimumEverFreeHeapSize(), heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); + } +} + +void updateRTC() +{ + if (online.rtc == false) + { + if (online.gnss == true) + { + if (millis() - lastRTCAttempt > 1000) + { + lastRTCAttempt = millis(); + + i2cGNSS.checkUblox(); //Regularly poll to get latest data and any RTCM + i2cGNSS.checkCallbacks(); //Process any callbacks: ie, eventTriggerReceived + + bool timeValid = false; + if (validTime == true && validDate == true) //Will pass if ZED's RTC is reporting (regardless of GNSS fix) + timeValid = true; + if (confirmedTime == true && confirmedDate == true) //Requires GNSS fix + timeValid = true; + + if (timeValid == true) + { + int hour; + int minute; + int second; + + //Get the latest time in the GNSS + i2cGNSS.checkUblox(); + + //Get the time values + hour = i2cGNSS.getHour(); //Range: 0 - 23 + minute = i2cGNSS.getMinute(); //Range: 0 - 59 + second = i2cGNSS.getSecond(); //Range: 0 - 59 + + //Perform time zone adjustment + second += settings.timeZoneSeconds; + minute += settings.timeZoneMinutes; + hour += settings.timeZoneHours; + + //Set the internal system time + //This is normally set with WiFi NTP but we will rarely have WiFi + //rtc.setTime(gnssSecond, gnssMinute, gnssHour, gnssDay, gnssMonth, gnssYear); + rtc.setTime(second, minute, hour, i2cGNSS.getDay(), i2cGNSS.getMonth(), i2cGNSS.getYear()); + + online.rtc = true; + + Serial.print("System time set to: "); + Serial.println(rtc.getDateTime(true)); + + recordSystemSettingsToFileSD(settingsFileName); //This will re-record the setting file with current date/time. + } + else + { + Serial.println("No GNSS date/time available for system RTC."); + } //End timeValid + } //End lastRTCAttempt + } //End online.gnss + } //End online.rtc +} + +//Export the current settings to a config file on SD +//We share the recording with LittleFS so this is all the semphore and SD specific handling +void recordSystemSettingsToFileSD(char *fileName) +{ + bool gotSemaphore = false; + bool wasSdCardOnline; + + //Try to gain access the SD card + wasSdCardOnline = online.microSD; + if (online.microSD != true) + beginSD(); + + while (online.microSD == true) + { + //Attempt to write to file system. This avoids collisions with file writing from other functions like updateLogs() + if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_longWait_ms) == pdPASS) + { + gotSemaphore = true; + if (sd->exists(fileName)) + sd->remove(fileName); + + SdFile settingsFile; //FAT32 + if (settingsFile.open(fileName, O_CREAT | O_APPEND | O_WRITE) == false) + { + Serial.println("Failed to create settings file"); + break; + } + + updateDataFileCreate(&settingsFile); // Update the file to create time & date + + recordSystemSettingsToFile((File *)&settingsFile); //Record all the settings via strings to file + + updateDataFileAccess(&settingsFile); // Update the file access time & date + + settingsFile.close(); + + log_d("Settings recorded to SD: %s", fileName); + } + else + { + //This is an error because the current settings no longer match the settings + //on the microSD card, and will not be restored to the expected settings! + Serial.printf("sdCardSemaphore failed to yield, NVM.ino line %d\r\n", __LINE__); + } + break; + } + + //Release access the SD card + if (online.microSD && (!wasSdCardOnline)) + endSD(gotSemaphore, true); + else if (gotSemaphore) + xSemaphoreGive(sdCardSemaphore); +} + +//Update the file create time with date and time obtained from GNSS +void updateDataFileCreate(SdFile *dataFile) +{ + if (online.rtc == true) + dataFile->timestamp(T_CREATE, rtc.getYear(), rtc.getMonth() + 1, rtc.getDay(), rtc.getHour(true), rtc.getMinute(), rtc.getSecond()); //ESP32Time returns month:0-11 +} + +//Export the current settings to a config file on SD +//We share the recording with LittleFS so this is all the semphore and SD specific handling +void recordSystemSettingsToFileLFS(char *fileName) +{ + if (online.fs == true) + { + if (LittleFS.exists(fileName)) + LittleFS.remove(fileName); + + File settingsFile = LittleFS.open(fileName, FILE_WRITE); + if (!settingsFile) + { + log_d("Failed to write to settings file %s", fileName); + } + else + { + recordSystemSettingsToFile(&settingsFile); //Record all the settings via strings to file + settingsFile.close(); + log_d("Settings recorded to LittleFS: %s", fileName); + } + } +} + + //Update the file access and write time with date and time obtained from GNSS +void updateDataFileAccess(SdFile *dataFile) +{ + if (online.rtc == true) + { + //ESP32Time returns month:0-11 + dataFile->timestamp(T_ACCESS, rtc.getYear(), rtc.getMonth() + 1, rtc.getDay(), rtc.getHour(true), rtc.getMinute(), rtc.getSecond()); + dataFile->timestamp(T_WRITE, rtc.getYear(), rtc.getMonth() + 1, rtc.getDay(), rtc.getHour(true), rtc.getMinute(), rtc.getSecond()); + } +} + +//Write the settings struct to a clear text file +void recordSystemSettingsToFile(File * settingsFile) +{ + settingsFile->printf("%s=%d\n\r", "sizeOfSettings", settings.sizeOfSettings); + settingsFile->printf("%s=%d\n\r", "rtkIdentifier", settings.rtkIdentifier); + + char firmwareVersion[30]; //v1.3 December 31 2021 + sprintf(firmwareVersion, "v%d.%d-%s", FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, __DATE__); + settingsFile->printf("%s=%s\n\r", "rtkFirmwareVersion", firmwareVersion); + + settingsFile->printf("%s=%s\n\r", "zedFirmwareVersion", zedFirmwareVersion); + if (productVariant == RTK_FACET_LBAND) + settingsFile->printf("%s=%s\n\r", "neoFirmwareVersion", neoFirmwareVersion); + settingsFile->printf("%s=%d\n\r", "printDebugMessages", settings.printDebugMessages); + settingsFile->printf("%s=%d\n\r", "enableSD", settings.enableSD); + settingsFile->printf("%s=%d\n\r", "enableDisplay", settings.enableDisplay); + settingsFile->printf("%s=%d\n\r", "maxLogTime_minutes", settings.maxLogTime_minutes); + settingsFile->printf("%s=%d\n\r", "maxLogLength_minutes", settings.maxLogLength_minutes); + settingsFile->printf("%s=%d\n\r", "observationSeconds", settings.observationSeconds); + settingsFile->printf("%s=%0.2f\n\r", "observationPositionAccuracy", settings.observationPositionAccuracy); + settingsFile->printf("%s=%d\n\r", "fixedBase", settings.fixedBase); + settingsFile->printf("%s=%d\n\r", "fixedBaseCoordinateType", settings.fixedBaseCoordinateType); + settingsFile->printf("%s=%0.3f\n\r", "fixedEcefX", settings.fixedEcefX); //-1280206.568 + settingsFile->printf("%s=%0.3f\n\r", "fixedEcefY", settings.fixedEcefY); + settingsFile->printf("%s=%0.3f\n\r", "fixedEcefZ", settings.fixedEcefZ); + settingsFile->printf("%s=%0.9f\n\r", "fixedLat", settings.fixedLat); //40.09029479 + settingsFile->printf("%s=%0.9f\n\r", "fixedLong", settings.fixedLong); + settingsFile->printf("%s=%0.4f\n\r", "fixedAltitude", settings.fixedAltitude); + settingsFile->printf("%s=%d\n\r", "dataPortBaud", settings.dataPortBaud); + settingsFile->printf("%s=%d\n\r", "radioPortBaud", settings.radioPortBaud); + settingsFile->printf("%s=%0.1f\n\r", "surveyInStartingAccuracy", settings.surveyInStartingAccuracy); + settingsFile->printf("%s=%d\n\r", "measurementRate", settings.measurementRate); + settingsFile->printf("%s=%d\n\r", "navigationRate", settings.navigationRate); + settingsFile->printf("%s=%d\n\r", "enableI2Cdebug", settings.enableI2Cdebug); + settingsFile->printf("%s=%d\n\r", "enableHeapReport", settings.enableHeapReport); + settingsFile->printf("%s=%d\n\r", "enableTaskReports", settings.enableTaskReports); + settingsFile->printf("%s=%d\n\r", "dataPortChannel", (uint8_t)settings.dataPortChannel); + settingsFile->printf("%s=%d\n\r", "spiFrequency", settings.spiFrequency); + settingsFile->printf("%s=%d\n\r", "sppRxQueueSize", settings.sppRxQueueSize); + settingsFile->printf("%s=%d\n\r", "sppTxQueueSize", settings.sppTxQueueSize); + settingsFile->printf("%s=%d\n\r", "dynamicModel", settings.dynamicModel); + settingsFile->printf("%s=%d\n\r", "lastState", settings.lastState); + settingsFile->printf("%s=%d\n\r", "enableSensorFusion", settings.enableSensorFusion); + settingsFile->printf("%s=%d\n\r", "autoIMUmountAlignment", settings.autoIMUmountAlignment); + settingsFile->printf("%s=%d\n\r", "enableResetDisplay", settings.enableResetDisplay); + settingsFile->printf("%s=%d\n\r", "enableExternalPulse", settings.enableExternalPulse); + settingsFile->printf("%s=%d\n\r", "externalPulseTimeBetweenPulse_us", settings.externalPulseTimeBetweenPulse_us); + settingsFile->printf("%s=%d\n\r", "externalPulseLength_us", settings.externalPulseLength_us); + settingsFile->printf("%s=%d\n\r", "externalPulsePolarity", settings.externalPulsePolarity); + settingsFile->printf("%s=%d\n\r", "enableExternalHardwareEventLogging", settings.enableExternalHardwareEventLogging); + settingsFile->printf("%s=%s\n\r", "profileName", settings.profileName); + settingsFile->printf("%s=%d\n\r", "enableNtripServer", settings.enableNtripServer); + settingsFile->printf("%s=%d\n\r", "ntripServer_StartAtSurveyIn", settings.ntripServer_StartAtSurveyIn); + settingsFile->printf("%s=%s\n\r", "ntripServer_CasterHost", settings.ntripServer_CasterHost); + settingsFile->printf("%s=%d\n\r", "ntripServer_CasterPort", settings.ntripServer_CasterPort); + settingsFile->printf("%s=%s\n\r", "ntripServer_CasterUser", settings.ntripServer_CasterUser); + settingsFile->printf("%s=%s\n\r", "ntripServer_CasterUserPW", settings.ntripServer_CasterUserPW); + settingsFile->printf("%s=%s\n\r", "ntripServer_MountPoint", settings.ntripServer_MountPoint); + settingsFile->printf("%s=%s\n\r", "ntripServer_MountPointPW", settings.ntripServer_MountPointPW); + settingsFile->printf("%s=%s\n\r", "ntripServer_wifiSSID", settings.ntripServer_wifiSSID); + settingsFile->printf("%s=%s\n\r", "ntripServer_wifiPW", settings.ntripServer_wifiPW); + settingsFile->printf("%s=%d\n\r", "enableNtripClient", settings.enableNtripClient); + settingsFile->printf("%s=%s\n\r", "ntripClient_CasterHost", settings.ntripClient_CasterHost); + settingsFile->printf("%s=%d\n\r", "ntripClient_CasterPort", settings.ntripClient_CasterPort); + settingsFile->printf("%s=%s\n\r", "ntripClient_CasterUser", settings.ntripClient_CasterUser); + settingsFile->printf("%s=%s\n\r", "ntripClient_CasterUserPW", settings.ntripClient_CasterUserPW); + settingsFile->printf("%s=%s\n\r", "ntripClient_MountPoint", settings.ntripClient_MountPoint); + settingsFile->printf("%s=%s\n\r", "ntripClient_MountPointPW", settings.ntripClient_MountPointPW); + settingsFile->printf("%s=%s\n\r", "ntripClient_wifiSSID", settings.ntripClient_wifiSSID); + settingsFile->printf("%s=%s\n\r", "ntripClient_wifiPW", settings.ntripClient_wifiPW); + settingsFile->printf("%s=%d\n\r", "ntripClient_TransmitGGA", settings.ntripClient_TransmitGGA); + settingsFile->printf("%s=%d\n\r", "serialTimeoutGNSS", settings.serialTimeoutGNSS); + settingsFile->printf("%s=%s\n\r", "pointPerfectDeviceProfileToken", settings.pointPerfectDeviceProfileToken); + settingsFile->printf("%s=%d\n\r", "enablePointPerfectCorrections", settings.enablePointPerfectCorrections); + settingsFile->printf("%s=%s\n\r", "home_wifiSSID", settings.home_wifiSSID); + settingsFile->printf("%s=%s\n\r", "home_wifiPW", settings.home_wifiPW); + settingsFile->printf("%s=%d\n\r", "autoKeyRenewal", settings.autoKeyRenewal); + settingsFile->printf("%s=%s\n\r", "pointPerfectClientID", settings.pointPerfectClientID); + settingsFile->printf("%s=%s\n\r", "pointPerfectBrokerHost", settings.pointPerfectBrokerHost); + settingsFile->printf("%s=%s\n\r", "pointPerfectLBandTopic", settings.pointPerfectLBandTopic); + settingsFile->printf("%s=%s\n\r", "pointPerfectCurrentKey", settings.pointPerfectCurrentKey); + settingsFile->printf("%s=%llu\n\r", "pointPerfectCurrentKeyDuration", settings.pointPerfectCurrentKeyDuration); + settingsFile->printf("%s=%llu\n\r", "pointPerfectCurrentKeyStart", settings.pointPerfectCurrentKeyStart); + settingsFile->printf("%s=%s\n\r", "pointPerfectNextKey", settings.pointPerfectNextKey); + settingsFile->printf("%s=%llu\n\r", "pointPerfectNextKeyDuration", settings.pointPerfectNextKeyDuration); + settingsFile->printf("%s=%llu\n\r", "pointPerfectNextKeyStart", settings.pointPerfectNextKeyStart); + settingsFile->printf("%s=%llu\n\r", "lastKeyAttempt", settings.lastKeyAttempt); + settingsFile->printf("%s=%d\n\r", "updateZEDSettings", settings.updateZEDSettings); + settingsFile->printf("%s=%d\n\r", "LBandFreq", settings.LBandFreq); + settingsFile->printf("%s=%d\n\r", "enableLogging", settings.enableLogging); + settingsFile->printf("%s=%d\n\r", "timeZoneHours", settings.timeZoneHours); + settingsFile->printf("%s=%d\n\r", "timeZoneMinutes", settings.timeZoneMinutes); + settingsFile->printf("%s=%d\n\r", "timeZoneSeconds", settings.timeZoneSeconds); + settingsFile->printf("%s=%d\n\r", "enablePrintWifiIpAddress", settings.enablePrintWifiIpAddress); + settingsFile->printf("%s=%d\n\r", "enablePrintState", settings.enablePrintState); + settingsFile->printf("%s=%d\n\r", "enablePrintWifiState", settings.enablePrintWifiState); + settingsFile->printf("%s=%d\n\r", "enablePrintNtripClientState", settings.enablePrintNtripClientState); + settingsFile->printf("%s=%d\n\r", "enablePrintNtripServerState", settings.enablePrintNtripServerState); + settingsFile->printf("%s=%d\n\r", "enablePrintPosition", settings.enablePrintPosition); + settingsFile->printf("%s=%d\n\r", "enableMarksFile", settings.enableMarksFile); + settingsFile->printf("%s=%d\n\r", "enablePrintBatteryMessages", settings.enablePrintBatteryMessages); + settingsFile->printf("%s=%d\n\r", "enablePrintRoverAccuracy", settings.enablePrintRoverAccuracy); + settingsFile->printf("%s=%d\n\r", "enablePrintBadMessages", settings.enablePrintBadMessages); + settingsFile->printf("%s=%d\n\r", "enablePrintLogFileMessages", settings.enablePrintLogFileMessages); + settingsFile->printf("%s=%d\n\r", "enablePrintLogFileStatus", settings.enablePrintLogFileStatus); + settingsFile->printf("%s=%d\n\r", "enablePrintRingBufferOffsets", settings.enablePrintRingBufferOffsets); + settingsFile->printf("%s=%d\n\r", "enablePrintNtripServerRtcm", settings.enablePrintNtripServerRtcm); + settingsFile->printf("%s=%d\n\r", "enablePrintNtripClientRtcm", settings.enablePrintNtripClientRtcm); + settingsFile->printf("%s=%d\n\r", "enablePrintStates", settings.enablePrintStates); + settingsFile->printf("%s=%d\n\r", "enablePrintDuplicateStates", settings.enablePrintDuplicateStates); + settingsFile->printf("%s=%d\n\r", "radioType", settings.radioType); + //Record peer MAC addresses + for (int x = 0 ; x < settings.espnowPeerCount ; x++) + { + char tempString[50]; //espnowPeers.1=B4,C1,33,42,DE,01, + sprintf(tempString, "espnowPeers.%d=%02X,%02X,%02X,%02X,%02X,%02X,", x, + settings.espnowPeers[x][0], + settings.espnowPeers[x][1], + settings.espnowPeers[x][2], + settings.espnowPeers[x][3], + settings.espnowPeers[x][4], + settings.espnowPeers[x][5] + ); + settingsFile->println(tempString); + } + settingsFile->printf("%s=%d\n\r", "espnowPeerCount", settings.espnowPeerCount); + settingsFile->printf("%s=%d\n\r", "enableRtcmMessageChecking", settings.enableRtcmMessageChecking); + settingsFile->printf("%s=%d\n\r", "bluetoothRadioType", settings.bluetoothRadioType); + + //Record constellation settings + for (int x = 0 ; x < MAX_CONSTELLATIONS ; x++) + { + char tempString[50]; //constellation.BeiDou=1 + sprintf(tempString, "constellation.%s=%d", settings.ubxConstellations[x].textName, settings.ubxConstellations[x].enabled); + settingsFile->println(tempString); + } + + //Record message settings + for (int x = 0 ; x < MAX_UBX_MSG ; x++) + { + char tempString[50]; //message.nmea_dtm.msgRate=5 + sprintf(tempString, "message.%s.msgRate=%d", settings.ubxMessages[x].msgTextName, settings.ubxMessages[x].msgRate); + settingsFile->println(tempString); + } +} +//Set the settingsFileName used many places +void setSettingsFileName() +{ + // sprintf(settingsFileName, "/%s_Settings_%d.txt", platformFilePrefix, profileNumber); + sprintf(settingsFileName, "/%s_Settings_%d.txt", "TiGNSS", profileNumber); +} + + //Creates a log if logging is enabled, and SD is detected +//Based on GPS data/time, create a log file in the format SFE_Surveyor_YYMMDD_HHMMSS.ubx +void beginLogging() +{ + beginLogging(""); +} + +void beginLogging(const char *customFileName) +{ + if (online.microSD == false) + beginSD(); + + if (online.logging == false) + { + if (online.microSD == true && settings.enableLogging == true && online.rtc == true) //We can't create a file until we have date/time + { + char fileName[66 + 6 + 40] = ""; + + if (strlen(customFileName) == 0) + { + //Generate a standard log file name + if (reuseLastLog == true) //attempt to use previous log + { + if (findLastLog(fileName) == false) + log_d("Failed to find last log. Making new one."); + else + log_d("Using last log file."); + } + + if (strlen(fileName) == 0) + { + // sprintf(fileName, "%s_%02d%02d%02d_%02d%02d%02d.ubx", //SdFat library + // platformFilePrefix, + // rtc.getYear() - 2000, rtc.getMonth() + 1, rtc.getDay(), //ESP32Time returns month:0-11 + // rtc.getHour(true), rtc.getMinute(), rtc.getSecond() //ESP32Time getHour(true) returns hour:0-23 + // ); + sprintf(fileName, "%s_%02d%02d%02d_%02d%02d%02d.ubx", //SdFat library + "TiGNSS", + rtc.getYear() - 2000, rtc.getMonth() + 1, rtc.getDay(), //ESP32Time returns month:0-11 + rtc.getHour(true), rtc.getMinute(), rtc.getSecond() //ESP32Time getHour(true) returns hour:0-23 + ); + } + } + else + { + strcpy(fileName, customFileName); + } + + //Allocate the ubxFile + if (!ubxFile) + { + ubxFile = new SdFile(); + if (!ubxFile) + { + Serial.println("Failed to allocate ubxFile!"); + online.logging = false; + return; + } + } + + //Attempt to write to file system. This avoids collisions with file writing in F9PSerialReadTask() + if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_longWait_ms) == pdPASS) + { + // O_CREAT - create the file if it does not exist + // O_APPEND - seek to the end of the file prior to each write + // O_WRITE - open for write + if (ubxFile->open(fileName, O_CREAT | O_APPEND | O_WRITE) == false) + { + Serial.printf("Failed to create GNSS UBX data file: %s\n\r", fileName); + online.logging = false; + xSemaphoreGive(sdCardSemaphore); + return; + } + + lastLogSize = 0; //Reset counter - used for displaying active logging icon + + updateDataFileCreate(ubxFile); // Update the file to create time & date + + startCurrentLogTime_minutes = millis() / 1000L / 60; //Mark now as start of logging + + //If it hasn't been done before, mark the initial start of logging for total run time + if (startLogTime_minutes == 0) startLogTime_minutes = millis() / 1000L / 60; + + //Add NMEA txt message with restart reason + // char rstReason[30]; + // switch (esp_reset_reason()) + // { + // case ESP_RST_UNKNOWN: strcpy(rstReason, "ESP_RST_UNKNOWN"); break; + // case ESP_RST_POWERON : strcpy(rstReason, "ESP_RST_POWERON"); break; + // case ESP_RST_SW : strcpy(rstReason, "ESP_RST_SW"); break; + // case ESP_RST_PANIC : strcpy(rstReason, "ESP_RST_PANIC"); break; + // case ESP_RST_INT_WDT : strcpy(rstReason, "ESP_RST_INT_WDT"); break; + // case ESP_RST_TASK_WDT : strcpy(rstReason, "ESP_RST_TASK_WDT"); break; + // case ESP_RST_WDT : strcpy(rstReason, "ESP_RST_WDT"); break; + // case ESP_RST_DEEPSLEEP : strcpy(rstReason, "ESP_RST_DEEPSLEEP"); break; + // case ESP_RST_BROWNOUT : strcpy(rstReason, "ESP_RST_BROWNOUT"); break; + // case ESP_RST_SDIO : strcpy(rstReason, "ESP_RST_SDIO"); break; + // default : strcpy(rstReason, "Unknown"); + // } + + char nmeaMessage[82]; //Max NMEA sentence length is 82 + // createNMEASentence(CUSTOM_NMEA_TYPE_RESET_REASON, nmeaMessage, rstReason); //textID, buffer, text + // ubxFile->println(nmeaMessage); + + //Record system firmware versions and info to log + + //SparkFun RTK Express v1.10-Feb 11 2022 + char firmwareVersion[30]; //v1.3 December 31 2021 + sprintf(firmwareVersion, "v%d.%d-%s", FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, __DATE__); + createNMEASentence(CUSTOM_NMEA_TYPE_SYSTEM_VERSION, nmeaMessage, firmwareVersion); //textID, buffer, text + ubxFile->println(nmeaMessage); + + //ZED-F9P firmware: HPG 1.30 + createNMEASentence(CUSTOM_NMEA_TYPE_ZED_VERSION, nmeaMessage, zedFirmwareVersion); //textID, buffer, text + ubxFile->println(nmeaMessage); + + if (reuseLastLog == true) + { + Serial.println("Appending last available log"); + } + + xSemaphoreGive(sdCardSemaphore); + } + else + { + //A retry will happen during the next loop, the log will eventually be opened + log_d("Failed to get file system lock to create GNSS UBX data file"); + online.logging = false; + return; + } + + Serial.printf("Log file created: %s\n\r", fileName); + online.logging = true; + } //online.sd, enable.logging, online.rtc + } //online.logging +} + +//Finds last log +//Returns true if succesful +bool findLastLog(char *lastLogName) +{ + bool foundAFile = false; + + if (online.microSD == true) + { + //Attempt to access file system. This avoids collisions with file writing in F9PSerialReadTask() + //Wait up to 5s, this is important + if (xSemaphoreTake(sdCardSemaphore, 5000 / portTICK_PERIOD_MS) == pdPASS) + { + //Count available binaries + SdFile tempFile; + SdFile dir; + const char* LOG_EXTENSION = "ubx"; + // const char* LOG_PREFIX = platformFilePrefix; + const char* LOG_PREFIX = "TiGNSS"; + char fname[50]; //Handle long file names + + dir.open("/"); //Open root + + while (tempFile.openNext(&dir, O_READ)) + { + if (tempFile.isFile()) + { + tempFile.getName(fname, sizeof(fname)); + + //Check for matching file name prefix and extension + if (strcmp(LOG_EXTENSION, &fname[strlen(fname) - strlen(LOG_EXTENSION)]) == 0) + { + if (strstr(fname, LOG_PREFIX) != NULL) + { + strcpy(lastLogName, fname); //Store this file as last known log file + foundAFile = true; + } + } + } + tempFile.close(); + } + + xSemaphoreGive(sdCardSemaphore); + } + else + { + //Error when a log file exists on the microSD card, data should be appended + //to the existing log file + Serial.printf("sdCardSemaphore failed to yield, menuMessages.ino line %d\r\n", __LINE__); + } + } + + return (foundAFile); +} + + //Create $GNTXT, type message complete with CRC +//https://www.nmea.org/Assets/20160520%20txt%20amendment.pdf +//Used for recording system events (boot reason, event triggers, etc) inside the log +void createNMEASentence(customNmeaType_e textID, char *nmeaMessage, char *textMessage) +{ + //Currently we don't have messages longer than 82 char max so we hardcode the sentence numbers + const uint8_t totalNumberOfSentences = 1; + const uint8_t sentenceNumber = 1; + + char nmeaTxt[200]; //Max NMEA sentence length is 82 + sprintf(nmeaTxt, "$GNTXT,%02d,%02d,%02d,%s*", totalNumberOfSentences, sentenceNumber, textID, textMessage); + + //From: http://engineeringnotes.blogspot.com/2015/02/generate-crc-for-nmea-strings-arduino.html + byte CRC = 0; // XOR chars between '$' and '*' + for (byte x = 1 ; x < strlen(nmeaTxt) - 1; x++) + CRC = CRC ^ nmeaTxt[x]; + + sprintf(nmeaMessage, "%s%02X", nmeaTxt, CRC); +} + +//Create or close files as needed (startup or as user changes settings) +//Push new data to log as needed +void updateLogs() +{ + if (online.logging == false && settings.enableLogging == true) + { + beginLogging(); + + setLoggingType(); //Determine if we are standard, PPP, or custom. Changes logging icon accordingly. + } + else if (online.logging == true && settings.enableLogging == false) + { + //Close down file + endSD(false, true); + } + else if (online.logging == true && settings.enableLogging == true && (systemTime_minutes - startCurrentLogTime_minutes) >= settings.maxLogLength_minutes) + { + // if (settings.runLogTest == false) + // endSD(false, true); //Close down file. A new one will be created at the next calling of updateLogs(). + // else if (settings.runLogTest == true) + // updateLogTest(); + } + + if (online.logging == true) + { + //Force file sync every 5000ms + if (millis() - lastUBXLogSyncTime > 5000) + { + if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_shortWait_ms) == pdPASS) + { + if (productVariant == RTK_SURVEYOR) + digitalWrite(pin_baseStatusLED, !digitalRead(pin_baseStatusLED)); //Blink LED to indicate logging activity + + long startWriteTime = micros(); + ubxFile->sync(); + long stopWriteTime = micros(); + totalWriteTime += stopWriteTime - startWriteTime; //Used to calculate overall write speed + + if (productVariant == RTK_SURVEYOR) + digitalWrite(pin_baseStatusLED, !digitalRead(pin_baseStatusLED)); //Return LED to previous state + + updateDataFileAccess(ubxFile); // Update the file access time & date + + lastUBXLogSyncTime = millis(); + xSemaphoreGive(sdCardSemaphore); + } //End sdCardSemaphore + else + { + //This is OK because in the interim more data will be written to the log + //and the log file will eventually be synced by the next call in loop + log_d("sdCardSemaphore failed to yield, RTK_Surveyor.ino line %d", __LINE__); + } + } + + //Record any pending trigger events + // if (newEventToRecord == true) + // { + // Serial.println("Recording event"); + + // //Record trigger count with Time Of Week of rising edge (ms) and Millisecond fraction of Time Of Week of rising edge (ns) + // char eventData[82]; //Max NMEA sentence length is 82 + // snprintf(eventData, sizeof(eventData), "%d,%d,%d", triggerCount, towMsR, towSubMsR); + + // char nmeaMessage[82]; //Max NMEA sentence length is 82 + // createNMEASentence(CUSTOM_NMEA_TYPE_EVENT, nmeaMessage, eventData); //textID, buffer, text + + // if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_shortWait_ms) == pdPASS) + // { + // ubxFile->println(nmeaMessage); + + // xSemaphoreGive(sdCardSemaphore); + // newEventToRecord = false; + // } + // else + // { + // //While a retry does occur during the next loop, it is possible to loose + // //trigger events if they occur too rapidly or if the log file is closed + // //before the trigger event is written! + // log_w("sdCardSemaphore failed to yield, RTK_Surveyor.ino line %d", __LINE__); + // } + // } + + //Report file sizes to show recording is working + if ((millis() - lastFileReport) > 5000) + { + //Attempt to access file system. This avoids collisions with file writing from other functions like recordSystemSettingsToFile() and F9PSerialReadTask() + if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_shortWait_ms) == pdPASS) + { + fileSize = ubxFile->fileSize(); + + xSemaphoreGive(sdCardSemaphore); + } + else + { + //This is OK because outputting this message is not critical to the RTK + //operation and the message will be output by the next call in loop + log_d("sdCardSemaphore failed to yield, RTK_Surveyor.ino line %d", __LINE__); + } + + if (fileSize > 0) + { + lastFileReport = millis(); + if (settings.enablePrintLogFileStatus) + { + Serial.printf("UBX file size: %ld", fileSize); + + if ((systemTime_minutes - startLogTime_minutes) < settings.maxLogTime_minutes) + { + //Calculate generation and write speeds every 5 seconds + uint32_t fileSizeDelta = fileSize - lastLogSize; + Serial.printf(" - Generation rate: %0.1fkB/s", fileSizeDelta / 5.0 / 1000.0); + + if (totalWriteTime > 0) + Serial.printf(" - Write speed: %0.1fkB/s", fileSizeDelta / (totalWriteTime / 1000000.0) / 1000.0); + else + Serial.printf(" - Write speed: 0.0kB/s"); + } + else + { + Serial.printf(" reached max log time %d", settings.maxLogTime_minutes); + } + + Serial.println(); + } + + totalWriteTime = 0; //Reset write time every 5s + + if (fileSize > lastLogSize) + { + lastLogSize = fileSize; + logIncreasing = true; + } + else + { + log_d("No increase in file size"); + logIncreasing = false; + } + } + } + } +} + +//Determine logging type +//If user is logging basic 5 sentences, this is 'S'tandard logging +//If user is logging 7 PPP sentences, this is 'P'PP logging +//If user has other setences turned on, it's custom logging +//This controls the type of icon displayed +void setLoggingType() +{ + loggingType = LOGGING_CUSTOM; + + int messageCount = getActiveMessageCount(); + if (messageCount == 5 || messageCount == 7) + { + if (getMessageRateByName("UBX_NMEA_GGA") > 0 + && getMessageRateByName("UBX_NMEA_GSA") > 0 + && getMessageRateByName("UBX_NMEA_GST") > 0 + && getMessageRateByName("UBX_NMEA_GSV") > 0 + && getMessageRateByName("UBX_NMEA_RMC") > 0 + ) + { + loggingType = LOGGING_STANDARD; + + if (getMessageRateByName("UBX_RXM_RAWX") > 0 && getMessageRateByName("UBX_RXM_SFRBX") > 0) + loggingType = LOGGING_PPP; + } + } +} + +//Return the number of active/enabled messages +uint8_t getActiveMessageCount() +{ + uint8_t count = 0; + for (int x = 0 ; x < MAX_UBX_MSG ; x++) + if (settings.ubxMessages[x].msgRate > 0) count++; + return (count); +} + +//Given the name of a message, find it, and return the rate +uint8_t getMessageRateByName(const char *msgName) +{ + for (int x = 0 ; x < MAX_UBX_MSG ; x++) + { + if (strcmp(settings.ubxMessages[x].msgTextName, msgName) == 0) + return (settings.ubxMessages[x].msgRate); + } + + Serial.printf("getMessageRateByName: %s not found\n\r", msgName); + return (0); +} + \ No newline at end of file diff --git a/src/mybluetooth.h b/src/mybluetooth.h new file mode 100644 index 0000000..13bbcbe --- /dev/null +++ b/src/mybluetooth.h @@ -0,0 +1,76 @@ +#include "BluetoothSerial.h" + +class BTSerialInterface +{ + public: + virtual bool begin(String deviceName) = 0; + virtual void disconnect() = 0; + virtual void end() = 0; + virtual esp_err_t register_callback(esp_spp_cb_t * callback) = 0; + virtual void setTimeout(unsigned long timeout) = 0; + + virtual int available() = 0; + virtual bool hasClient() = 0; + virtual size_t readBytes(uint8_t *buffer, size_t bufferSize) = 0; + + //virtual bool isCongested() = 0; + virtual size_t write(const uint8_t *buffer, size_t size) = 0; + virtual void flush() = 0; +}; + + +class BTClassicSerial : public virtual BTSerialInterface, public BluetoothSerial +{ + // Everything is already implemented in BluetoothSerial since the code was + // originally written using that class + public: + bool begin(String deviceName) + { + return BluetoothSerial::begin(deviceName); + } + + void disconnect() + { + BluetoothSerial::disconnect(); + } + + void end() + { + BluetoothSerial::end(); + } + + esp_err_t register_callback(esp_spp_cb_t * callback) + { + return BluetoothSerial::register_callback(callback); + } + + void setTimeout(unsigned long timeout) + { + BluetoothSerial::setTimeout(timeout); + } + + int available() + { + return BluetoothSerial::available(); + } + + bool hasClient() + { + return BluetoothSerial::hasClient(); + } + + size_t readBytes(uint8_t *buffer, size_t bufferSize) + { + return BluetoothSerial::readBytes(buffer, bufferSize); + } + + size_t write(const uint8_t *buffer, size_t size) + { + return BluetoothSerial::write(buffer, size); + } + + void flush() + { + BluetoothSerial::flush(); + } +}; diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 0000000..7caba46 --- /dev/null +++ b/src/settings.h @@ -0,0 +1,428 @@ +#include //http://librarymanager/All#SparkFun_u-blox_GNSS + +//System can enter a variety of states +//See statemachine diagram at: https://lucid.app/lucidchart/53519501-9fa5-4352-aa40-673f88ca0c9b/edit?invitationId=inv_ebd4b988-513d-4169-93fd-c291851108f8 +typedef enum +{ + STATE_ROVER_NOT_STARTED = 0, + STATE_ROVER_NO_FIX, + STATE_ROVER_FIX, + STATE_ROVER_RTK_FLOAT, + STATE_ROVER_RTK_FIX, + STATE_BASE_NOT_STARTED, + STATE_BASE_TEMP_SETTLE, //User has indicated base, but current pos accuracy is too low + STATE_BASE_TEMP_SURVEY_STARTED, + STATE_BASE_TEMP_TRANSMITTING, + STATE_BASE_FIXED_NOT_STARTED, + STATE_BASE_FIXED_TRANSMITTING, + STATE_BUBBLE_LEVEL, + STATE_MARK_EVENT, + STATE_DISPLAY_SETUP, + STATE_WIFI_CONFIG_NOT_STARTED, + STATE_WIFI_CONFIG, + STATE_TEST, + STATE_TESTING, + STATE_PROFILE, + STATE_KEYS_STARTED, + STATE_KEYS_NEEDED, + STATE_KEYS_WIFI_STARTED, + STATE_KEYS_WIFI_CONNECTED, + STATE_KEYS_WIFI_TIMEOUT, + STATE_KEYS_EXPIRED, + STATE_KEYS_DAYS_REMAINING, + STATE_KEYS_LBAND_CONFIGURE, + STATE_KEYS_LBAND_ENCRYPTED, + STATE_KEYS_PROVISION_WIFI_STARTED, + STATE_KEYS_PROVISION_WIFI_CONNECTED, + STATE_KEYS_PROVISION_WIFI_TIMEOUT, + STATE_ESPNOW_PAIRING_NOT_STARTED, + STATE_ESPNOW_PAIRING, + STATE_SHUTDOWN, +} SystemState; +volatile SystemState systemState = STATE_ROVER_NOT_STARTED; +SystemState lastSystemState = STATE_ROVER_NOT_STARTED; +SystemState requestedSystemState = STATE_ROVER_NOT_STARTED; +bool newSystemStateRequested = false; + +//The setup display can show a limited set of states +//When user pauses for X amount of time, system will enter that state +SystemState setupState = STATE_MARK_EVENT; + +typedef enum +{ + RTK_SURVEYOR = 0, + RTK_EXPRESS, + RTK_FACET, + RTK_EXPRESS_PLUS, + RTK_FACET_LBAND, +} ProductVariant; +ProductVariant productVariant = RTK_SURVEYOR; + +//Each constellation will have its config key, enable, and a visible name +typedef struct ubxConstellation +{ + uint32_t configKey; + uint8_t gnssID; + bool enabled; + char textName[30]; +} ubxConstellation; + +//Different ZED modules support different messages (F9P vs F9R vs F9T) +//Create binary packed struct for different platforms +typedef enum ubxPlatform +{ + PLATFORM_F9P = 0b0001, + PLATFORM_F9R = 0b0010, + PLATFORM_F9T = 0b0100, +} ubxPlatform; + +//Data port mux (RTK Express) can enter one of four different connections +typedef enum muxConnectionType_e +{ + MUX_UBLOX_NMEA = 0, + MUX_PPS_EVENTTRIGGER, + MUX_I2C_WT, + MUX_ADC_DAC, +} muxConnectionType_e; + +//User can enter fixed base coordinates in ECEF or degrees +typedef enum +{ + COORD_TYPE_ECEF = 0, + COORD_TYPE_GEODETIC, +} coordinateType_e; + +//User can select output pulse as either falling or rising edge +typedef enum +{ + PULSE_FALLING_EDGE = 0, + PULSE_RISING_EDGE, +} pulseEdgeType_e; + +typedef enum RadioType_e +{ + RADIO_EXTERNAL = 0, + RADIO_ESPNOW, +} RadioType_e; + +typedef enum BluetoothRadioType_e +{ + BLUETOOTH_RADIO_SPP = 0, + BLUETOOTH_RADIO_BLE, + BLUETOOTH_RADIO_OFF, +} BluetoothRadioType_e; + +enum LogTestState +{ + LOGTEST_START = 0, + LOGTEST_4HZ_5MSG_10MS, + LOGTEST_4HZ_7MSG_10MS, + LOGTEST_10HZ_5MSG_10MS, + LOGTEST_10HZ_7MSG_10MS, + LOGTEST_4HZ_5MSG_0MS, + LOGTEST_4HZ_7MSG_0MS, + LOGTEST_10HZ_5MSG_0MS, + LOGTEST_10HZ_7MSG_0MS, + LOGTEST_4HZ_5MSG_50MS, + LOGTEST_4HZ_7MSG_50MS, + LOGTEST_10HZ_5MSG_50MS, + LOGTEST_10HZ_7MSG_50MS, + + LOGTEST_END, +} ; +uint8_t logTestState = LOGTEST_END; + + +//Custom NMEA sentence types output to the log file +typedef enum +{ + CUSTOM_NMEA_TYPE_RESET_REASON = 0, + CUSTOM_NMEA_TYPE_WAYPOINT, + CUSTOM_NMEA_TYPE_EVENT, + CUSTOM_NMEA_TYPE_SYSTEM_VERSION, + CUSTOM_NMEA_TYPE_ZED_VERSION, + CUSTOM_NMEA_TYPE_STATUS, + CUSTOM_NMEA_TYPE_LOGTEST_STATUS, +} customNmeaType_e; + +//These are the allowable constellations to receive from and log (if enabled) +//Tested with u-center v21.02 +#define MAX_CONSTELLATIONS 6 //(sizeof(ubxConstellations)/sizeof(ubxConstellation)) + +//Each message will have a rate, a visible name, and a class +typedef struct ubxMsg +{ + uint32_t msgConfigKey; + uint8_t msgID; + uint8_t msgClass; + uint8_t msgRate; + char msgTextName[30]; + uint8_t supported; +} ubxMsg; + +//These are the allowable messages to broadcast and log (if enabled) +//Tested with u-center v21.02 +#define MAX_UBX_MSG (13 + 25 + 5 + 10 + 3 + 12 + 5) //(sizeof(ubxMessages)/sizeof(ubxMsg)) + + +//This is all the settings that can be set on RTK Surveyor. It's recorded to NVM and the config file. +typedef struct { + int sizeOfSettings = 0; //sizeOfSettings **must** be the first entry and must be int + int rtkIdentifier = RTK_IDENTIFIER; // rtkIdentifier **must** be the second entry + bool printDebugMessages = false; + bool enableSD = true; + bool enableDisplay = true; + int maxLogTime_minutes = 60 * 24; //Default to 24 hours + int observationSeconds = 60; //Default survey in time of 60 seconds + float observationPositionAccuracy = 5.0; //Default survey in pos accy of 5m + bool fixedBase = false; //Use survey-in by default + bool fixedBaseCoordinateType = COORD_TYPE_ECEF; + double fixedEcefX = -1280206.568; + double fixedEcefY = -4716804.403; + double fixedEcefZ = 4086665.484; + double fixedLat = 40.09029479; + double fixedLong = -105.18505761; + double fixedAltitude = 1560.089; + uint32_t dataPortBaud = 460800; //Default to 460800bps to support >10Hz update rates + uint32_t radioPortBaud = 57600; //Default to 57600bps to support connection to SiK1000 radios + float surveyInStartingAccuracy = 1.0; //Wait for 1m horizontal positional accuracy before starting survey in + uint16_t measurementRate = 250; //Elapsed ms between GNSS measurements. 25ms to 65535ms. Default 4Hz. + uint16_t navigationRate = 1; //Ratio between number of measurements and navigation solutions. Default 1 for 4Hz (with measurementRate). + bool enableI2Cdebug = false; //Turn on to display GNSS library debug messages + bool enableHeapReport = false; //Turn on to display free heap + bool enableTaskReports = false; //Turn on to display task high water marks + muxConnectionType_e dataPortChannel = MUX_UBLOX_NMEA; //Mux default to ublox UART1 + uint16_t spiFrequency = 16; //By default, use 16MHz SPI + bool enableLogging = true; //If an SD card is present, log default sentences + uint16_t sppRxQueueSize = 2048; + uint16_t sppTxQueueSize = 512; + uint8_t dynamicModel = DYN_MODEL_PORTABLE; + SystemState lastState = STATE_ROVER_NOT_STARTED; //For Express, start unit in last known state + bool enableSensorFusion = false; //If IMU is available, avoid using it unless user specifically selects automotive + bool autoIMUmountAlignment = true; //Allows unit to automatically establish device orientation in vehicle + bool enableResetDisplay = false; + uint8_t resetCount = 0; + bool enableExternalPulse = true; //Send pulse once lock is achieved + uint32_t externalPulseTimeBetweenPulse_us = 900000; //us between pulses, max of 65s + uint32_t externalPulseLength_us = 100000; //us length of pulse + pulseEdgeType_e externalPulsePolarity = PULSE_RISING_EDGE; //Pulse rises for pulse length, then falls + bool enableExternalHardwareEventLogging = false; //Log when INT/TM2 pin goes low + bool enableMarksFile = false; //Log marks to the marks file + + ubxMsg ubxMessages[MAX_UBX_MSG] = //Report rates for all known messages + { + //NMEA + {UBLOX_CFG_MSGOUT_NMEA_ID_DTM_UART1, UBX_NMEA_DTM, UBX_CLASS_NMEA, 0, "UBX_NMEA_DTM", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_NMEA_ID_GBS_UART1, UBX_NMEA_GBS, UBX_CLASS_NMEA, 0, "UBX_NMEA_GBS", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_NMEA_ID_GGA_UART1, UBX_NMEA_GGA, UBX_CLASS_NMEA, 1, "UBX_NMEA_GGA", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_NMEA_ID_GLL_UART1, UBX_NMEA_GLL, UBX_CLASS_NMEA, 0, "UBX_NMEA_GLL", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_NMEA_ID_GNS_UART1, UBX_NMEA_GNS, UBX_CLASS_NMEA, 0, "UBX_NMEA_GNS", (PLATFORM_F9P | PLATFORM_F9R)}, + + {UBLOX_CFG_MSGOUT_NMEA_ID_GRS_UART1, UBX_NMEA_GRS, UBX_CLASS_NMEA, 0, "UBX_NMEA_GRS", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_NMEA_ID_GSA_UART1, UBX_NMEA_GSA, UBX_CLASS_NMEA, 1, "UBX_NMEA_GSA", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_NMEA_ID_GST_UART1, UBX_NMEA_GST, UBX_CLASS_NMEA, 1, "UBX_NMEA_GST", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_NMEA_ID_GSV_UART1, UBX_NMEA_GSV, UBX_CLASS_NMEA, 4, "UBX_NMEA_GSV", (PLATFORM_F9P | PLATFORM_F9R)}, //Default to 1 update every 4 fixes + {UBLOX_CFG_MSGOUT_NMEA_ID_RMC_UART1, UBX_NMEA_RMC, UBX_CLASS_NMEA, 1, "UBX_NMEA_RMC", (PLATFORM_F9P | PLATFORM_F9R)}, + + {UBLOX_CFG_MSGOUT_NMEA_ID_VLW_UART1, UBX_NMEA_VLW, UBX_CLASS_NMEA, 0, "UBX_NMEA_VLW", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_NMEA_ID_VTG_UART1, UBX_NMEA_VTG, UBX_CLASS_NMEA, 0, "UBX_NMEA_VTG", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_NMEA_ID_ZDA_UART1, UBX_NMEA_ZDA, UBX_CLASS_NMEA, 0, "UBX_NMEA_ZDA", (PLATFORM_F9P | PLATFORM_F9R)}, + + //NAV + {UBLOX_CFG_MSGOUT_UBX_NAV_ATT_UART1, UBX_NAV_ATT, UBX_CLASS_NAV, 0, "UBX_NAV_ATT", (PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_CLOCK_UART1, UBX_NAV_CLOCK, UBX_CLASS_NAV, 0, "UBX_NAV_CLOCK", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_DOP_UART1, UBX_NAV_DOP, UBX_CLASS_NAV, 0, "UBX_NAV_DOP", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_EOE_UART1, UBX_NAV_EOE, UBX_CLASS_NAV, 0, "UBX_NAV_EOE", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_GEOFENCE_UART1, UBX_NAV_GEOFENCE, UBX_CLASS_NAV, 0, "UBX_NAV_GEOFENCE", (PLATFORM_F9P | PLATFORM_F9R)}, + + {UBLOX_CFG_MSGOUT_UBX_NAV_HPPOSECEF_UART1, UBX_NAV_HPPOSECEF, UBX_CLASS_NAV, 0, "UBX_NAV_HPPOSECEF", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_HPPOSLLH_UART1, UBX_NAV_HPPOSLLH, UBX_CLASS_NAV, 0, "UBX_NAV_HPPOSLLH", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_ODO_UART1, UBX_NAV_ODO, UBX_CLASS_NAV, 0, "UBX_NAV_ODO", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_ORB_UART1, UBX_NAV_ORB, UBX_CLASS_NAV, 0, "UBX_NAV_ORB", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_POSECEF_UART1, UBX_NAV_POSECEF, UBX_CLASS_NAV, 0, "UBX_NAV_POSECEF", (PLATFORM_F9P | PLATFORM_F9R)}, + + {UBLOX_CFG_MSGOUT_UBX_NAV_POSLLH_UART1, UBX_NAV_POSLLH, UBX_CLASS_NAV, 0, "UBX_NAV_POSLLH", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_PVT_UART1, UBX_NAV_PVT, UBX_CLASS_NAV, 0, "UBX_NAV_PVT", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_RELPOSNED_UART1, UBX_NAV_RELPOSNED, UBX_CLASS_NAV, 0, "UBX_NAV_RELPOSNED", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_SAT_UART1, UBX_NAV_SAT, UBX_CLASS_NAV, 0, "UBX_NAV_SAT", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_SIG_UART1, UBX_NAV_SIG, UBX_CLASS_NAV, 0, "UBX_NAV_SIG", (PLATFORM_F9P | PLATFORM_F9R)}, + + {UBLOX_CFG_MSGOUT_UBX_NAV_STATUS_UART1, UBX_NAV_STATUS, UBX_CLASS_NAV, 0, "UBX_NAV_STATUS", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_SVIN_UART1, UBX_NAV_SVIN, UBX_CLASS_NAV, 0, "UBX_NAV_SVIN", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_TIMEBDS_UART1, UBX_NAV_TIMEBDS, UBX_CLASS_NAV, 0, "UBX_NAV_TIMEBDS", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_TIMEGAL_UART1, UBX_NAV_TIMEGAL, UBX_CLASS_NAV, 0, "UBX_NAV_TIMEGAL", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_TIMEGLO_UART1, UBX_NAV_TIMEGLO, UBX_CLASS_NAV, 0, "UBX_NAV_TIMEGLO", (PLATFORM_F9P | PLATFORM_F9R)}, + + {UBLOX_CFG_MSGOUT_UBX_NAV_TIMEGPS_UART1, UBX_NAV_TIMEGPS, UBX_CLASS_NAV, 0, "UBX_NAV_TIMEGPS", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_TIMELS_UART1, UBX_NAV_TIMELS, UBX_CLASS_NAV, 0, "UBX_NAV_TIMELS", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_TIMEUTC_UART1, UBX_NAV_TIMEUTC, UBX_CLASS_NAV, 0, "UBX_NAV_TIMEUTC", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_VELECEF_UART1, UBX_NAV_VELECEF, UBX_CLASS_NAV, 0, "UBX_NAV_VELECEF", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_NAV_VELNED_UART1, UBX_NAV_VELNED, UBX_CLASS_NAV, 0, "UBX_NAV_VELNED", (PLATFORM_F9P | PLATFORM_F9R)}, + + //RXM + {UBLOX_CFG_MSGOUT_UBX_RXM_MEASX_UART1, UBX_RXM_MEASX, UBX_CLASS_RXM, 0, "UBX_RXM_MEASX", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_RXM_RAWX_UART1, UBX_RXM_RAWX, UBX_CLASS_RXM, 0, "UBX_RXM_RAWX", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_RXM_RLM_UART1, UBX_RXM_RLM, UBX_CLASS_RXM, 0, "UBX_RXM_RLM", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_RXM_RTCM_UART1, UBX_RXM_RTCM, UBX_CLASS_RXM, 0, "UBX_RXM_RTCM", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_RXM_SFRBX_UART1, UBX_RXM_SFRBX, UBX_CLASS_RXM, 0, "UBX_RXM_SFRBX", (PLATFORM_F9P | PLATFORM_F9R)}, + + //MON + {UBLOX_CFG_MSGOUT_UBX_MON_COMMS_UART1, UBX_MON_COMMS, UBX_CLASS_MON, 0, "UBX_MON_COMMS", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_MON_HW2_UART1, UBX_MON_HW2, UBX_CLASS_MON, 0, "UBX_MON_HW2", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_MON_HW3_UART1, UBX_MON_HW3, UBX_CLASS_MON, 0, "UBX_MON_HW3", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_MON_HW_UART1, UBX_MON_HW, UBX_CLASS_MON, 0, "UBX_MON_HW", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_MON_IO_UART1, UBX_MON_IO, UBX_CLASS_MON, 0, "UBX_MON_IO", (PLATFORM_F9P | PLATFORM_F9R)}, + + {UBLOX_CFG_MSGOUT_UBX_MON_MSGPP_UART1, UBX_MON_MSGPP, UBX_CLASS_MON, 0, "UBX_MON_MSGPP", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_MON_RF_UART1, UBX_MON_RF, UBX_CLASS_MON, 0, "UBX_MON_RF", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_MON_RXBUF_UART1, UBX_MON_RXBUF, UBX_CLASS_MON, 0, "UBX_MON_RXBUF", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_MON_RXR_UART1, UBX_MON_RXR, UBX_CLASS_MON, 0, "UBX_MON_RXR", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_MON_TXBUF_UART1, UBX_MON_TXBUF, UBX_CLASS_MON, 0, "UBX_MON_TXBUF", (PLATFORM_F9P | PLATFORM_F9R)}, + + //TIM + {UBLOX_CFG_MSGOUT_UBX_TIM_TM2_UART1, UBX_TIM_TM2, UBX_CLASS_TIM, 0, "UBX_TIM_TM2", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_TIM_TP_UART1, UBX_TIM_TP, UBX_CLASS_TIM, 0, "UBX_TIM_TP", (PLATFORM_F9P | PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_TIM_VRFY_UART1, UBX_TIM_VRFY, UBX_CLASS_TIM, 0, "UBX_TIM_VRFY", (PLATFORM_F9P | PLATFORM_F9R)}, + + //RTCM + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1005_UART1, UBX_RTCM_1005, UBX_RTCM_MSB, 0, "UBX_RTCM_1005", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1074_UART1, UBX_RTCM_1074, UBX_RTCM_MSB, 0, "UBX_RTCM_1074", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1077_UART1, UBX_RTCM_1077, UBX_RTCM_MSB, 0, "UBX_RTCM_1077", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1084_UART1, UBX_RTCM_1084, UBX_RTCM_MSB, 0, "UBX_RTCM_1084", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1087_UART1, UBX_RTCM_1087, UBX_RTCM_MSB, 0, "UBX_RTCM_1087", (PLATFORM_F9P)}, + + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1094_UART1, UBX_RTCM_1094, UBX_RTCM_MSB, 0, "UBX_RTCM_1094", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1097_UART1, UBX_RTCM_1097, UBX_RTCM_MSB, 0, "UBX_RTCM_1097", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1124_UART1, UBX_RTCM_1124, UBX_RTCM_MSB, 0, "UBX_RTCM_1124", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1127_UART1, UBX_RTCM_1127, UBX_RTCM_MSB, 0, "UBX_RTCM_1127", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1230_UART1, UBX_RTCM_1230, UBX_RTCM_MSB, 0, "UBX_RTCM_1230", (PLATFORM_F9P)}, + + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE4072_0_UART1, UBX_RTCM_4072_0, UBX_RTCM_MSB, 0, "UBX_RTCM_4072_0", (PLATFORM_F9P)}, + {UBLOX_CFG_MSGOUT_RTCM_3X_TYPE4072_1_UART1, UBX_RTCM_4072_1, UBX_RTCM_MSB, 0, "UBX_RTCM_4072_1", (PLATFORM_F9P)}, + + //ESF + {UBLOX_CFG_MSGOUT_UBX_ESF_MEAS_UART1, UBX_ESF_MEAS, UBX_CLASS_ESF, 0, "UBX_ESF_MEAS", (PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_ESF_RAW_UART1, UBX_ESF_RAW, UBX_CLASS_ESF, 0, "UBX_ESF_RAW", (PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_ESF_STATUS_UART1, UBX_ESF_STATUS, UBX_CLASS_ESF, 0, "UBX_ESF_STATUS", (PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_ESF_ALG_UART1, UBX_ESF_ALG, UBX_CLASS_ESF, 0, "UBX_ESF_ALG", (PLATFORM_F9R)}, + {UBLOX_CFG_MSGOUT_UBX_ESF_INS_UART1, UBX_ESF_INS, UBX_CLASS_ESF, 0, "UBX_ESF_INS", (PLATFORM_F9R)}, + }; + + //Constellations monitored/used for fix + ubxConstellation ubxConstellations[MAX_CONSTELLATIONS] = + { + {UBLOX_CFG_SIGNAL_GPS_ENA, SFE_UBLOX_GNSS_ID_GPS, true, "GPS"}, + {UBLOX_CFG_SIGNAL_SBAS_ENA, SFE_UBLOX_GNSS_ID_SBAS, true, "SBAS"}, + {UBLOX_CFG_SIGNAL_GAL_ENA, SFE_UBLOX_GNSS_ID_GALILEO, true, "Galileo"}, + {UBLOX_CFG_SIGNAL_BDS_ENA, SFE_UBLOX_GNSS_ID_BEIDOU, true, "BeiDou"}, + //{UBLOX_CFG_SIGNAL_QZSS_ENA, SFE_UBLOX_GNSS_ID_IMES, false, "IMES"}, //Not yet supported? Config key does not exist? + {UBLOX_CFG_SIGNAL_QZSS_ENA, SFE_UBLOX_GNSS_ID_QZSS, true, "QZSS"}, + {UBLOX_CFG_SIGNAL_GLO_ENA, SFE_UBLOX_GNSS_ID_GLONASS, true, "GLONASS"}, + }; + + int maxLogLength_minutes = 60 * 24; //Default to 24 hours + char profileName[50] = ""; + + //NTRIP Server + bool enableNtripServer = false; + bool ntripServer_StartAtSurveyIn = false; //true = Start WiFi instead of Bluetooth at Survey-In + char ntripServer_CasterHost[50] = "rtk2go.com"; //It's free... + uint16_t ntripServer_CasterPort = 2101; + char ntripServer_CasterUser[50] = "test@test.com"; //Some free casters require auth. User must provide their own email address to use RTK2Go + char ntripServer_CasterUserPW[50] = ""; + char ntripServer_MountPoint[50] = "bldr_dwntwn2"; //NTRIP Server + char ntripServer_MountPointPW[50] = "WR5wRo4H"; + char ntripServer_wifiSSID[50] = "TRex"; //NTRIP Server WiFi + char ntripServer_wifiPW[50] = "parachutes"; + + //NTRIP Client + bool enableNtripClient = false; + char ntripClient_CasterHost[50] = "rtk2go.com"; //It's free... + uint16_t ntripClient_CasterPort = 2101; + char ntripClient_CasterUser[50] = "test@test.com"; //Some free casters require auth. User must provide their own email address to use RTK2Go + char ntripClient_CasterUserPW[50] = ""; + char ntripClient_MountPoint[50] = "bldr_SparkFun1"; + char ntripClient_MountPointPW[50] = ""; + char ntripClient_wifiSSID[50] = "TRex"; //NTRIP Server WiFi + char ntripClient_wifiPW[50] = "parachutes"; + bool ntripClient_TransmitGGA = true; + + int16_t serialTimeoutGNSS = 1; //In ms - used during SerialGNSS.begin. Number of ms to pass of no data before hardware serial reports data available. + + char pointPerfectDeviceProfileToken[40] = ""; + bool enablePointPerfectCorrections = true; + char home_wifiSSID[50] = ""; //WiFi network to use when attempting to obtain PointPerfect keys and ThingStream provisioning + char home_wifiPW[50] = ""; + bool autoKeyRenewal = true; //Attempt to get keys if we get under 28 days from the expiration date + char pointPerfectClientID[50] = ""; + char pointPerfectBrokerHost[50] = ""; // pp.services.u-blox.com + char pointPerfectLBandTopic[20] = ""; // /pp/key/Lb + + char pointPerfectCurrentKey[33] = ""; //32 hexadecimal digits = 128 bits = 16 Bytes + uint64_t pointPerfectCurrentKeyDuration = 0; + uint64_t pointPerfectCurrentKeyStart = 0; + + char pointPerfectNextKey[33] = ""; + uint64_t pointPerfectNextKeyDuration = 0; + uint64_t pointPerfectNextKeyStart = 0; + + uint64_t lastKeyAttempt = 0; //Epoch time of last attempt at obtaining keys + bool updateZEDSettings = true; //When in doubt, update the ZED with current settings + uint32_t LBandFreq = 1556290000; //Default to US band + + //Time Zone - Default to UTC + int8_t timeZoneHours = 0; + int8_t timeZoneMinutes = 0; + int8_t timeZoneSeconds = 0; + + //Debug settings + bool enablePrintWifiIpAddress = true; + bool enablePrintState = false; + bool enablePrintWifiState = false; + bool enablePrintNtripClientState = false; + bool enablePrintNtripServerState = false; + bool enablePrintPosition = false; + bool enablePrintIdleTime = false; + bool enablePrintBatteryMessages = true; + bool enablePrintRoverAccuracy = true; + bool enablePrintBadMessages = false; + bool enablePrintLogFileMessages = false; + bool enablePrintLogFileStatus = true; + bool enablePrintRingBufferOffsets = false; + bool enablePrintNtripServerRtcm = false; + bool enablePrintNtripClientRtcm = false; + bool enablePrintStates = true; + bool enablePrintDuplicateStates = false; + RadioType_e radioType = RADIO_EXTERNAL; + uint8_t espnowPeers[5][6]; //Max of 5 peers. Contains the MAC addresses (6 bytes) of paired units + uint8_t espnowPeerCount; + bool enableRtcmMessageChecking = false; + BluetoothRadioType_e bluetoothRadioType = BLUETOOTH_RADIO_SPP; + bool runLogTest = false; //When set to true, device will create a series of test logs +} Settings; +Settings settings; + +struct struct_online { + bool microSD = false; + bool display = false; + bool gnss = false; + bool logging = false; + bool serialOutput = false; + bool fs = false; + bool rtc = false; + bool battery = false; + bool accelerometer = false; + bool ntripClient = false; + bool ntripServer = false; + bool lband = false; + bool lbandCorrections = false; + bool i2c = false; +} online; + +//Radio status LED goes from off (LED off), no connection (blinking), to connected (solid) +enum BTState +{ + BT_OFF = 0, + BT_NOTCONNECTED, + BT_CONNECTED, +}; \ No newline at end of file diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html