From e81d940e2ba00ea7e69bfbdb1c8819b19493a17b Mon Sep 17 00:00:00 2001 From: tazomatalax Date: Tue, 15 Apr 2025 12:50:56 +1200 Subject: [PATCH 1/4] Stop tracking .vscode files --- .vscode/c_cpp_properties.json | 367 ---------------------------------- .vscode/extensions.json | 10 - .vscode/launch.json | 47 ----- .vscode/settings.json | 5 - 4 files changed, 429 deletions(-) delete mode 100644 .vscode/c_cpp_properties.json delete mode 100644 .vscode/extensions.json delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/settings.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 4b00fb3..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,367 +0,0 @@ -// -// !!! WARNING !!! AUTO-GENERATED FILE! -// PLEASE DO NOT MODIFY IT AND USE "platformio.ini": -// https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags -// -{ - "configurations": [ - { - "name": "PlatformIO", - "includePath": [ - "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/include", - "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/src", - "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/lib/ModbusRTUSlave/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/FreeRTOS/src", - "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/.pio/libdeps/pico/INA226", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Wire/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/cores/rp2040", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/cores/rp2040/api/deprecated", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/cores/rp2040/api/deprecated-avr-comp", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/include/rp2040", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/include/rp2040/pico_base", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2040/hardware_regs/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2040/hardware_structs/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2040/pico_platform/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_btstack/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_cyw43_arch/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_cyw43_driver/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/cyw43-driver/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/btstack/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/btstack/3rd-party/bluedroid/decoder/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/btstack/3rd-party/bluedroid/encoder/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/btstack/platform/embedded", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Device/RP2040/Include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/tinyusb/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/boards/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/hardware_claim/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_base_headers/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_binary_info/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_sync/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_time/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_util/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_stdlib_headers/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_usb_reset_interface_headers/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/boot_bootrom_headers/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/cmsis/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Core/Include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_adc/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_base/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_boot_lock/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_clocks/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_divider/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_dma/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_exception/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_flash/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_gpio/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_i2c/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_interp/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_irq/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_rtc/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_pio/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_pll/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_pwm/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_resets/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_spi/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_sync/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_sync_spin_lock/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_timer/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_uart/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_vreg/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_watchdog/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_xosc/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_aon_timer/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_async_context/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_bootrom/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_double/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_flash/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_float/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_int64_ops/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_lwip/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_multicore/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_platform_compiler/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_platform_sections/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_platform_panic/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_printf/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_runtime/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_runtime_init/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_rand/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_stdio/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_stdio_uart/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_unique_id/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/lwip/src/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/variants/rpipico", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/ADCInput/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/ArduinoOTA/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/AsyncUDP/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/AudioBufferManager/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/BTstackLib/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/BluetoothAudio/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/BluetoothHCI/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/BluetoothHIDMaster/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/DNSServer/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/EEPROM/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/ESPHost/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/FatFS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/FatFSUSB/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HID_Bluetooth/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HID_Joystick/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HID_Keyboard/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HID_Mouse/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HTTPClient/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HTTPUpdate/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HTTPUpdateServer/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Hash/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/I2S/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Joystick/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/JoystickBLE/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/JoystickBT/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Keyboard/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/KeyboardBLE/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/KeyboardBT/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/LEAmDNS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/LittleFS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/MD5Builder/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Mouse/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/MouseAbsolute/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/MouseBLE/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/MouseBT/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/NetBIOS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/PDM/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/PWMAudio/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/PicoOTA/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SD/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SDFS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SPI/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SPISlave/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SdFat/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SerialBT/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Servo/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SimpleMDNS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SingleFileDrive/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SoftwareSPI/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Ticker/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Updater/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/VFS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/WebServer/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/WiFi/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/http-parser/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_CYW43/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_ESPHost/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_Ethernet/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_WINC1500/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_enc28j60/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_w5100/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_w5500/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_w55rp20/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_w6100/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/rp2040", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/rp2350", - "" - ], - "browse": { - "limitSymbolsToIncludedHeaders": true, - "path": [ - "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/include", - "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/src", - "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/lib/ModbusRTUSlave/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/FreeRTOS/src", - "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/.pio/libdeps/pico/INA226", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Wire/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/cores/rp2040", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/cores/rp2040/api/deprecated", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/cores/rp2040/api/deprecated-avr-comp", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/include/rp2040", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/include/rp2040/pico_base", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2040/hardware_regs/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2040/hardware_structs/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2040/pico_platform/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_btstack/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_cyw43_arch/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_cyw43_driver/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/cyw43-driver/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/btstack/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/btstack/3rd-party/bluedroid/decoder/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/btstack/3rd-party/bluedroid/encoder/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/btstack/platform/embedded", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Device/RP2040/Include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/tinyusb/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/boards/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/hardware_claim/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_base_headers/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_binary_info/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_sync/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_time/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_util/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_stdlib_headers/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/common/pico_usb_reset_interface_headers/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/boot_bootrom_headers/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/cmsis/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Core/Include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_adc/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_base/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_boot_lock/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_clocks/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_divider/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_dma/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_exception/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_flash/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_gpio/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_i2c/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_interp/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_irq/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_rtc/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_pio/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_pll/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_pwm/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_resets/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_spi/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_sync/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_sync_spin_lock/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_timer/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_uart/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_vreg/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_watchdog/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/hardware_xosc/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_aon_timer/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_async_context/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_bootrom/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_double/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_flash/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_float/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_int64_ops/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_lwip/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_multicore/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_platform_compiler/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_platform_sections/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_platform_panic/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_printf/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_runtime/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_runtime_init/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_rand/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_stdio/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_stdio_uart/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/src/rp2_common/pico_unique_id/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/pico-sdk/lib/lwip/src/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/include", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/variants/rpipico", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/ADCInput/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/ArduinoOTA/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/AsyncUDP/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/AudioBufferManager/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/BTstackLib/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/BluetoothAudio/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/BluetoothHCI/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/BluetoothHIDMaster/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/DNSServer/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/EEPROM/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/ESPHost/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/FatFS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/FatFSUSB/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HID_Bluetooth/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HID_Joystick/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HID_Keyboard/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HID_Mouse/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HTTPClient/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HTTPUpdate/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/HTTPUpdateServer/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Hash/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/I2S/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Joystick/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/JoystickBLE/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/JoystickBT/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Keyboard/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/KeyboardBLE/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/KeyboardBT/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/LEAmDNS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/LittleFS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/MD5Builder/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Mouse/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/MouseAbsolute/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/MouseBLE/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/MouseBT/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/NetBIOS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/PDM/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/PWMAudio/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/PicoOTA/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SD/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SDFS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SPI/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SPISlave/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SdFat/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SerialBT/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Servo/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SimpleMDNS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SingleFileDrive/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/SoftwareSPI/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Ticker/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/Updater/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/VFS/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/WebServer/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/WiFi/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/http-parser/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_CYW43/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_ESPHost/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_Ethernet/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_WINC1500/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_enc28j60/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_w5100/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_w5500/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_w55rp20/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/lwIP_w6100/src", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/rp2040", - "C:/Users/vanderwt/.platformio/packages/framework-arduinopico/libraries/rp2350", - "" - ] - }, - "defines": [ - "PLATFORMIO=60118", - "ARDUINO_RASPBERRY_PI_PICO", - "ARDUINO_ARCH_RP2040", - "USBD_MAX_POWER_MA=500", - "ARDUINO=10810", - "ARDUINO_ARCH_RP2040", - "F_CPU=133000000L", - "BOARD_NAME=\"pico\"", - "ARM_MATH_CM0_FAMILY", - "ARM_MATH_CM0_PLUS", - "TARGET_RP2040", - "PICO_RP2040=1", - "PICO_FLASH_SIZE_BYTES=2097152", - "FILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC", - "USE_UTF8_LONG_NAMES=1", - "DISABLE_FS_H_WARNING=1", - "CFG_TUSB_MCU=OPT_MCU_RP2040", - "USB_VID=0x2e8a", - "USB_PID=0x000a", - "USBD_VID=0x2e8a", - "USBD_PID=0x000a", - "USB_MANUFACTURER=\"Raspberry Pi\"", - "USB_PRODUCT=\"Pico\"", - "PICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1", - "CYW43_LWIP=1", - "CYW43_PIO_CLOCK_DIV_DYNAMIC=1", - "LWIP_IPV4=1", - "LWIP_IGMP=1", - "LWIP_CHECKSUM_CTRL_PER_NETIF=1", - "LWIP_IPV6=0", - "ARDUINO_VARIANT=\"rpipico\"", - "" - ], - "cStandard": "gnu17", - "cppStandard": "gnu++17", - "compilerPath": "C:/Users/vanderwt/.platformio/packages/toolchain-rp2040-earlephilhower/bin/arm-none-eabi-gcc.exe", - "compilerArgs": [ - "-march=armv6-m", - "-mcpu=cortex-m0plus", - "-mthumb", - "" - ] - } - ], - "version": 4 -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 080e70d..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - // 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/launch.json b/.vscode/launch.json deleted file mode 100644 index 55f738e..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,47 +0,0 @@ -// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY -// -// PlatformIO Debugging Solution -// -// Documentation: https://docs.platformio.org/en/latest/plus/debugging.html -// Configuration: https://docs.platformio.org/en/latest/projectconf/sections/env/options/debug/index.html - -{ - "version": "0.2.0", - "configurations": [ - { - "type": "platformio-debug", - "request": "launch", - "name": "PIO Debug", - "executable": "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/.pio/build/pico/firmware.elf", - "projectEnvName": "pico", - "toolchainBinDir": "C:/Users/vanderwt/.platformio/packages/toolchain-rp2040-earlephilhower/bin", - "internalConsoleOptions": "openOnSessionStart", - "svdPath": "C:/Users/vanderwt/.platformio/platforms/raspberrypi/misc/svd/rp2040.svd", - "preLaunchTask": { - "type": "PlatformIO", - "task": "Pre-Debug" - } - }, - { - "type": "platformio-debug", - "request": "launch", - "name": "PIO Debug (skip Pre-Debug)", - "executable": "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/.pio/build/pico/firmware.elf", - "projectEnvName": "pico", - "toolchainBinDir": "C:/Users/vanderwt/.platformio/packages/toolchain-rp2040-earlephilhower/bin", - "internalConsoleOptions": "openOnSessionStart", - "svdPath": "C:/Users/vanderwt/.platformio/platforms/raspberrypi/misc/svd/rp2040.svd" - }, - { - "type": "platformio-debug", - "request": "launch", - "name": "PIO Debug (without uploading)", - "executable": "C:/Users/vanderwt/OneDrive - Scion/Documents/PROJECTS/Cetogenix/Reactor Upgrades/pico-sensor-interface/.pio/build/pico/firmware.elf", - "projectEnvName": "pico", - "toolchainBinDir": "C:/Users/vanderwt/.platformio/packages/toolchain-rp2040-earlephilhower/bin", - "internalConsoleOptions": "openOnSessionStart", - "svdPath": "C:/Users/vanderwt/.platformio/platforms/raspberrypi/misc/svd/rp2040.svd", - "loadMode": "manual" - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 5e09a03..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "files.associations": { - "chrono": "cpp" - } -} \ No newline at end of file From 4889cc5d86de5e4f4867f11a2817cca32fac23aa Mon Sep 17 00:00:00 2001 From: tazomatalax Date: Fri, 16 May 2025 11:17:42 +1200 Subject: [PATCH 2/4] implment AS5600 sensor via i2c --- src/main.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/sys_init.h | 12 +++++----- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d08c118..9956fce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,19 @@ #include "sys_init.h" +#include + +// AS5600 I2C address and register +#define AS5600_ADDR 0x36 +#define AS5600_ANGLE_HI 0x0E +#define AS5600_ANGLE_LO 0x0F + +// AS5600 RPM calculation state +volatile uint16_t as5600_prev_angle = 0; +volatile uint32_t as5600_prev_time = 0; +volatile float as5600_rpm = 0.0; +#define AS5600_SAMPLE_INTERVAL 50 // ms +#define AS5600_MA_FILTER_SIZE 5 +float as5600_rpm_history[AS5600_MA_FILTER_SIZE] = {0}; +uint8_t as5600_rpm_idx = 0; // Forward declarations void pulse_ISR(void); @@ -7,6 +22,8 @@ void handle_current_sensor(void); void handle_led(void); bool set_rpm(float rpm_val); bool check_pulse_timeout(void); +uint16_t as5600_read_angle(); +void handle_as5600_rpm(void); // ----------------------------Setup ---------------------------- @@ -66,6 +83,7 @@ void loop() { if (millis() - adcLastMillis > ADC_INTERVAL) handle_ADC(); if (millis() - motorLastMillis > MOTOR_INTERVAL) handle_current_sensor(); if (millis() - ledLastMillis > LED_INTERVAL) handle_led(); + if (millis() - as5600_prev_time > AS5600_SAMPLE_INTERVAL) handle_as5600_rpm(); } void loop1() { @@ -168,4 +186,47 @@ bool check_pulse_timeout(void) { rpm = 0.0; set_rpm(0.0); return true; +} + +// Read 12-bit angle from AS5600 +uint16_t as5600_read_angle() { + Wire.beginTransmission(AS5600_ADDR); + Wire.write(AS5600_ANGLE_HI); + Wire.endTransmission(false); + Wire.requestFrom(AS5600_ADDR, 2u); + uint8_t hi = Wire.read(); + uint8_t lo = Wire.read(); + return ((hi << 8) | lo) & 0x0FFF; +} + +// Handle AS5600 RPM calculation +void handle_as5600_rpm() { + static bool initialized = false; + uint32_t now = millis(); + uint16_t angle = as5600_read_angle(); + if (!initialized) { + as5600_prev_angle = angle; + as5600_prev_time = now; + initialized = true; + return; + } + int16_t delta = angle - as5600_prev_angle; + if (delta > 2048) delta -= 4096; + if (delta < -2048) delta += 4096; + uint32_t dt = now - as5600_prev_time; + if (dt > 0) { + float rpm = fabsf((float)delta * 60.0f / 4096.0f / ((float)dt / 1000.0f)); + as5600_rpm_history[as5600_rpm_idx] = rpm; + as5600_rpm_idx = (as5600_rpm_idx + 1) % AS5600_MA_FILTER_SIZE; + float sum = 0.0f; + for (uint8_t i = 0; i < AS5600_MA_FILTER_SIZE; i++) sum += as5600_rpm_history[i]; + as5600_rpm = sum / AS5600_MA_FILTER_SIZE; + if (xSemaphoreTake(sensorMutex, 0) == pdTRUE) { + sensors.rpm = as5600_rpm; + xSemaphoreGive(sensorMutex); + } + if (debug) SerialDebug.printf("AS5600: raw delta=%d, dt=%lu ms, rpm=%.2f\n", delta, dt, as5600_rpm); + } + as5600_prev_angle = angle; + as5600_prev_time = now; } \ No newline at end of file diff --git a/src/sys_init.h b/src/sys_init.h index 48cdceb..7dfbd59 100644 --- a/src/sys_init.h +++ b/src/sys_init.h @@ -23,7 +23,7 @@ // Modbus #define MODBUS_SLAVE_ID 1 #define MODBUS_BAUD 115200 -#define MODBUS_UNIT_ID 1 // Unit ID in holding register for ident Change this to match the unit ID of the Pico +#define MODBUS_UNIT_ID 4 // Unit ID in holding register for ident Change this to match the unit ID of the Pico // 4-2-mA calcs #define SAMPLES 255 // Number of samples per channel measurement (about 100µs per sample) @@ -34,7 +34,7 @@ // Per board calibration adjustment (choose 1) ------------------------------------------> -// Pico sensor interface #1 +/*// Pico sensor interface #1 #define UNIT_ID "Unit 1" #define mA_OFFSET_ch0 -0.2 // Channel 0 mA offset #define mA_OFFSET_ch1 -0.2 // Channel 1 mA offset @@ -43,7 +43,7 @@ #define mA_MULTIPLIER_ch0 0.991 // Channel 0 mA multiplier #define mA_MULTIPLIER_ch1 0.991 // Channel 1 mA multiplier #define mA_MULTIPLIER_ch2 0.991 // Channel 2 mA multiplier -#define mA_MULTIPLIER_ina226 0.916 // Motor current sensor mA multiplier +#define mA_MULTIPLIER_ina226 0.916 // Motor current sensor mA multiplier*/ /*// Pico sensor interface #2 #define UNIT_ID "Unit 2" @@ -65,9 +65,9 @@ #define mA_MULTIPLIER_ch0 0.991 // Channel 0 mA multiplier #define mA_MULTIPLIER_ch1 0.991 // Channel 1 mA multiplier #define mA_MULTIPLIER_ch2 0.991 // Channel 2 mA multiplier -#define mA_MULTIPLIER_ina226 0.921 // Motor current sensor mA multiplier*/ +#define mA_MULTIPLIER_ina226 0.921 // Motor current sensor mA*/ -/*// Pico sensor interface #4 +// Pico sensor interface #4 #define UNIT_ID "Unit 4" #define mA_OFFSET_ch0 -0.22 // Channel 0 mA offset #define mA_OFFSET_ch1 -0.22 // Channel 1 mA offset @@ -76,7 +76,7 @@ #define mA_MULTIPLIER_ch0 0.991 // Channel 0 mA multiplier #define mA_MULTIPLIER_ch1 0.991 // Channel 1 mA multiplier #define mA_MULTIPLIER_ch2 0.991 // Channel 2 mA multiplier -#define mA_MULTIPLIER_ina226 0.897 // Motor current sensor mA multiplier*/ +#define mA_MULTIPLIER_ina226 0.897 // Motor current sensor mA multiplier // <-------------------------------------------------------------------------------------- From a03a12fb1ac0adcec25fdaaed2dc373281a6b37e Mon Sep 17 00:00:00 2001 From: tazomatalax Date: Tue, 20 May 2025 12:36:01 +1200 Subject: [PATCH 3/4] docs: clarify INA226 current sensing is disabled by default - Updated README to explain that all INA226 (current sensing) code is present but commented out due to hardware issues. - Provided instructions for re-enabling current sensing in the future. - Noted that all other features (ADC, RPM, Modbus, etc.) remain fully functional with current sensing disabled. - Commented out INA226 code in main.cpp and sys_init.h for safe operation on hardware with a faulty or missing INA226. --- README.md | 18 ++++- src/main.cpp | 178 ++++++++++++++++++++++++------------------------- src/sys_init.h | 4 +- 3 files changed, 107 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 8c8b76c..4a6ab07 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,14 @@ A Raspberry Pi Pico-based sensor interface project that provides real-time monit - Dual-core utilization for efficient task handling - Motor power control functionality - Thread-safe sensor data handling using FreeRTOS mutexes +- **Current sensing code is present but disabled by default for hardware compatibility** ## Hardware Requirements - Raspberry Pi Pico - 4 - 20mA Sensors (100R shunt resistor) - Active low RPM sensor (1 pulse per revolution) -- Current monitoring over I2C +- Current monitoring over I2C (INA226, optional/disabled by default) - USB Serial Modbus RTU communication ![Basic circuit diagram](/schematic/circuit.png) @@ -49,6 +50,19 @@ A Raspberry Pi Pico-based sensor interface project that provides real-time monit - `/include` - Header files - `/test` - Test files +## Important Note on Current Sensing (INA226) + +The codebase includes support for current sensing using the INA226 over I2C. However, due to a hardware issue, all INA226-related code (initialization, runtime, and configuration) is commented out by default in `main.cpp` and `sys_init.h`. + +- **To enable current sensing:** + - Uncomment the INA226-related lines in `main.cpp` and `sys_init.h`. + - Ensure your hardware is working and the INA226 is connected properly. +- **If left commented:** + - The rest of the system (ADC, RPM, Modbus, etc.) will function normally. + - No current measurements will be reported in the Modbus registers. + +This approach allows easy re-enabling of current sensing in the future by simply uncommenting the relevant code blocks. + ## Configuration The project can be configured through the `platformio.ini` file. Current settings: @@ -79,7 +93,7 @@ The following table describes the Modbus holding registers used in this device: | 40003 (0x0002) | CH1_MA | FLOAT32 | Channel 1 current measurement in mA | | 40005 (0x0004) | CH2_MA | FLOAT32 | Channel 2 current measurement in mA | | 40007 (0x0006) | PULSE_RATE | FLOAT32 | Measured pulse rate in Hz | -| 40009 (0x0008) | MOTOR_MA | FLOAT32 | Motor current measurement in mA | +| 40009 (0x0008) | MOTOR_MA | FLOAT32 | Motor current measurement in mA (only if INA226 enabled) | Notes: - All FLOAT32 values use two consecutive registers (32 bits) diff --git a/src/main.cpp b/src/main.cpp index 9956fce..78d497f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,29 +1,29 @@ #include "sys_init.h" -#include - -// AS5600 I2C address and register -#define AS5600_ADDR 0x36 -#define AS5600_ANGLE_HI 0x0E -#define AS5600_ANGLE_LO 0x0F - -// AS5600 RPM calculation state -volatile uint16_t as5600_prev_angle = 0; -volatile uint32_t as5600_prev_time = 0; -volatile float as5600_rpm = 0.0; -#define AS5600_SAMPLE_INTERVAL 50 // ms -#define AS5600_MA_FILTER_SIZE 5 -float as5600_rpm_history[AS5600_MA_FILTER_SIZE] = {0}; -uint8_t as5600_rpm_idx = 0; - -// Forward declarations +// #include +// +// // AS5600 I2C address and register +// #define AS5600_ADDR 0x36 +// #define AS5600_ANGLE_HI 0x0E +// #define AS5600_ANGLE_LO 0x0F +// +// // AS5600 RPM calculation state +// volatile uint16_t as5600_prev_angle = 0; +// volatile uint32_t as5600_prev_time = 0; +// volatile float as5600_rpm = 0.0; +// #define AS5600_SAMPLE_INTERVAL 50 // ms +// #define AS5600_MA_FILTER_SIZE 5 +// float as5600_rpm_history[AS5600_MA_FILTER_SIZE] = {0}; +// uint8_t as5600_rpm_idx = 0; +// +// // Forward declarations void pulse_ISR(void); void handle_ADC(void); -void handle_current_sensor(void); +// void handle_current_sensor(void); void handle_led(void); bool set_rpm(float rpm_val); bool check_pulse_timeout(void); -uint16_t as5600_read_angle(); -void handle_as5600_rpm(void); +// uint16_t as5600_read_angle(); +// void handle_as5600_rpm(void); // ----------------------------Setup ---------------------------- @@ -37,18 +37,18 @@ void setup() { analogReadResolution(12); pinMode(LED_BUILTIN, OUTPUT); - Wire.setSDA(PIN_SDA); - Wire.setSCL(PIN_SCL); - Wire.begin(); - if (!ina226.begin()) { - if (debug) SerialDebug.println("ERROR: Failed to start INA226 current sensor interface"); - while (1); - } - if (debug) SerialDebug.println("INA226 current sensor interface started"); - ina226.setAverage(INA226_1024_SAMPLES); - uint16_t err = ina226.setMaxCurrentShunt(8, 0.01, true); - char *shunt_err[4] = {"shunt voltage high", "max current low", "shunt low", "normalize fail"}; - if (debug && err != 0) SerialDebug.printf("ERROR: Failed to set INA226 shunt current limit, reason: %s\r\n", shunt_err[err & 3]); +// Wire.setSDA(PIN_SDA); +// Wire.setSCL(PIN_SCL); +// Wire.begin(); +// if (!ina226.begin()) { +// if (debug) SerialDebug.println("ERROR: Failed to start INA226 current sensor interface"); +// while (1); +// } +// if (debug) SerialDebug.println("INA226 current sensor interface started"); +// ina226.setAverage(INA226_1024_SAMPLES); +// uint16_t err = ina226.setMaxCurrentShunt(8, 0.01, true); +// char *shunt_err[4] = {"shunt voltage high", "max current low", "shunt low", "normalize fail"}; +// if (debug && err != 0) SerialDebug.printf("ERROR: Failed to set INA226 shunt current limit, reason: %s\r\n", shunt_err[err & 3]); // DEBUG - Setup modbus serial port on UART0 (debug mode only - frees up USB for uploads and debugging) Serial1.setTX(PIN_UART_TX); @@ -81,9 +81,9 @@ void setup1() { void loop() { modbus.poll(); if (millis() - adcLastMillis > ADC_INTERVAL) handle_ADC(); - if (millis() - motorLastMillis > MOTOR_INTERVAL) handle_current_sensor(); + // if (millis() - motorLastMillis > MOTOR_INTERVAL) handle_current_sensor(); if (millis() - ledLastMillis > LED_INTERVAL) handle_led(); - if (millis() - as5600_prev_time > AS5600_SAMPLE_INTERVAL) handle_as5600_rpm(); + // if (millis() - as5600_prev_time > AS5600_SAMPLE_INTERVAL) handle_as5600_rpm(); } void loop1() { @@ -149,19 +149,19 @@ void handle_ADC(void) { } // Update motor current from INA226? sensor -void handle_current_sensor(void) { - motorLastMillis += MOTOR_INTERVAL; - float motor_current = ina226.getCurrent_mA(); - motor_current = mA_OFFSET_ina226 + motor_current * mA_MULTIPLIER_ina226; - if (debug) { - SerialDebug.printf("%fV, %fV, %fmA, %fW\n", ina226.getBusVoltage(), ina226.getShuntVoltage(), motor_current, ina226.getPower()); - } - - if (xSemaphoreTake(sensorMutex, 0) == pdTRUE) { - sensors.motor_mA = motor_current; - xSemaphoreGive(sensorMutex); - } -} +// void handle_current_sensor(void) { +// motorLastMillis += MOTOR_INTERVAL; +// float motor_current = ina226.getCurrent_mA(); +// motor_current = mA_OFFSET_ina226 + motor_current * mA_MULTIPLIER_ina226; +// if (debug) { +// SerialDebug.printf("%fV, %fV, %fmA, %fW\n", ina226.getBusVoltage(), ina226.getShuntVoltage(), motor_current, ina226.getPower()); +// } +// +// if (xSemaphoreTake(sensorMutex, 0) == pdTRUE) { +// sensors.motor_mA = motor_current; +// xSemaphoreGive(sensorMutex); +// } +// } // Toggle status LED void handle_led(void) { @@ -188,45 +188,45 @@ bool check_pulse_timeout(void) { return true; } -// Read 12-bit angle from AS5600 -uint16_t as5600_read_angle() { - Wire.beginTransmission(AS5600_ADDR); - Wire.write(AS5600_ANGLE_HI); - Wire.endTransmission(false); - Wire.requestFrom(AS5600_ADDR, 2u); - uint8_t hi = Wire.read(); - uint8_t lo = Wire.read(); - return ((hi << 8) | lo) & 0x0FFF; -} - -// Handle AS5600 RPM calculation -void handle_as5600_rpm() { - static bool initialized = false; - uint32_t now = millis(); - uint16_t angle = as5600_read_angle(); - if (!initialized) { - as5600_prev_angle = angle; - as5600_prev_time = now; - initialized = true; - return; - } - int16_t delta = angle - as5600_prev_angle; - if (delta > 2048) delta -= 4096; - if (delta < -2048) delta += 4096; - uint32_t dt = now - as5600_prev_time; - if (dt > 0) { - float rpm = fabsf((float)delta * 60.0f / 4096.0f / ((float)dt / 1000.0f)); - as5600_rpm_history[as5600_rpm_idx] = rpm; - as5600_rpm_idx = (as5600_rpm_idx + 1) % AS5600_MA_FILTER_SIZE; - float sum = 0.0f; - for (uint8_t i = 0; i < AS5600_MA_FILTER_SIZE; i++) sum += as5600_rpm_history[i]; - as5600_rpm = sum / AS5600_MA_FILTER_SIZE; - if (xSemaphoreTake(sensorMutex, 0) == pdTRUE) { - sensors.rpm = as5600_rpm; - xSemaphoreGive(sensorMutex); - } - if (debug) SerialDebug.printf("AS5600: raw delta=%d, dt=%lu ms, rpm=%.2f\n", delta, dt, as5600_rpm); - } - as5600_prev_angle = angle; - as5600_prev_time = now; -} \ No newline at end of file +// // Read 12-bit angle from AS5600 +// uint16_t as5600_read_angle() { +// Wire.beginTransmission(AS5600_ADDR); +// Wire.write(AS5600_ANGLE_HI); +// Wire.endTransmission(false); +// Wire.requestFrom(AS5600_ADDR, 2u); +// uint8_t hi = Wire.read(); +// uint8_t lo = Wire.read(); +// return ((hi << 8) | lo) & 0x0FFF; +// } +// +// // Handle AS5600 RPM calculation +// void handle_as5600_rpm() { +// static bool initialized = false; +// uint32_t now = millis(); +// uint16_t angle = as5600_read_angle(); +// if (!initialized) { +// as5600_prev_angle = angle; +// as5600_prev_time = now; +// initialized = true; +// return; +// } +// int16_t delta = angle - as5600_prev_angle; +// if (delta > 2048) delta -= 4096; +// if (delta < -2048) delta += 4096; +// uint32_t dt = now - as5600_prev_time; +// if (dt > 0) { +// float rpm = fabsf((float)delta * 60.0f / 4096.0f / ((float)dt / 1000.0f)); +// as5600_rpm_history[as5600_rpm_idx] = rpm; +// as5600_rpm_idx = (as5600_rpm_idx + 1) % AS5600_MA_FILTER_SIZE; +// float sum = 0.0f; +// for (uint8_t i = 0; i < AS5600_MA_FILTER_SIZE; i++) sum += as5600_rpm_history[i]; +// as5600_rpm = sum / AS5600_MA_FILTER_SIZE; +// if (xSemaphoreTake(sensorMutex, 0) == pdTRUE) { +// sensors.rpm = as5600_rpm; +// xSemaphoreGive(sensorMutex); +// } +// if (debug) SerialDebug.printf("AS5600: raw delta=%d, dt=%lu ms, rpm=%.2f\n", delta, dt, as5600_rpm); +// } +// as5600_prev_angle = angle; +// as5600_prev_time = now; +// } \ No newline at end of file diff --git a/src/sys_init.h b/src/sys_init.h index 7dfbd59..25ddb0f 100644 --- a/src/sys_init.h +++ b/src/sys_init.h @@ -3,7 +3,7 @@ #include "ModbusRTUSlave.h" #include "FreeRTOS.h" #include "semphr.h" -#include "INA226.h" +// #include "INA226.h" // Pins #define PIN_ADC_0 26 @@ -98,7 +98,7 @@ // Lib objects ModbusRTUSlave modbus(SerialModbus); SemaphoreHandle_t sensorMutex = NULL; // Mutex so both cores can safely access sensor data -INA226 ina226(0x40); // INA226 sensor object +// INA226 ina226(0x40); // INA226 sensor object // Globals struct sensors_t { From 00e977135689298b5bcd22365d03b0fd36d2c46a Mon Sep 17 00:00:00 2001 From: tazomatalax Date: Tue, 27 May 2025 12:44:43 +1200 Subject: [PATCH 4/4] update nodered flow file --- flow.json | 3143 ++++++++++---------------------------------------- grafana.json | 2454 ++++++++++++++++++++++++++++++--------- 2 files changed, 2557 insertions(+), 3040 deletions(-) diff --git a/flow.json b/flow.json index f6bf026..ff821de 100644 --- a/flow.json +++ b/flow.json @@ -1,2063 +1,17 @@ [ { - "id": "6c900aff9d56898f", - "type": "tab", - "label": "Flow 1", - "disabled": true, - "info": "", - "env": [] - }, - { - "id": "ac978cd8aec44371", - "type": "tab", - "label": "Flow 2", - "disabled": false, - "info": "", - "env": [] - }, - { - "id": "1389333f9aa0bc16", - "type": "tab", - "label": "Modbus Gas Counter", - "disabled": false, - "info": "" - }, - { - "id": "c7f58bd6e7679c23", - "type": "junction", - "z": "6c900aff9d56898f", - "x": 540, - "y": 520, - "wires": [ - [ - "c8f6b8115e7825a1", - "e18ed1e7523f4a79", - "216a4d4bca74cbdc" - ] - ] - }, - { - "id": "8031e60f3dcb4ea1", - "type": "modbus-client", - "name": "Serial 0", - "clienttype": "simpleser", - "bufferCommands": true, - "stateLogEnabled": false, - "queueLogEnabled": false, - "failureLogEnabled": true, - "tcpHost": "127.0.0.1", - "tcpPort": "502", - "tcpType": "DEFAULT", - "serialPort": "/dev/ttyS0", - "serialType": "RTU-BUFFERD", - "serialBaudrate": "115200", - "serialDatabits": "8", - "serialStopbits": "1", - "serialParity": "none", - "serialConnectionDelay": "100", - "serialAsciiResponseStartDelimiter": "0x3A", - "unit_id": 1, - "commandDelay": 1, - "clientTimeout": 1000, - "reconnectOnTimeout": false, - "reconnectTimeout": 100, - "parallelUnitIdsAllowed": false, - "showErrors": true, - "showWarnings": true, - "showLogs": true - }, - { - "id": "modbus-server", - "type": "modbus-client", - "name": "RS485", - "clienttype": "serial", - "bufferCommands": true, - "stateLogEnabled": true, - "queueLogEnabled": true, - "failureLogEnabled": true, - "tcpHost": "127.0.0.1", - "tcpPort": "502", - "serialPort": "/dev/ttyUSB0", - "serialType": "RTU-BUFFERD", - "serialBaudrate": "9600", - "serialDatabits": "8", - "serialStopbits": "1", - "serialParity": "none", - "serialConnectionDelay": "100", - "serialAsciiResponseStartDelimiter": "", - "unit_id": 1, - "commandDelay": 1, - "clientTimeout": 1000, - "reconnectOnTimeout": false, - "reconnectTimeout": 2000, - "parallelUnitIdsAllowed": false, - "showErrors": false, - "showWarnings": true, - "showLogs": true - }, - { - "id": "17fa2ebfd6e7a3c8", - "type": "ui_tab", - "name": "Reactor", - "icon": "dashboard", - "disabled": false, - "hidden": false - }, - { - "id": "5ae5c0e0bbc69168", - "type": "ui_group", - "name": "pH", - "tab": "17fa2ebfd6e7a3c8", - "order": 1, - "disp": true, - "width": "6", - "collapse": false, - "className": "" - }, - { - "id": "2a129fe40b8c9a16", - "type": "ui_group", - "name": "Temp", - "tab": "17fa2ebfd6e7a3c8", - "order": 2, - "disp": true, - "width": "6", - "collapse": false, - "className": "" - }, - { - "id": "calibration_group", - "type": "ui_group", - "name": "Calibration", - "tab": "17fa2ebfd6e7a3c8", - "order": 3, - "disp": true, - "width": "6" - }, - { - "id": "3665fa5e334434b5", - "type": "ui_base", - "theme": { - "name": "theme-light", - "lightTheme": { - "default": "#0094CE", - "baseColor": "#0094CE", - "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", - "edited": false - }, - "darkTheme": { - "default": "#097479", - "baseColor": "#097479", - "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", - "edited": false - }, - "customTheme": { - "name": "Untitled Theme 1", - "default": "#4B7930", - "baseColor": "#4B7930", - "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" - }, - "themeState": { - "base-color": { - "default": "#0094CE", - "value": "#0094CE", - "edited": false - }, - "page-titlebar-backgroundColor": { - "value": "#0094CE", - "edited": false - }, - "page-backgroundColor": { - "value": "#fafafa", - "edited": false - }, - "page-sidebar-backgroundColor": { - "value": "#ffffff", - "edited": false - }, - "group-textColor": { - "value": "#1bbfff", - "edited": false - }, - "group-borderColor": { - "value": "#ffffff", - "edited": false - }, - "group-backgroundColor": { - "value": "#ffffff", - "edited": false - }, - "widget-textColor": { - "value": "#111111", - "edited": false - }, - "widget-backgroundColor": { - "value": "#0094ce", - "edited": false - }, - "widget-borderColor": { - "value": "#ffffff", - "edited": false - }, - "base-font": { - "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" - } - }, - "angularTheme": { - "primary": "indigo", - "accents": "blue", - "warn": "red", - "background": "grey", - "palette": "light" - } - }, - "site": { - "name": "Node-RED Dashboard", - "hideToolbar": "false", - "allowSwipe": "false", - "lockMenu": "false", - "allowTempTheme": "true", - "dateFormat": "DD/MM/YYYY", - "sizes": { - "sx": 48, - "sy": 48, - "gx": 6, - "gy": 6, - "cx": 6, - "cy": 6, - "px": 0, - "py": 0 - } - } - }, - { - "id": "mbclient1", - "type": "modbus-client", - "name": "ACM0", - "clienttype": "serial", - "bufferCommands": false, - "stateLogEnabled": true, - "queueLogEnabled": false, - "failureLogEnabled": true, - "tcpHost": "127.0.0.1", - "tcpPort": "502", - "tcpType": "DEFAULT", - "serialPort": "/dev/ttyACM0", - "serialType": "RTU", - "serialBaudrate": "115200", - "serialDatabits": "8", - "serialStopbits": "1", - "serialParity": "none", - "serialConnectionDelay": "100", - "serialAsciiResponseStartDelimiter": "", - "unit_id": "", - "commandDelay": "1", - "clientTimeout": "1000", - "reconnectOnTimeout": false, - "reconnectTimeout": "2000", - "parallelUnitIdsAllowed": false, - "showErrors": false, - "showWarnings": true, - "showLogs": true - }, - { - "id": "a680a93aa70ad8b3", - "type": "serial-port", - "name": "test", - "serialport": "/dev/ttyACM2", - "serialbaud": "115200", - "databits": "8", - "parity": "none", - "stopbits": "1", - "waitfor": "", - "dtr": "none", - "rts": "none", - "cts": "none", - "dsr": "none", - "newline": "\\n", - "bin": "false", - "out": "char", - "addchar": "", - "responsetimeout": "10000" - }, - { - "id": "d9a29a25883258e7", - "type": "influxdb", - "hostname": "127.0.0.1", - "port": "8086", - "protocol": "http", - "database": "reactor_db", - "name": "Local InfluxDB", - "usetls": false, - "tls": "", - "influxdbVersion": "2.0", - "url": "http://localhost:8086", - "timeout": "", - "rejectUnauthorized": false - }, - { - "id": "influxconfig", - "type": "influxdb", - "hostname": "127.0.0.1", - "port": "8086", - "protocol": "http", - "database": "database", - "name": "Scion InfluxDB", - "usetls": false, - "tls": "", - "influxdbVersion": "2.0", - "url": "https://influxdb.scionresearch.com", - "timeout": "10", - "rejectUnauthorized": false - }, - { - "id": "ef99d96f993392a8", - "type": "modbus-client", - "name": "ACM2", - "clienttype": "simpleser", - "bufferCommands": true, - "stateLogEnabled": false, - "queueLogEnabled": false, - "failureLogEnabled": true, - "tcpHost": "127.0.0.1", - "tcpPort": "502", - "tcpType": "DEFAULT", - "serialPort": "/dev/ttyACM2", - "serialType": "RTU", - "serialBaudrate": "115200", - "serialDatabits": "8", - "serialStopbits": "1", - "serialParity": "none", - "serialConnectionDelay": "100", - "serialAsciiResponseStartDelimiter": "0x3A", - "unit_id": "1", - "commandDelay": "1", - "clientTimeout": "1000", - "reconnectOnTimeout": true, - "reconnectTimeout": "2000", - "parallelUnitIdsAllowed": true, - "showErrors": false, - "showWarnings": true, - "showLogs": true - }, - { - "id": "5b33d3a0df2de5e0", - "type": "modbus-client", - "name": "ACM1", - "clienttype": "simpleser", - "bufferCommands": true, - "stateLogEnabled": false, - "queueLogEnabled": false, - "failureLogEnabled": true, - "tcpHost": "127.0.0.1", - "tcpPort": "502", - "tcpType": "DEFAULT", - "serialPort": "/dev/ttyACM1", - "serialType": "RTU-BUFFERD", - "serialBaudrate": "115200", - "serialDatabits": "8", - "serialStopbits": "1", - "serialParity": "none", - "serialConnectionDelay": "100", - "serialAsciiResponseStartDelimiter": "0x3A", - "unit_id": "1", - "commandDelay": "1", - "clientTimeout": "1000", - "reconnectOnTimeout": true, - "reconnectTimeout": "2000", - "parallelUnitIdsAllowed": true, - "showErrors": false, - "showWarnings": true, - "showLogs": true - }, - { - "id": "a1f84dec681b9b29", - "type": "modbus-client", - "name": "ACM3", - "clienttype": "simpleser", - "bufferCommands": true, - "stateLogEnabled": false, - "queueLogEnabled": false, - "failureLogEnabled": true, - "tcpHost": "127.0.0.1", - "tcpPort": "502", - "tcpType": "DEFAULT", - "serialPort": "/dev/ttyACM3", - "serialType": "RTU-BUFFERD", - "serialBaudrate": "115200", - "serialDatabits": "8", - "serialStopbits": "1", - "serialParity": "none", - "serialConnectionDelay": "100", - "serialAsciiResponseStartDelimiter": "0x3A", - "unit_id": "1", - "commandDelay": "1", - "clientTimeout": "1000", - "reconnectOnTimeout": true, - "reconnectTimeout": "2000", - "parallelUnitIdsAllowed": true, - "showErrors": false, - "showWarnings": true, - "showLogs": true - }, - { - "id": "c8f6b8115e7825a1", - "type": "modbus-flex-getter", - "z": "6c900aff9d56898f", - "name": "", - "showStatusActivities": true, - "showErrors": true, - "showWarnings": true, - "logIOActivities": false, - "server": "8031e60f3dcb4ea1", - "useIOFile": false, - "ioFile": "", - "useIOForPayload": false, - "emptyMsgOnFail": false, - "keepMsgProperties": true, - "delayOnStart": false, - "startDelayTime": "", - "x": 730, - "y": 480, - "wires": [ - [ - "70cf43b58a240f50", - "37c38d6782068e98" - ], - [] - ] - }, - { - "id": "7bf32269fc4e10e2", - "type": "function", - "z": "6c900aff9d56898f", - "name": "Unit 1", - "func": "if (msg.payload) {\n msg.payload = { 'fc': 3, 'unitid': 1, 'address': 0, 'quantity': 19 }\n return msg;\n}", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 410, - "y": 440, - "wires": [ - [ - "c7f58bd6e7679c23" - ] - ] - }, - { - "id": "9159aab2b265d102", - "type": "inject", - "z": "6c900aff9d56898f", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "true", - "payloadType": "bool", - "x": 190, - "y": 420, - "wires": [ - [ - "7bf32269fc4e10e2" - ] - ] - }, - { - "id": "70cf43b58a240f50", - "type": "debug", - "z": "6c900aff9d56898f", - "name": "debug 1", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 1180, - "y": 420, - "wires": [] - }, - { - "id": "33069d683e935a18", - "type": "rpi-gpio out", - "z": "6c900aff9d56898f", - "name": "", - "pin": "17", - "set": true, - "level": "0", - "freq": "", - "out": "out", - "bcm": true, - "x": 920, - "y": 560, - "wires": [] - }, - { - "id": "e18ed1e7523f4a79", - "type": "function", - "z": "6c900aff9d56898f", - "name": "DE high", - "func": "msg.payload = 1;\nreturn msg;", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 740, - "y": 560, - "wires": [ - [ - "33069d683e935a18" - ] - ] - }, - { - "id": "da246e33543bc5f0", - "type": "function", - "z": "6c900aff9d56898f", - "name": "DE low", - "func": "msg.payload = 0;\nreturn msg;", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 780, - "y": 620, - "wires": [ - [ - "33069d683e935a18" - ] - ] - }, - { - "id": "216a4d4bca74cbdc", - "type": "delay", - "z": "6c900aff9d56898f", - "name": "", - "pauseType": "delay", - "timeout": "1", - "timeoutUnits": "milliseconds", - "rate": "1", - "nbRateUnits": "1", - "rateUnits": "second", - "randomFirst": "1", - "randomLast": "5", - "randomUnits": "seconds", - "drop": false, - "allowrate": false, - "outputs": 1, - "x": 630, - "y": 620, - "wires": [ - [ - "da246e33543bc5f0" - ] - ] - }, - { - "id": "37c38d6782068e98", - "type": "buffer-parser", - "z": "6c900aff9d56898f", - "name": "", - "data": "payload", - "dataType": "msg", - "specification": "spec", - "specificationType": "ui", - "items": [ - { - "type": "floatle", - "name": "volume", - "offset": 0, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "flow", - "offset": 4, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "temperature", - "offset": 8, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "pressure", - "offset": 12, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "int32le", - "name": "timestamp", - "offset": 16, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "psu_volts", - "offset": 20, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "batt_volts", - "offset": 24, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "string", - "name": "unit_ID", - "offset": 28, - "length": 10, - "offsetbit": 0, - "scale": "1", - "mask": "" - } - ], - "swap1": "swap16", - "swap2": "", - "swap3": "", - "swap1Type": "swap", - "swap2Type": "swap", - "swap3Type": "swap", - "msgProperty": "payload", - "msgPropertyType": "str", - "resultType": "value", - "resultTypeType": "return", - "multipleResult": false, - "fanOutMultipleResult": false, - "setTopic": true, - "outputs": 1, - "x": 950, - "y": 500, - "wires": [ - [ - "70cf43b58a240f50", - "91636ecda4a5200b" - ] - ] - }, - { - "id": "2d3f68a2e43d6dc0", - "type": "rpi-gpio in", - "z": "6c900aff9d56898f", - "name": "Trigger 1", - "pin": "0", - "intype": "tri", - "debounce": "25", - "read": false, - "bcm": true, - "x": 180, - "y": 480, - "wires": [ - [ - "7bf32269fc4e10e2" - ] - ] - }, - { - "id": "91636ecda4a5200b", - "type": "file", - "z": "6c900aff9d56898f", - "name": "", - "filename": "\"/home/admin/FlowCounterLogs/unit_ID_\" & msg.payload[7] & \".csv\"", - "filenameType": "jsonata", - "appendNewline": true, - "createDir": false, - "overwriteFile": "false", - "encoding": "none", - "x": 1160, - "y": 560, - "wires": [ - [] - ] - }, - { - "id": "3080df2b0f87b256", - "type": "function", - "z": "6c900aff9d56898f", - "name": "Unit 2", - "func": "if (msg.payload) {\n msg.payload = { 'fc': 3, 'unitid': 2, 'address': 0, 'quantity': 19 }\n return msg;\n}", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 410, - "y": 540, - "wires": [ - [ - "c7f58bd6e7679c23" - ] - ] - }, - { - "id": "31abc2f6633194e8", - "type": "inject", - "z": "6c900aff9d56898f", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "true", - "payloadType": "bool", - "x": 190, - "y": 540, - "wires": [ - [ - "3080df2b0f87b256" - ] - ] - }, - { - "id": "05842cfa2fdc3bb0", - "type": "rpi-gpio in", - "z": "6c900aff9d56898f", - "name": "Trigger 2", - "pin": "1", - "intype": "tri", - "debounce": "25", - "read": false, - "bcm": true, - "x": 180, - "y": 580, - "wires": [ - [ - "3080df2b0f87b256" - ] - ] - }, - { - "id": "4d87b28a2854d8cf", - "type": "function", - "z": "6c900aff9d56898f", - "name": "Unit 3", - "func": "if (msg.payload) {\n msg.payload = { 'fc': 3, 'unitid': 3, 'address': 0, 'quantity': 19 }\n return msg;\n}", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 410, - "y": 640, - "wires": [ - [ - "c7f58bd6e7679c23" - ] - ] - }, - { - "id": "f9685f1205fe55bf", - "type": "inject", - "z": "6c900aff9d56898f", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "true", - "payloadType": "bool", - "x": 190, - "y": 640, - "wires": [ - [ - "4d87b28a2854d8cf" - ] - ] - }, - { - "id": "bb412829d3d460c5", - "type": "rpi-gpio in", - "z": "6c900aff9d56898f", - "name": "Trigger 3", - "pin": "2", - "intype": "tri", - "debounce": "25", - "read": false, - "bcm": true, - "x": 180, - "y": 680, - "wires": [ - [ - "4d87b28a2854d8cf" - ] - ] - }, - { - "id": "56652e00910ab055", - "type": "function", - "z": "6c900aff9d56898f", - "name": "Unit 4", - "func": "if (msg.payload) {\n msg.payload = { 'fc': 3, 'unitid': 4, 'address': 0, 'quantity': 19 }\n return msg;\n}", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 410, - "y": 740, - "wires": [ - [ - "c7f58bd6e7679c23" - ] - ] - }, - { - "id": "a066d39e92ad5616", - "type": "inject", - "z": "6c900aff9d56898f", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "true", - "payloadType": "bool", - "x": 190, - "y": 740, - "wires": [ - [ - "56652e00910ab055" - ] - ] - }, - { - "id": "d81c65a80dc119f0", - "type": "rpi-gpio in", - "z": "6c900aff9d56898f", - "name": "Trigger 4", - "pin": "3", - "intype": "tri", - "debounce": "25", - "read": false, - "bcm": true, - "x": 180, - "y": 780, - "wires": [ - [ - "56652e00910ab055" - ] - ] - }, - { - "id": "7d014796be50cbac", - "type": "function", - "z": "6c900aff9d56898f", - "name": "Unit 5", - "func": "if (msg.payload) {\n msg.payload = { 'fc': 3, 'unitid': 5, 'address': 0, 'quantity': 19 }\n return msg;\n}", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 410, - "y": 840, - "wires": [ - [ - "c7f58bd6e7679c23" - ] - ] - }, - { - "id": "a0ce4b0cacb8ed38", - "type": "inject", - "z": "6c900aff9d56898f", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "true", - "payloadType": "bool", - "x": 190, - "y": 840, - "wires": [ - [ - "7d014796be50cbac" - ] - ] - }, - { - "id": "b3d9d7f6a791237d", - "type": "rpi-gpio in", - "z": "6c900aff9d56898f", - "name": "Trigger 5", - "pin": "4", - "intype": "tri", - "debounce": "25", - "read": false, - "bcm": true, - "x": 180, - "y": 880, - "wires": [ - [ - "7d014796be50cbac" - ] - ] - }, - { - "id": "b7e8a00e93d527bc", - "type": "function", - "z": "6c900aff9d56898f", - "name": "Unit 6", - "func": "if (msg.payload) {\n msg.payload = { 'fc': 3, 'unitid': 6, 'address': 0, 'quantity': 19 }\n return msg;\n}", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 410, - "y": 940, - "wires": [ - [ - "c7f58bd6e7679c23" - ] - ] - }, - { - "id": "d2ea20e6122b9ac9", - "type": "inject", - "z": "6c900aff9d56898f", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "true", - "payloadType": "bool", - "x": 190, - "y": 940, - "wires": [ - [ - "b7e8a00e93d527bc" - ] - ] - }, - { - "id": "af0d5096cc3eca32", - "type": "rpi-gpio in", - "z": "6c900aff9d56898f", - "name": "Trigger 6", - "pin": "5", - "intype": "tri", - "debounce": "25", - "read": false, - "bcm": true, - "x": 180, - "y": 980, - "wires": [ - [ - "b7e8a00e93d527bc" - ] - ] - }, - { - "id": "10dc28d912fad79e", - "type": "function", - "z": "6c900aff9d56898f", - "name": "Unit 7", - "func": "if (msg.payload) {\n msg.payload = { 'fc': 3, 'unitid': 7, 'address': 0, 'quantity': 19 }\n return msg;\n}", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 410, - "y": 1040, - "wires": [ - [ - "c7f58bd6e7679c23" - ] - ] - }, - { - "id": "e722d18521bb1e06", - "type": "inject", - "z": "6c900aff9d56898f", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "true", - "payloadType": "bool", - "x": 190, - "y": 1040, - "wires": [ - [ - "10dc28d912fad79e" - ] - ] - }, - { - "id": "8ec86d895e756810", - "type": "rpi-gpio in", - "z": "6c900aff9d56898f", - "name": "Trigger 7", - "pin": "6", - "intype": "tri", - "debounce": "25", - "read": false, - "bcm": true, - "x": 180, - "y": 1080, - "wires": [ - [ - "10dc28d912fad79e" - ] - ] - }, - { - "id": "51976245a47d055b", - "type": "function", - "z": "6c900aff9d56898f", - "name": "Unit 8", - "func": "if (msg.payload) {\n msg.payload = { 'fc': 3, 'unitid': 8, 'address': 0, 'quantity': 19 }\n return msg;\n}", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 410, - "y": 1140, - "wires": [ - [ - "c7f58bd6e7679c23" - ] - ] - }, - { - "id": "8f153c654742e9f4", - "type": "inject", - "z": "6c900aff9d56898f", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "true", - "payloadType": "bool", - "x": 190, - "y": 1140, - "wires": [ - [ - "51976245a47d055b" - ] - ] - }, - { - "id": "f2bc1f66a9b4b0f7", - "type": "rpi-gpio in", - "z": "6c900aff9d56898f", - "name": "Trigger 8", - "pin": "7", - "intype": "tri", - "debounce": "25", - "read": false, - "bcm": true, - "x": 180, - "y": 1180, - "wires": [ - [ - "51976245a47d055b" - ] - ] - }, - { - "id": "237bcd4b6f3ff0da", - "type": "modbus-flex-getter", - "z": "ac978cd8aec44371", - "name": "Read Raw Modbus Data", - "showStatusActivities": false, - "showErrors": false, - "showWarnings": true, - "logIOActivities": false, - "server": "5b33d3a0df2de5e0", - "useIOFile": false, - "ioFile": "", - "useIOForPayload": false, - "emptyMsgOnFail": false, - "keepMsgProperties": false, - "delayOnStart": false, - "startDelayTime": "", - "x": 550, - "y": 220, - "wires": [ - [ - "4da9d0eedf82a427", - "parser1" - ], - [] - ] - }, - { - "id": "c542afaad3d2881f", - "type": "inject", - "z": "ac978cd8aec44371", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "7.5", - "crontab": "", - "once": true, - "onceDelay": "1", - "topic": "", - "payload": "", - "payloadType": "date", - "x": 210, - "y": 94.28571428571422, - "wires": [ - [ - "a9634b12787cd3d4", - "48ca6410c5bae19d" - ] - ] - }, - { - "id": "a9634b12787cd3d4", - "type": "function", - "z": "ac978cd8aec44371", - "name": "Modbus Request Setup", - "func": "//Store unit id here\nmsg.unitid = 1; \nmsg.payload = {\n 'fc': 3,\n 'unitid': 1,\n 'address': 0,\n 'quantity': 11 \n};\nreturn msg;", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 250, - "y": 171.42857142857133, - "wires": [ - [ - "237bcd4b6f3ff0da" - ] - ] - }, - { - "id": "4da9d0eedf82a427", - "type": "debug", - "z": "ac978cd8aec44371", - "name": "debug 2", - "active": false, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "targetType": "msg", - "statusVal": "", - "statusType": "auto", - "x": 650, - "y": 120, - "wires": [] - }, - { - "id": "parser1", - "type": "buffer-parser", - "z": "ac978cd8aec44371", - "name": "Parse Pico 1", - "data": "payload", - "dataType": "msg", - "specification": "spec", - "specificationType": "ui", - "items": [ - { - "type": "floatle", - "name": "Temperature", - "offset": 0, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "pH", - "offset": 4, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "ORP", - "offset": 8, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "RPM", - "offset": 12, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "motor_mA", - "offset": 16, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "uint16le", - "name": "reactor_number", - "offset": 20, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - } - ], - "swap1": "swap16", - "swap2": "", - "swap3": "", - "swap1Type": "swap", - "swap2Type": "swap", - "swap3Type": "swap", - "msgProperty": "", - "msgPropertyType": "", - "resultType": "keyvalue", - "resultTypeType": "", - "multipleResult": false, - "fanOutMultipleResult": false, - "setTopic": false, - "outputs": 1, - "x": 790, - "y": 220, - "wires": [ - [ - "da801bb2e618c5a0", - "87759633c5d9f7f2" - ] - ] - }, - { - "id": "87759633c5d9f7f2", - "type": "debug", - "z": "ac978cd8aec44371", - "name": "debug 14", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 260, - "wires": [] - }, - { - "id": "48ca6410c5bae19d", - "type": "delay", - "z": "ac978cd8aec44371", - "name": "", - "pauseType": "delay", - "timeout": "200", - "timeoutUnits": "milliseconds", - "rate": "1", - "nbRateUnits": "1", - "rateUnits": "second", - "randomFirst": "1", - "randomLast": "5", - "randomUnits": "seconds", - "drop": false, - "allowrate": false, - "outputs": 1, - "x": 210, - "y": 248.5714285714285, - "wires": [ - [ - "bb5ed0db02b4330a", - "6a8a53838b8b14af" - ] - ] - }, - { - "id": "bb5ed0db02b4330a", - "type": "function", - "z": "ac978cd8aec44371", - "name": "Modbus Request Setup", - "func": "//Store unit id here\nmsg.unitid = 2;\nmsg.payload = {\n 'fc': 3,\n 'unitid': 1,\n 'address': 0,\n 'quantity': 11\n};\nreturn msg;", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 250, - "y": 325.71428571428567, - "wires": [ - [ - "26409017be2e8b10" - ] - ] - }, - { - "id": "26409017be2e8b10", - "type": "modbus-flex-getter", - "z": "ac978cd8aec44371", - "name": "Read Raw Modbus Data", - "showStatusActivities": true, - "showErrors": true, - "showWarnings": true, - "logIOActivities": false, - "server": "a1f84dec681b9b29", - "useIOFile": false, - "ioFile": "", - "useIOForPayload": false, - "emptyMsgOnFail": false, - "keepMsgProperties": true, - "delayOnStart": false, - "startDelayTime": "", - "x": 550, - "y": 313.3333333333333, - "wires": [ - [ - "b6cd9fc8a3165f7e" - ], - [] - ] - }, - { - "id": "b4c9ebbdfa012465", - "type": "function", - "z": "ac978cd8aec44371", - "name": "Modbus Request Setup", - "func": "//Store unit id here\nmsg.unitid = 3;\nmsg.payload = {\n 'fc': 3,\n 'unitid': 1,\n 'address': 0,\n 'quantity': 11 \n};\nreturn msg;", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 250, - "y": 480, - "wires": [ - [ - "8d8a31674c496bc3" - ] - ] - }, - { - "id": "8d8a31674c496bc3", - "type": "modbus-flex-getter", - "z": "ac978cd8aec44371", - "name": "Read Raw Modbus Data", - "showStatusActivities": true, - "showErrors": true, - "showWarnings": true, - "logIOActivities": false, - "server": "ef99d96f993392a8", - "useIOFile": false, - "ioFile": "", - "useIOForPayload": false, - "emptyMsgOnFail": false, - "keepMsgProperties": true, - "delayOnStart": false, - "startDelayTime": "", - "x": 550, - "y": 406.66666666666663, - "wires": [ - [ - "e538f9895bf8d49b" - ], - [] - ] - }, - { - "id": "41ecbfac290e7bb7", - "type": "function", - "z": "ac978cd8aec44371", - "name": "Modbus Request Setup", - "func": "//Store unit id here\nmsg.unitid = 4;\nmsg.payload = {\n 'fc': 3,\n 'unitid': 1,\n 'address': 0,\n 'quantity': 11\n};\nreturn msg;", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 250, - "y": 634.2857142857142, - "wires": [ - [ - "06e8d36c947a56d0" - ] - ] - }, - { - "id": "06e8d36c947a56d0", - "type": "modbus-flex-getter", - "z": "ac978cd8aec44371", - "name": "Read Raw Modbus Data", - "showStatusActivities": true, - "showErrors": true, - "showWarnings": true, - "logIOActivities": false, - "server": "mbclient1", - "useIOFile": false, - "ioFile": "", - "useIOForPayload": false, - "emptyMsgOnFail": false, - "keepMsgProperties": true, - "delayOnStart": false, - "startDelayTime": "", - "x": 550, - "y": 500, - "wires": [ - [ - "2aaaa727b8288992" - ], - [] - ] - }, - { - "id": "6a8a53838b8b14af", - "type": "delay", - "z": "ac978cd8aec44371", - "name": "", - "pauseType": "delay", - "timeout": "200", - "timeoutUnits": "milliseconds", - "rate": "1", - "nbRateUnits": "1", - "rateUnits": "second", - "randomFirst": "1", - "randomLast": "5", - "randomUnits": "seconds", - "drop": false, - "allowrate": false, - "outputs": 1, - "x": 210, - "y": 402.85714285714283, - "wires": [ - [ - "b4c9ebbdfa012465", - "8f9e7921f8a07719" - ] - ] - }, - { - "id": "8f9e7921f8a07719", - "type": "delay", - "z": "ac978cd8aec44371", - "name": "", - "pauseType": "delay", - "timeout": "200", - "timeoutUnits": "milliseconds", - "rate": "1", - "nbRateUnits": "1", - "rateUnits": "second", - "randomFirst": "1", - "randomLast": "5", - "randomUnits": "seconds", - "drop": false, - "allowrate": false, - "outputs": 1, - "x": 210, - "y": 557.1428571428571, - "wires": [ - [ - "41ecbfac290e7bb7" - ] - ] - }, - { - "id": "a9ec1568645bc5f9", - "type": "influxdb batch", - "z": "ac978cd8aec44371", - "influxdb": "d9a29a25883258e7", - "precision": "", - "retentionPolicy": "", - "name": "Influx Batch", - "database": "database", - "precisionV18FluxV20": "ms", - "retentionPolicyV18Flux": "", - "org": "Cetogenix", - "bucket": "reactor-monitoring", - "x": 1290, - "y": 360, - "wires": [] - }, - { - "id": "b6cd9fc8a3165f7e", - "type": "buffer-parser", - "z": "ac978cd8aec44371", - "name": "Parse Pico 2", - "data": "payload", - "dataType": "msg", - "specification": "spec", - "specificationType": "ui", - "items": [ - { - "type": "floatle", - "name": "Temperature", - "offset": 0, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "pH", - "offset": 4, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "ORP", - "offset": 8, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "RPM", - "offset": 12, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "motor_mA", - "offset": 16, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "uint16le", - "name": "reactor_number", - "offset": 20, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - } - ], - "swap1": "swap16", - "swap2": "", - "swap3": "", - "swap1Type": "swap", - "swap2Type": "swap", - "swap3Type": "swap", - "msgProperty": "", - "msgPropertyType": "", - "resultType": "keyvalue", - "resultTypeType": "", - "multipleResult": false, - "fanOutMultipleResult": false, - "setTopic": false, - "outputs": 1, - "x": 790, - "y": 320, - "wires": [ - [ - "da801bb2e618c5a0", - "25add18126934daa" - ] - ] - }, - { - "id": "e538f9895bf8d49b", - "type": "buffer-parser", - "z": "ac978cd8aec44371", - "name": "Parse Pico 3", - "data": "payload", - "dataType": "msg", - "specification": "spec", - "specificationType": "ui", - "items": [ - { - "type": "floatle", - "name": "Temperature", - "offset": 0, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "pH", - "offset": 4, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "ORP", - "offset": 8, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "RPM", - "offset": 12, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "motor_mA", - "offset": 16, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "uint16le", - "name": "reactor_number", - "offset": 20, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - } - ], - "swap1": "swap16", - "swap2": "", - "swap3": "", - "swap1Type": "swap", - "swap2Type": "swap", - "swap3Type": "swap", - "msgProperty": "", - "msgPropertyType": "", - "resultType": "keyvalue", - "resultTypeType": "", - "multipleResult": false, - "fanOutMultipleResult": false, - "setTopic": false, - "outputs": 1, - "x": 790, - "y": 420, - "wires": [ - [ - "da801bb2e618c5a0", - "d7cf368f5e64ec1c" - ] - ] - }, - { - "id": "da801bb2e618c5a0", - "type": "function", - "z": "ac978cd8aec44371", - "name": "Scale and Filter Data", - "func": "// --- Configuration Section (Easily Editable) ---\n\nconst deviceIdPrefix = \"pico_\"; // Prefix for device IDs\n\n// Calibration/Scaling factors. These are now *PER DEVICE* and *PER SENSOR*.\nconst scalingFactors = {\n \"pico_1\": {\n Temperature: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 100,\n // Add calibration factors here\n calibration: { slope: 1.0, offset: 0.0 }\n },\n pH: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 14,\n calibration: { slope: 1.0107, offset: 0.0 }\n },\n ORP: { \n sensorType: \"mA_INPUT\", \n rangeMin: -1000, \n rangeMax: 500,\n calibration: { slope: 1.093, offset: 0.0 }\n },\n motor_mA: { \n multiplier: 1, \n offset: 0.0, \n sensorType: \"INA226\",\n calibration: { slope: 1.0, offset: 0.0 }\n },\n RPM: { \n multiplier: 1, \n offset: 0, \n sensorType: \"PULSE\",\n calibration: { slope: 1.0, offset: 0.0 }\n }\n },\n \"pico_2\": {\n Temperature: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 100,\n calibration: { slope: 1.0, offset: 0.0 }\n },\n pH: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 14,\n calibration: { slope: 1.0107, offset: 0.0 }\n },\n ORP: { \n sensorType: \"mA_INPUT\", \n rangeMin: -1000, \n rangeMax: 500,\n calibration: { slope: 0.977, offset: 0.0 }\n },\n motor_mA: { \n multiplier: -1, \n offset: 0.0, \n sensorType: \"INA226\",\n calibration: { slope: 1.0, offset: 0.0 }\n },\n RPM: { \n multiplier: 1, \n offset: 0, \n sensorType: \"PULSE\",\n calibration: { slope: 1.0, offset: 0.0 }\n }\n },\n \"pico_3\": {\n Temperature: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 100,\n calibration: { slope: 1.005, offset: 0.0 }\n },\n pH: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 14,\n calibration: { slope: 1.0, offset: 0.0 }\n },\n ORP: { \n sensorType: \"mA_INPUT\", \n rangeMin: -1000, \n rangeMax: 500,\n calibration: { slope: 0.988, offset: 0.0 }\n },\n motor_mA: { \n multiplier: 1, \n offset: 0.0, \n sensorType: \"INA226\",\n calibration: { slope: 1.0, offset: 0.0 }\n },\n RPM: { \n multiplier: 1/5.167, \n offset: 0, \n sensorType: \"PULSE\",\n calibration: { slope: 1.0, offset: 0.0 }\n }\n },\n \"pico_4\": {\n Temperature: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 100,\n calibration: { slope: 1.0, offset: 0.0 }\n },\n pH: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 14,\n calibration: { slope: 1.0, offset: 0.0 }\n },\n ORP: { \n sensorType: \"mA_INPUT\", \n rangeMin: -1000, \n rangeMax: 500,\n calibration: { slope: 0.72, offset: 0.0 }\n },\n motor_mA: { \n multiplier: 1, \n offset: 0.0, \n sensorType: \"INA226\",\n calibration: { slope: 1.0, offset: 0.0 }\n },\n RPM: { \n multiplier: 1/5.167, \n offset: 0, \n sensorType: \"PULSE\",\n calibration: { slope: 1.0, offset: 0.0 }\n }\n },\n};\n\n// Define valid ranges for each measurement, *after* final scaling. These are your REAL-WORLD units.\nconst validRanges = {\n Temperature: { min: -1, max: 110 }, // Allow slight negative/overrange\n pH: { min: -0.5, max: 14.5 },\n ORP: { min: -2100, max: 2100 },\n motor_mA: { min: -5, max: 2000 },\n RPM: { min: 0, max: 500000 } // Slightly above expected max, for margin.\n};\n\n// Noise floor for 4-20mA signals.\nconst noiseFloor_mA = 0.25;\n\n// Measurement name for InfluxDB\nconst influxMeasurement = 'pico_measurements';\n\n// --- End Configuration Section ---\n\n// --- Helper Functions ---\n\nfunction scale_mA_Input(value, rangeMin, rangeMax, calibration) {\n // 1. Apply noise floor (in mA).\n if (value < noiseFloor_mA) {\n value = 0;\n }\n\n // 2. Convert 4-20mA to 0-100%.\n const percent = (value - 4) / 16;\n\n // 3. Clamp the percentage.\n const clampedPercent = Math.max(0, Math.min(1, percent));\n\n // 4. Scale to engineering units.\n let scaledValue = rangeMin + clampedPercent * (rangeMax - rangeMin);\n \n // 5. Apply calibration factors (y = mx + b)\n if (calibration) {\n scaledValue = scaledValue * calibration.slope + calibration.offset;\n }\n \n return scaledValue;\n}\n\nfunction validateAndScale(value, fieldName, deviceId) {\n if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {\n node.warn(`Invalid ${fieldName} for ${deviceId}: ${value} (Not a valid number)`);\n return null;\n }\n\n const deviceScaling = scalingFactors[deviceId] || {};\n const scaling = deviceScaling[fieldName] || { sensorType: \"UNKNOWN\", rangeMin: 0, rangeMax: 1 };\n\n let scaledValue = value;\n\n // Apply scaling based on sensor type\n if (scaling.sensorType === \"mA_INPUT\") {\n // Scale the mA value to engineering units.\n scaledValue = scale_mA_Input(scaledValue, scaling.rangeMin, scaling.rangeMax, scaling.calibration);\n\n } else if (scaling.sensorType === \"INA226\") {\n // Apply offset and multiplier (for motor current).\n scaledValue = value + scaling.offset;\n scaledValue = scaledValue * scaling.multiplier;\n \n // Apply calibration factors\n if (scaling.calibration) {\n scaledValue = scaledValue * scaling.calibration.slope + scaling.calibration.offset;\n }\n } else if (scaling.sensorType === \"PULSE\") {\n // Apply multiplier for RPM (including gear reduction for pico_3 and pico_4)\n scaledValue = value * scaling.multiplier;\n \n // Apply calibration factors\n if (scaling.calibration) {\n scaledValue = scaledValue * scaling.calibration.slope + scaling.calibration.offset;\n }\n }\n\n // Range validation *after* scaling\n const range = validRanges[fieldName];\n if (range && (scaledValue < range.min || scaledValue > range.max)) {\n // node.warn(`Invalid ${fieldName} for ${deviceId}: ${scaledValue} (Out of range [${range.min}, ${range.max}])`);\n return null;\n }\n\n return scaledValue;\n}\n\n// --- Main Function Logic ---\n\nconst data = msg.payload;\nconst deviceId = msg.unitid ? `${deviceIdPrefix}${msg.unitid}` : `${deviceIdPrefix}1`;\nconst timestamp = Date.now();\n\nconst influxData = {\n measurement: influxMeasurement,\n tags: {\n device: deviceId\n },\n timestamp: timestamp,\n fields: {}\n};\n\nconst fieldsToProcess = [\"Temperature\", \"pH\", \"ORP\", \"motor_mA\", \"RPM\"];\n\nfor (const field of fieldsToProcess) {\n const scaledValue = validateAndScale(data[field], field, deviceId);\n if (scaledValue !== null) {\n influxData.fields[field] = scaledValue;\n }\n}\n\n// Debug point - uncommenting this can help with calibration\n// node.warn(`Device ${deviceId} scaled values: ${JSON.stringify(influxData.fields)}`);\n\nmsg.payload = [influxData];\nreturn msg;", - "outputs": 1, - "timeout": "", - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 1040, - "y": 360, - "wires": [ - [ - "a9ec1568645bc5f9", - "c0eeb8449b8a850c", - "f49821d1ade651d8" - ] - ] - }, - { - "id": "2aaaa727b8288992", - "type": "buffer-parser", - "z": "ac978cd8aec44371", - "name": "Parse Pico 4", - "data": "payload", - "dataType": "msg", - "specification": "spec", - "specificationType": "ui", - "items": [ - { - "type": "floatle", - "name": "Temperature", - "offset": 0, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "pH", - "offset": 4, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "ORP", - "offset": 8, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "RPM", - "offset": 12, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "motor_mA", - "offset": 16, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "uint16le", - "name": "reactor_number", - "offset": 20, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - } - ], - "swap1": "swap16", - "swap2": "", - "swap3": "", - "swap1Type": "swap", - "swap2Type": "swap", - "swap3Type": "swap", - "msgProperty": "", - "msgPropertyType": "", - "resultType": "keyvalue", - "resultTypeType": "", - "multipleResult": false, - "fanOutMultipleResult": false, - "setTopic": false, - "outputs": 1, - "x": 790, - "y": 520, - "wires": [ - [ - "da801bb2e618c5a0", - "c0b097bea214691d" - ] - ] - }, - { - "id": "c0eeb8449b8a850c", - "type": "debug", - "z": "ac978cd8aec44371", - "name": "debug 15", - "active": false, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 1280, - "y": 300, - "wires": [] - }, - { - "id": "f49821d1ade651d8", - "type": "influxdb batch", - "z": "ac978cd8aec44371", - "influxdb": "influxconfig", - "precision": "", - "retentionPolicy": "", - "name": "Influx Batch", - "database": "database", - "precisionV18FluxV20": "ms", - "retentionPolicyV18Flux": "", - "org": "Scion", - "bucket": "reactor-monitoring", - "x": 1290, - "y": 420, - "wires": [] - }, - { - "id": "c0b097bea214691d", - "type": "debug", - "z": "ac978cd8aec44371", - "name": "debug 16", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 560, - "wires": [] - }, - { - "id": "d7cf368f5e64ec1c", - "type": "debug", - "z": "ac978cd8aec44371", - "name": "debug 17", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 460, - "wires": [] - }, - { - "id": "25add18126934daa", - "type": "debug", - "z": "ac978cd8aec44371", - "name": "debug 18", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 360, - "wires": [] - }, - { - "id": "02afb9aa984725d2", - "type": "delay", - "z": "1389333f9aa0bc16", - "name": "", - "pauseType": "delay", - "timeout": "500", - "timeoutUnits": "milliseconds", - "rate": "1", - "nbRateUnits": "1", - "rateUnits": "second", - "randomFirst": "1", - "randomLast": "5", - "randomUnits": "seconds", - "drop": false, - "allowrate": false, - "outputs": 1, - "x": 150, - "y": 254.28571428571428, - "wires": [ - [ - "cd7a7f5d47241c59", - "356d91517565b696" - ] - ] - }, - { - "id": "b1155883a018456b", - "type": "function", - "z": "1389333f9aa0bc16", - "name": "Modbus Request Setup", - "func": "//Store unit id here\nmsg.unitid = 1;\nmsg.payload = {\n 'fc': 3, // Function code: Read Holding Registers\n 'unitid': 1,\n 'address': 0, \n 'quantity': 20\n};\n\nreturn msg;", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 210, - "y": 200, - "wires": [ - [ - "f98c7ec29a1b140f", - "c65ac1587b432575" - ] - ] - }, - { - "id": "f98c7ec29a1b140f", - "type": "modbus-flex-getter", - "z": "1389333f9aa0bc16", - "name": "Read Raw Modbus Data", - "showStatusActivities": true, - "showErrors": true, - "showWarnings": true, - "logIOActivities": false, - "server": "modbus-server", - "useIOFile": false, - "ioFile": "", - "useIOForPayload": false, - "emptyMsgOnFail": false, - "keepMsgProperties": true, - "delayOnStart": false, - "startDelayTime": "", - "x": 510, - "y": 171.4285714285714, - "wires": [ - [ - "36a10a37369b498b", - "727f1a6b051cf6d1" - ], - [] - ] + "id": "ac978cd8aec44371", + "type": "tab", + "label": "Flow 2", + "disabled": false, + "info": "", + "env": [] }, { - "id": "e4dc3e5bad17b487", + "id": "c542afaad3d2881f", "type": "inject", - "z": "1389333f9aa0bc16", - "name": "5 Sec Trigger", + "z": "ac978cd8aec44371", + "name": "", "props": [ { "p": "payload" @@ -2074,268 +28,203 @@ "topic": "", "payload": "", "payloadType": "date", - "x": 120, - "y": 145.71428571428572, + "x": 210, + "y": 94.28571428571422, "wires": [ [ - "b1155883a018456b", - "02afb9aa984725d2" + "a9634b12787cd3d4" ] ] }, { - "id": "36a10a37369b498b", - "type": "buffer-parser", - "z": "1389333f9aa0bc16", - "name": "Parser", - "data": "payload", - "dataType": "msg", - "specification": "spec", - "specificationType": "ui", - "items": [ - { - "type": "floatle", - "name": "Volume (mL)", - "offset": 0, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Volume Normalised (mL)", - "offset": 4, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Flow (mL/min)", - "offset": 8, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Flow Normalised (mL/min)", - "offset": 12, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Temperature (°C)", - "offset": 16, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Pressure (hPa)", - "offset": 20, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "uint32le", - "name": "Timestamp", - "offset": 24, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Power Supply (V) ", - "offset": 28, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Battery (V)", - "offset": 32, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - } - ], - "swap1": "swap16", - "swap2": "", - "swap3": "", - "swap1Type": "swap", - "swap2Type": "swap", - "swap3Type": "swap", - "msgProperty": "payload", - "msgPropertyType": "str", - "resultType": "keyvalue", - "resultTypeType": "return", - "multipleResult": false, - "fanOutMultipleResult": false, - "setTopic": true, + "id": "a9634b12787cd3d4", + "type": "function", + "z": "ac978cd8aec44371", + "name": "Modbus Request Setup", + "func": "//Store unit id here\nmsg.unitid = 1; \nmsg.payload = {\n 'fc': 3,\n 'unitid': 1,\n 'address': 0,\n 'quantity': 11 \n};\nreturn msg;", "outputs": 1, - "x": 730, - "y": 233.33333333333337, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 270, + "y": 180, "wires": [ [ - "5b3c52ea8f15ce17", - "118b254427b33a50" + "20862974760bf062", + "327883f0775caba3" ] ] }, { - "id": "8e702722a1c2df99", + "id": "4da9d0eedf82a427", "type": "debug", - "z": "1389333f9aa0bc16", - "name": "debug 9", + "z": "ac978cd8aec44371", + "name": "debug 2", "active": false, "tosidebar": true, "console": false, "tostatus": false, - "complete": "false", + "complete": "payload", + "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 805, - "y": 80, + "x": 650, + "y": 120, "wires": [] }, { - "id": "cd7a7f5d47241c59", + "id": "48ca6410c5bae19d", + "type": "delay", + "z": "ac978cd8aec44371", + "name": "", + "pauseType": "delay", + "timeout": "200", + "timeoutUnits": "milliseconds", + "rate": "1", + "nbRateUnits": "1", + "rateUnits": "second", + "randomFirst": "1", + "randomLast": "5", + "randomUnits": "seconds", + "drop": false, + "allowrate": false, + "outputs": 1, + "x": 210, + "y": 248.5714285714285, + "wires": [ + [ + "bb5ed0db02b4330a" + ] + ] + }, + { + "id": "bb5ed0db02b4330a", "type": "function", - "z": "1389333f9aa0bc16", + "z": "ac978cd8aec44371", "name": "Modbus Request Setup", - "func": "//Store unit id here\nmsg.unitid = 2;\nmsg.payload = {\n 'fc': 3, // Function code: Read Holding Registers\n 'unitid': 2,\n 'address': 0,\n 'quantity': 20\n};\n\nreturn msg;", + "func": "//Store unit id here\nmsg.unitid = 2;\nmsg.payload = {\n 'fc': 3,\n 'unitid': 1,\n 'address': 0,\n 'quantity': 11\n};\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 230, - "y": 308.57142857142856, + "x": 250, + "y": 325.71428571428567, "wires": [ [ - "aaaf1be865503c38" + "26409017be2e8b10" ] ] }, { - "id": "aaaf1be865503c38", + "id": "26409017be2e8b10", "type": "modbus-flex-getter", - "z": "1389333f9aa0bc16", + "z": "ac978cd8aec44371", "name": "Read Raw Modbus Data", "showStatusActivities": true, "showErrors": true, "showWarnings": true, "logIOActivities": false, - "server": "modbus-server", + "server": "a1f84dec681b9b29", "useIOFile": false, "ioFile": "", "useIOForPayload": false, - "emptyMsgOnFail": false, + "emptyMsgOnFail": true, "keepMsgProperties": true, "delayOnStart": false, "startDelayTime": "", - "x": 510, - "y": 287.6190476190476, + "x": 550, + "y": 313.3333333333333, "wires": [ [ - "02bfbbf8e2e0e8c6" + "1d78f3303ba8ec8b", + "6a8a53838b8b14af", + "32b1f66c58487512" ], [] ] }, { - "id": "f7fa4fcdfe04bd79", + "id": "b4c9ebbdfa012465", "type": "function", - "z": "1389333f9aa0bc16", + "z": "ac978cd8aec44371", "name": "Modbus Request Setup", - "func": "//Store unit id here\nmsg.unitid = 3;\nmsg.payload = {\n 'fc': 3, // Function code: Read Holding Registers\n 'unitid': 3,\n 'address': 0,\n 'quantity': 20\n};\n\nreturn msg;", + "func": "//Store unit id here\nmsg.unitid = 3;\nmsg.payload = {\n 'fc': 3,\n 'unitid': 1,\n 'address': 0,\n 'quantity': 11 \n};\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 230, - "y": 417.1428571428571, + "x": 250, + "y": 480, "wires": [ [ - "7bbcde623c6b6603" + "8d8a31674c496bc3" ] ] }, { - "id": "7bbcde623c6b6603", + "id": "8d8a31674c496bc3", "type": "modbus-flex-getter", - "z": "1389333f9aa0bc16", + "z": "ac978cd8aec44371", "name": "Read Raw Modbus Data", "showStatusActivities": true, "showErrors": true, "showWarnings": true, "logIOActivities": false, - "server": "modbus-server", + "server": "ef99d96f993392a8", "useIOFile": false, "ioFile": "", "useIOForPayload": false, - "emptyMsgOnFail": false, + "emptyMsgOnFail": true, "keepMsgProperties": true, "delayOnStart": false, "startDelayTime": "", - "x": 510, - "y": 403.8095238095238, + "x": 550, + "y": 406.66666666666663, "wires": [ [ - "5442578935fb809d" + "0469fde9e358b9c5", + "8f9e7921f8a07719", + "32b1f66c58487512" ], [] ] }, { - "id": "5287a71a7699bda6", + "id": "41ecbfac290e7bb7", "type": "function", - "z": "1389333f9aa0bc16", + "z": "ac978cd8aec44371", "name": "Modbus Request Setup", - "func": "//Store unit id here\nmsg.unitid = 4;\nmsg.payload = {\n 'fc': 3, // Function code: Read Holding Registers\n 'unitid': 4,\n 'address': 0, \n 'quantity': 20\n};\n\nreturn msg;", + "func": "//Store unit id here\nmsg.unitid = 4;\nmsg.payload = {\n 'fc': 3,\n 'unitid': 1,\n 'address': 0,\n 'quantity': 11\n};\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 230, - "y": 525.7142857142858, + "x": 250, + "y": 634.2857142857142, "wires": [ [ - "eacce3c9826c2a4c" + "06e8d36c947a56d0" ] ] }, { - "id": "eacce3c9826c2a4c", + "id": "06e8d36c947a56d0", "type": "modbus-flex-getter", - "z": "1389333f9aa0bc16", + "z": "ac978cd8aec44371", "name": "Read Raw Modbus Data", "showStatusActivities": true, - "showErrors": true, - "showWarnings": true, + "showErrors": false, + "showWarnings": false, "logIOActivities": false, - "server": "modbus-server", + "server": "mbclient1", "useIOFile": false, "ioFile": "", "useIOForPayload": false, @@ -2343,22 +232,23 @@ "keepMsgProperties": true, "delayOnStart": false, "startDelayTime": "", - "x": 510, - "y": 520, + "x": 550, + "y": 500, "wires": [ [ - "f491850509fa0f54" + "0d1c24a539a6f95a", + "32b1f66c58487512" ], [] ] }, { - "id": "356d91517565b696", + "id": "6a8a53838b8b14af", "type": "delay", - "z": "1389333f9aa0bc16", + "z": "ac978cd8aec44371", "name": "", "pauseType": "delay", - "timeout": "500", + "timeout": "200", "timeoutUnits": "milliseconds", "rate": "1", "nbRateUnits": "1", @@ -2369,22 +259,21 @@ "drop": false, "allowrate": false, "outputs": 1, - "x": 150, - "y": 362.85714285714283, + "x": 210, + "y": 402.85714285714283, "wires": [ [ - "f7fa4fcdfe04bd79", - "790fc227bb4493e9" + "b4c9ebbdfa012465" ] ] }, { - "id": "790fc227bb4493e9", + "id": "8f9e7921f8a07719", "type": "delay", - "z": "1389333f9aa0bc16", + "z": "ac978cd8aec44371", "name": "", "pauseType": "delay", - "timeout": "500", + "timeout": "200", "timeoutUnits": "milliseconds", "rate": "1", "nbRateUnits": "1", @@ -2395,35 +284,19 @@ "drop": false, "allowrate": false, "outputs": 1, - "x": 150, - "y": 471.4285714285714, + "x": 210, + "y": 557.1428571428571, "wires": [ [ - "5287a71a7699bda6" + "41ecbfac290e7bb7" ] ] }, { - "id": "1469138eb35d1607", - "type": "debug", - "z": "1389333f9aa0bc16", - "name": "debug 10", - "active": false, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 1140, - "y": 160, - "wires": [] - }, - { - "id": "c65ac1587b432575", + "id": "20862974760bf062", "type": "debug", - "z": "1389333f9aa0bc16", - "name": "debug 11", + "z": "ac978cd8aec44371", + "name": "debug 21", "active": false, "tosidebar": true, "console": false, @@ -2431,31 +304,43 @@ "complete": "false", "statusVal": "", "statusType": "auto", - "x": 395, - "y": 80, + "x": 470, + "y": 100, "wires": [] }, { - "id": "727f1a6b051cf6d1", - "type": "debug", - "z": "1389333f9aa0bc16", - "name": "debug 12", - "active": false, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 600, - "y": 80, - "wires": [] + "id": "327883f0775caba3", + "type": "modbus-flex-getter", + "z": "ac978cd8aec44371", + "name": "Read Raw Modbus Data", + "showStatusActivities": true, + "showErrors": true, + "showWarnings": true, + "logIOActivities": false, + "server": "61951d0040aac1f3", + "useIOFile": false, + "ioFile": "", + "useIOForPayload": false, + "emptyMsgOnFail": true, + "keepMsgProperties": true, + "delayOnStart": false, + "startDelayTime": "5", + "x": 550, + "y": 220, + "wires": [ + [ + "00fe3897c33a971f", + "48ca6410c5bae19d", + "32b1f66c58487512" + ], + [] + ] }, { - "id": "02bfbbf8e2e0e8c6", + "id": "00fe3897c33a971f", "type": "buffer-parser", - "z": "1389333f9aa0bc16", - "name": "Parser", + "z": "ac978cd8aec44371", + "name": "Parse Pico 1", "data": "payload", "dataType": "msg", "specification": "spec", @@ -2463,7 +348,7 @@ "items": [ { "type": "floatle", - "name": "Volume (mL)", + "name": "Temperature_mA", "offset": 0, "length": 1, "offsetbit": 0, @@ -2472,7 +357,7 @@ }, { "type": "floatle", - "name": "Volume Normalised (mL)", + "name": "pH_mA", "offset": 4, "length": 1, "offsetbit": 0, @@ -2481,7 +366,7 @@ }, { "type": "floatle", - "name": "Flow (mL/min)", + "name": "ORP_mA", "offset": 8, "length": 1, "offsetbit": 0, @@ -2490,7 +375,7 @@ }, { "type": "floatle", - "name": "Flow Normalised (mL/min)", + "name": "RPM_raw", "offset": 12, "length": 1, "offsetbit": 0, @@ -2499,7 +384,7 @@ }, { "type": "floatle", - "name": "Temperature (°C)", + "name": "motor_mA", "offset": 16, "length": 1, "offsetbit": 0, @@ -2507,40 +392,13 @@ "mask": "" }, { - "type": "floatle", - "name": "Pressure (hPa)", + "type": "uint16le", + "name": "reactor_number", "offset": 20, "length": 1, "offsetbit": 0, "scale": "1", "mask": "" - }, - { - "type": "uint32le", - "name": "Timestamp", - "offset": 24, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Power Supply (V) ", - "offset": 28, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Battery (V)", - "offset": 32, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" } ], "swap1": "swap16", @@ -2550,27 +408,61 @@ "swap2Type": "swap", "swap3Type": "swap", "msgProperty": "payload", - "msgPropertyType": "str", + "msgPropertyType": "msg", "resultType": "keyvalue", - "resultTypeType": "return", + "resultTypeType": "output", "multipleResult": false, "fanOutMultipleResult": false, "setTopic": true, "outputs": 1, - "x": 730, - "y": 306.6666666666667, + "x": 850, + "y": 220, "wires": [ [ - "8e702722a1c2df99", - "4b0b0633eeaafd92" + "e08e3d9d2b8f10e9", + "45f29de67ddbbc2a" ] ] }, { - "id": "5442578935fb809d", + "id": "45f29de67ddbbc2a", + "type": "debug", + "z": "ac978cd8aec44371", + "name": "Pico 1 Parsed (raw)", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 850, + "y": 280, + "wires": [] + }, + { + "id": "fad901313d420969", + "type": "influxdb batch", + "z": "ac978cd8aec44371", + "influxdb": "d9a29a25883258e7", + "precision": "", + "retentionPolicy": "", + "name": "Influx Batch (Local)", + "database": "database", + "precisionV18FluxV20": "ms", + "retentionPolicyV18Flux": "", + "org": "Cetogenix", + "bucket": "reactor-monitoring", + "x": 1390, + "y": 340, + "wires": [] + }, + { + "id": "1d78f3303ba8ec8b", "type": "buffer-parser", - "z": "1389333f9aa0bc16", - "name": "Parser", + "z": "ac978cd8aec44371", + "name": "Parse Pico 2", "data": "payload", "dataType": "msg", "specification": "spec", @@ -2578,7 +470,7 @@ "items": [ { "type": "floatle", - "name": "Volume (mL)", + "name": "Temperature_mA", "offset": 0, "length": 1, "offsetbit": 0, @@ -2587,7 +479,7 @@ }, { "type": "floatle", - "name": "Volume Normalised (mL)", + "name": "pH_mA", "offset": 4, "length": 1, "offsetbit": 0, @@ -2596,7 +488,7 @@ }, { "type": "floatle", - "name": "Flow (mL/min)", + "name": "ORP_mA", "offset": 8, "length": 1, "offsetbit": 0, @@ -2605,7 +497,7 @@ }, { "type": "floatle", - "name": "Flow Normalised (mL/min)", + "name": "RPM_raw", "offset": 12, "length": 1, "offsetbit": 0, @@ -2614,7 +506,7 @@ }, { "type": "floatle", - "name": "Temperature (°C)", + "name": "motor_mA", "offset": 16, "length": 1, "offsetbit": 0, @@ -2622,40 +514,13 @@ "mask": "" }, { - "type": "floatle", - "name": "Pressure (hPa)", + "type": "uint16le", + "name": "reactor_number", "offset": 20, "length": 1, "offsetbit": 0, "scale": "1", "mask": "" - }, - { - "type": "uint32le", - "name": "Timestamp", - "offset": 24, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Power Supply (V) ", - "offset": 28, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" - }, - { - "type": "floatle", - "name": "Battery (V)", - "offset": 32, - "length": 1, - "offsetbit": 0, - "scale": "1", - "mask": "" } ], "swap1": "swap16", @@ -2665,26 +530,27 @@ "swap2Type": "swap", "swap3Type": "swap", "msgProperty": "payload", - "msgPropertyType": "str", + "msgPropertyType": "msg", "resultType": "keyvalue", - "resultTypeType": "return", + "resultTypeType": "output", "multipleResult": false, "fanOutMultipleResult": false, "setTopic": true, "outputs": 1, - "x": 730, - "y": 380, + "x": 850, + "y": 340, "wires": [ [ - "c1fda35a740f781d" + "e08e3d9d2b8f10e9", + "2a736e67f3c7ea1c" ] ] }, { - "id": "f491850509fa0f54", + "id": "0469fde9e358b9c5", "type": "buffer-parser", - "z": "1389333f9aa0bc16", - "name": "Parser", + "z": "ac978cd8aec44371", + "name": "Parse Pico 3", "data": "payload", "dataType": "msg", "specification": "spec", @@ -2692,7 +558,7 @@ "items": [ { "type": "floatle", - "name": "Volume (mL)", + "name": "Temperature_mA", "offset": 0, "length": 1, "offsetbit": 0, @@ -2701,7 +567,7 @@ }, { "type": "floatle", - "name": "Volume Normalised (mL)", + "name": "pH_mA", "offset": 4, "length": 1, "offsetbit": 0, @@ -2710,7 +576,7 @@ }, { "type": "floatle", - "name": "Flow (mL/min)", + "name": "ORP_mA", "offset": 8, "length": 1, "offsetbit": 0, @@ -2719,7 +585,7 @@ }, { "type": "floatle", - "name": "Flow Normalised (mL/min)", + "name": "RPM_raw", "offset": 12, "length": 1, "offsetbit": 0, @@ -2728,7 +594,7 @@ }, { "type": "floatle", - "name": "Temperature (°C)", + "name": "motor_mA", "offset": 16, "length": 1, "offsetbit": 0, @@ -2736,18 +602,92 @@ "mask": "" }, { - "type": "floatle", - "name": "Pressure (hPa)", + "type": "uint16le", + "name": "reactor_number", "offset": 20, "length": 1, "offsetbit": 0, "scale": "1", "mask": "" + } + ], + "swap1": "swap16", + "swap2": "", + "swap3": "", + "swap1Type": "swap", + "swap2Type": "swap", + "swap3Type": "swap", + "msgProperty": "payload", + "msgPropertyType": "msg", + "resultType": "keyvalue", + "resultTypeType": "output", + "multipleResult": false, + "fanOutMultipleResult": false, + "setTopic": true, + "outputs": 1, + "x": 850, + "y": 440, + "wires": [ + [ + "e08e3d9d2b8f10e9", + "84866463b42b114a" + ] + ] + }, + { + "id": "e08e3d9d2b8f10e9", + "type": "function", + "z": "ac978cd8aec44371", + "name": "Scale and Filter Data", + "func": "// --- Configuration Section (Easily Editable) ---\n\nconst deviceIdPrefix = \"pico_\"; // Prefix for device IDs\n\n// Calibration/Scaling factors. These use the FINAL measurement names as keys.\nconst scalingFactors = {\n \"pico_1\": {\n Temperature: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 100,\n calibration: { slope: 1.0, offset: 0.0 }\n },\n pH: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 14,\n calibration: { slope: 1.0107, offset: 0.0 }\n },\n ORP: { \n sensorType: \"mA_INPUT\", \n rangeMin: -1000, \n rangeMax: 500,\n calibration: { slope: 0.95, offset: 0.0 }\n },\n motor_mA: { \n multiplier: 1, \n offset: 0.0, \n sensorType: \"INA226\", // Assumes motor_mA is already mA from parser\n calibration: { slope: 1.0, offset: 0.0 }\n },\n RPM: { // Config uses final name 'RPM'\n multiplier: 1, \n offset: 0, \n sensorType: \"PULSE\", // Input is RPM_raw from parser\n calibration: { slope: 1.0, offset: 0.0 }\n }\n },\n \"pico_2\": {\n Temperature: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 100,\n calibration: { slope: 1.0, offset: 0.0 }\n },\n pH: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 14,\n calibration: { slope: 1.0107, offset: 0.0 }\n },\n ORP: { \n sensorType: \"mA_INPUT\", \n rangeMin: -1000, \n rangeMax: 500,\n calibration: { slope: 1.01, offset: 0.0 }\n },\n motor_mA: { \n multiplier: -1, \n offset: 0.0, \n sensorType: \"INA226\",\n calibration: { slope: 1.0, offset: 0.0 }\n },\n RPM: { \n multiplier: 1, \n offset: 0, \n sensorType: \"PULSE\",\n calibration: { slope: 1.0, offset: 0.0 }\n }\n },\n \"pico_3\": {\n Temperature: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 100,\n calibration: { slope: 1.005, offset: 0.0 }\n },\n pH: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 14,\n calibration: { slope: 1.0, offset: 0.0 }\n },\n ORP: { \n sensorType: \"mA_INPUT\", \n rangeMin: -1000, \n rangeMax: 1000,\n calibration: { slope: 0.988, offset: 0.0 }\n },\n motor_mA: { \n multiplier: 1, \n offset: 0.0, \n sensorType: \"INA226\",\n calibration: { slope: 1.0, offset: 0.0 }\n },\n RPM: { \n multiplier: 1/5.18, \n offset: 0, \n sensorType: \"PULSE\",\n calibration: { slope: 1.0, offset: 0.0 }\n }\n },\n \"pico_4\": {\n Temperature: { \n sensorType: \"mA_INPUT\", \n rangeMin: 0, \n rangeMax: 100,\n calibration: { slope: 1.0, offset: 0.0 }\n },\n pH: { \n sensorType: \"mA_INPUT\", \n rangeMin: 6.5, \n rangeMax: 16,\n calibration: { slope: 1.0, offset: 0.0 }\n },\n ORP: { \n sensorType: \"mA_INPUT\", \n rangeMin: -1000, \n rangeMax: 500,\n calibration: { slope: 0.608, offset: 0.0 }\n },\n motor_mA: { \n multiplier: 1, \n offset: 0.0, \n sensorType: \"INA226\",\n calibration: { slope: 1.0, offset: 0.0 }\n },\n RPM: { \n multiplier: 1/5.18, \n offset: 0, \n sensorType: \"PULSE\",\n calibration: { slope: 1.0, offset: 0.0 }\n }\n },\n // Add entries for pico_5, pico_6 etc. if needed\n};\n\n// Define valid ranges for each measurement, *after* final scaling. These use FINAL names.\nconst validRanges = {\n Temperature: { min: -1, max: 110 },\n pH: { min: -0.5, max: 14.5 },\n ORP: { min: -2100, max: 2100 },\n motor_mA: { min: -5, max: 2000 }, \n RPM: { min: 0, max: 500000 } \n};\n\n// Noise floor for 4-20mA signals (applied to the RAW mA input).\nconst noiseFloor_mA = 0.25;\n\n// Measurement name for InfluxDB\nconst influxMeasurement = 'pico_measurements';\n\n// --- End Configuration Section ---\n\n// --- Helper Functions ---\n\n// Scales a RAW mA value to engineering units using config\nfunction scale_mA_Input(rawValue_mA, rangeMin, rangeMax, calibration) {\n let value_mA = rawValue_mA;\n if (value_mA < noiseFloor_mA) {\n value_mA = 0; \n }\n const percent = (value_mA - 4) / 16;\n const clampedPercent = Math.max(0, Math.min(1, percent));\n let scaledValue = rangeMin + clampedPercent * (rangeMax - rangeMin);\n if (calibration) {\n scaledValue = scaledValue * calibration.slope + calibration.offset;\n }\n return scaledValue;\n}\n\n// Validates, scales (based on sensorType config), and checks range\nfunction validateAndScale(rawValue, finalFieldName, deviceId) {\n if (typeof rawValue !== 'number' || isNaN(rawValue) || !isFinite(rawValue)) {\n node.warn(`Invalid raw value for ${finalFieldName} from ${deviceId}: ${rawValue}`);\n return null;\n }\n\n const deviceScalingConfig = scalingFactors[deviceId] || {};\n const sensorConfig = deviceScalingConfig[finalFieldName]; // Config uses FINAL name\n\n if (!sensorConfig) {\n node.warn(`No scaling config found for ${finalFieldName} on ${deviceId}`);\n return rawValue; \n }\n\n let scaledValue = rawValue;\n\n // Apply scaling based on sensor type defined in config\n if (sensorConfig.sensorType === \"mA_INPUT\") {\n scaledValue = scale_mA_Input(rawValue, sensorConfig.rangeMin, sensorConfig.rangeMax, sensorConfig.calibration);\n } else if (sensorConfig.sensorType === \"INA226\" || sensorConfig.sensorType === \"PULSE\") {\n // Apply multiplier and offset if defined\n if (typeof sensorConfig.offset === 'number') {\n // NOTE: For INA226/PULSE, offset might be applied before multiplier depending on need.\n // Current logic: applies offset first, then multiplier.\n scaledValue += sensorConfig.offset;\n }\n if (typeof sensorConfig.multiplier === 'number') {\n scaledValue *= sensorConfig.multiplier;\n }\n // Apply calibration (y = mx + b) if provided - typically after basic scaling\n if (sensorConfig.calibration) {\n scaledValue = scaledValue * sensorConfig.calibration.slope + sensorConfig.calibration.offset;\n }\n } // Add other sensor types here if needed\n\n // Final range validation *after* all scaling and calibration\n const range = validRanges[finalFieldName]; // Validation uses FINAL name\n if (range && (scaledValue < range.min || scaledValue > range.max)) {\n // node.warn(`Scaled value for ${finalFieldName} from ${deviceId} is out of range: ${scaledValue} [${range.min}, ${range.max}]`);\n return null; \n }\n\n return scaledValue;\n}\n\n// --- Main Function Logic ---\n\n// Input data from Buffer Parser (e.g., { Temperature_mA: 12.5, RPM_raw: 3000, ... })\nconst data = msg.payload; \n\n// Try to get reactor number from payload, default to msg.topic or '1'\nconst reactorNum = data.reactor_number || msg.topic || '1'; \nconst deviceId = `${deviceIdPrefix}${reactorNum}`; // Construct device ID (e.g., pico_1)\n\nconst timestamp = Date.now();\n\nconst influxData = {\n measurement: influxMeasurement,\n tags: {\n device: deviceId\n },\n timestamp: timestamp,\n fields: {}\n};\n\n// Define the FINAL field names we expect to process and output\nconst fieldsToProcess = [\"Temperature\", \"pH\", \"ORP\", \"motor_mA\", \"RPM\"];\n\n// Map FINAL names to their corresponding RAW input names from the parser\nconst inputNameMapping = {\n \"Temperature\": \"Temperature_mA\",\n \"pH\": \"pH_mA\",\n \"ORP\": \"ORP_mA\",\n \"RPM\": \"RPM_raw\", // <-- Added mapping for RPM\n \"motor_mA\": \"motor_mA\" // motor_mA doesn't change name\n};\n\nfor (const finalField of fieldsToProcess) {\n // Get the corresponding input field name from the parser output\n const inputFieldName = inputNameMapping[finalField];\n\n // Check if the expected input field exists in the data\n if (data.hasOwnProperty(inputFieldName)) {\n const rawValue = data[inputFieldName];\n // Validate and scale the raw value using the FINAL field name for config lookup\n const scaledValue = validateAndScale(rawValue, finalField, deviceId);\n \n if (scaledValue !== null) {\n // Add the scaled value to InfluxDB fields using the FINAL field name\n influxData.fields[finalField] = scaledValue;\n }\n } else {\n // node.warn(`Input field '${inputFieldName}' not found in payload for ${deviceId}`);\n }\n}\n\n\nif (Object.keys(influxData.fields).length > 0) {\n if (data.hasOwnProperty('reactor_number')) {\n influxData.fields.reactor_number = data.reactor_number;\n }\n msg.payload = [influxData]; \n return msg;\n} else {\n node.warn(`No valid fields to send for ${deviceId}`);\n return null; \n}\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1140, + "y": 320, + "wires": [ + [ + "fad901313d420969", + "7caeda9c35084969", + "adc8f59fdc0b3027" + ] + ] + }, + { + "id": "0d1c24a539a6f95a", + "type": "buffer-parser", + "z": "ac978cd8aec44371", + "name": "Parse Pico 4", + "data": "payload", + "dataType": "msg", + "specification": "spec", + "specificationType": "ui", + "items": [ + { + "type": "floatle", + "name": "Temperature_mA", + "offset": 0, + "length": 1, + "offsetbit": 0, + "scale": "1", + "mask": "" + }, + { + "type": "floatle", + "name": "pH_mA", + "offset": 4, + "length": 1, + "offsetbit": 0, + "scale": "1", + "mask": "" }, { - "type": "uint32le", - "name": "Timestamp", - "offset": 24, + "type": "floatle", + "name": "ORP_mA", + "offset": 8, "length": 1, "offsetbit": 0, "scale": "1", @@ -2755,8 +695,8 @@ }, { "type": "floatle", - "name": "Power Supply (V) ", - "offset": 28, + "name": "RPM_raw", + "offset": 12, "length": 1, "offsetbit": 0, "scale": "1", @@ -2764,8 +704,17 @@ }, { "type": "floatle", - "name": "Battery (V)", - "offset": 32, + "name": "motor_mA", + "offset": 16, + "length": 1, + "offsetbit": 0, + "scale": "1", + "mask": "" + }, + { + "type": "uint16le", + "name": "reactor_number", + "offset": 20, "length": 1, "offsetbit": 0, "scale": "1", @@ -2779,227 +728,397 @@ "swap2Type": "swap", "swap3Type": "swap", "msgProperty": "payload", - "msgPropertyType": "str", + "msgPropertyType": "msg", "resultType": "keyvalue", - "resultTypeType": "return", + "resultTypeType": "output", "multipleResult": false, "fanOutMultipleResult": false, "setTopic": true, "outputs": 1, - "x": 730, - "y": 453.33333333333337, + "x": 850, + "y": 540, "wires": [ [ - "157bdeaf2b6a360a" + "e08e3d9d2b8f10e9", + "2722f372f6020fb5" ] ] }, { - "id": "5b3c52ea8f15ce17", + "id": "7caeda9c35084969", "type": "debug", - "z": "1389333f9aa0bc16", - "name": "debug 19", + "z": "ac978cd8aec44371", + "name": "Final Scaled Output", "active": false, "tosidebar": true, "console": false, "tostatus": false, - "complete": "false", + "complete": "payload", + "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 980, - "y": 80, + "x": 1390, + "y": 280, "wires": [] }, { - "id": "118b254427b33a50", - "type": "function", - "z": "1389333f9aa0bc16", - "name": "function 1", - "func": "const data = msg.payload;\n\n// Define a fixed device ID since it's only coming from gas meter 1\nconst deviceId = 'gas_meter_1';\n\n// Convert timestamp to milliseconds (assuming precision in InfluxDB config is 'ms')\nconst timestamp = Math.floor(data[\"Timestamp\"] * 1000);\n\nmsg.measurement = 'gas_meter_1';\nmsg.tags = { device: deviceId };\nmsg.timestamp = timestamp;\nmsg.fields = {\n Volume_mL: parseFloat(data[\"Volume (mL)\"]),\n Volume_Normalised_mL: parseFloat(data[\"Volume Normalised (mL)\"]),\n Flow_mL_min: parseFloat(data[\"Flow (mL/min)\"]),\n Flow_Normalised_mL_min: parseFloat(data[\"Flow Normalised (mL/min)\"]),\n Temperature_C: parseFloat(data[\"Temperature (°C)\"]),\n Pressure_hPa: parseFloat(data[\"Pressure (hPa)\"]),\n Power_Supply_V: parseFloat(data[\"Power Supply (V) \"]),\n Battery_V: parseFloat(data[\"Battery (V)\"])\n};\n\nreturn msg;\n", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 940, - "y": 240, - "wires": [ - [ - "90bab4670523a2a9", - "6a8ac88abc4480c5", - "9e75a0cb14e9d129" - ] - ] - }, - { - "id": "90bab4670523a2a9", - "type": "influxdb out", - "z": "1389333f9aa0bc16", - "influxdb": "d9a29a25883258e7", - "name": "", - "measurement": "", + "id": "adc8f59fdc0b3027", + "type": "influxdb batch", + "z": "ac978cd8aec44371", + "influxdb": "influxconfig", "precision": "", "retentionPolicy": "", + "name": "Influx Batch (Scion)", "database": "database", "precisionV18FluxV20": "ms", "retentionPolicyV18Flux": "", - "org": "Cetogenix", + "org": "Scion", "bucket": "reactor-monitoring", - "x": 1260, - "y": 260, + "x": 1410, + "y": 400, + "wires": [] + }, + { + "id": "2722f372f6020fb5", + "type": "debug", + "z": "ac978cd8aec44371", + "name": "Pico 4 Parsed (raw)", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 870, + "y": 600, + "wires": [] + }, + { + "id": "84866463b42b114a", + "type": "debug", + "z": "ac978cd8aec44371", + "name": "Pico 3 Parsed (raw)", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 870, + "y": 480, + "wires": [] + }, + { + "id": "2a736e67f3c7ea1c", + "type": "debug", + "z": "ac978cd8aec44371", + "name": "Pico 2 Parsed (raw)", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 870, + "y": 400, "wires": [] }, { - "id": "6a8ac88abc4480c5", + "id": "passthrough_debug", "type": "debug", - "z": "1389333f9aa0bc16", - "name": "debug 20", + "z": "ac978cd8aec44371", + "name": "Modbus Status", "active": false, "tosidebar": true, "console": false, "tostatus": false, - "complete": "false", + "complete": "payload", + "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 1150, - "y": 100, - "wires": [] - }, - { - "id": "9e75a0cb14e9d129", - "type": "influxdb out", - "z": "1389333f9aa0bc16", - "influxdb": "influxconfig", - "name": "", - "measurement": "", - "precision": "", - "retentionPolicy": "", - "database": "database", - "precisionV18FluxV20": "ms", - "retentionPolicyV18Flux": "", - "org": "Scion", - "bucket": "reactor-monitoring", - "x": 1260, - "y": 340, + "x": 1100, + "y": 680, "wires": [] }, { - "id": "aa764ef5d4c2d5a0", - "type": "function", - "z": "1389333f9aa0bc16", - "name": "Prepare InfluxDB Data", - "func": "const data = msg.payload;\n\n// Determine the device ID\nconst deviceId = msg.unitid ? `gas_meter_${msg.unitid}` : 'gas_meter_1';\n\n// Since precision is set to 'ms' in InfluxDB config, convert timestamp to milliseconds\nconst timestamp = Math.floor(data[\"Timestamp\"] * 1000);\n\nconst influxData = {\n measurement: 'gas_meter',\n tags: {\n device: deviceId\n },\n timestamp: timestamp,\n fields: {\n Volume_mL: parseFloat(data[\"Volume (mL)\"]),\n Volume_Normalised_mL: parseFloat(data[\"Volume Normalised (mL)\"]),\n Flow_mL_min: parseFloat(data[\"Flow (mL/min)\"]),\n Flow_Normalised_mL_min: parseFloat(data[\"Flow Normalised (mL/min)\"]),\n Temperature_C: parseFloat(data[\"Temperature (°C)\"]),\n Pressure_hPa: parseFloat(data[\"Pressure (hPa)\"]),\n Power_Supply_V: parseFloat(data[\"Power Supply (V) \"]),\n Battery_V: parseFloat(data[\"Battery (V)\"])\n }\n};\n\nmsg.payload = [influxData]; // Wrap in array as expected by InfluxDB node\nreturn msg;", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 840, - "y": 800, + "id": "rpi_reboot_exec", + "type": "exec", + "z": "ac978cd8aec44371", + "command": "", + "addpay": "payload", + "append": "", + "useSpawn": "false", + "timer": "", + "winHide": false, + "name": "Execute Reboot", + "x": 1110, + "y": 740, "wires": [ - [ - "7326f3201ea38f6d", - "6a1b196baf7102b9" - ] + [], + [], + [] ] }, { - "id": "7326f3201ea38f6d", - "type": "influxdb batch", - "z": "1389333f9aa0bc16", - "influxdb": "d9a29a25883258e7", - "precision": "", - "retentionPolicy": "", - "name": "Local", - "database": "database", - "precisionV18FluxV20": "ms", - "retentionPolicyV18Flux": "", - "org": "Cetogenix", - "bucket": "reactor-monitoring", - "x": 1050, - "y": 720, - "wires": [] - }, - { - "id": "6a1b196baf7102b9", - "type": "influxdb batch", - "z": "1389333f9aa0bc16", - "influxdb": "influxconfig", - "precision": "", - "retentionPolicy": "", - "name": "Scion", - "database": "database", - "precisionV18FluxV20": "ms", - "retentionPolicyV18Flux": "", - "org": "Scion", - "bucket": "reactor-monitoring", - "x": 1030, - "y": 840, + "id": "reboot_debug", + "type": "debug", + "z": "ac978cd8aec44371", + "name": "Reboot Triggered", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 1110, + "y": 800, "wires": [] }, { - "id": "4b0b0633eeaafd92", + "id": "32b1f66c58487512", "type": "function", - "z": "1389333f9aa0bc16", - "name": "function 2", - "func": "const data = msg.payload;\n\n// Define a fixed device ID since it's only coming from gas meter 1\nconst deviceId = 'gas_meter_2';\n\n// Convert timestamp to milliseconds (assuming precision in InfluxDB config is 'ms')\nconst timestamp = Math.floor(data[\"Timestamp\"] * 1000);\n\nmsg.measurement = 'gas_meter_2';\nmsg.tags = { device: deviceId };\nmsg.timestamp = timestamp;\nmsg.fields = {\n Volume_mL: parseFloat(data[\"Volume (mL)\"]),\n Volume_Normalised_mL: parseFloat(data[\"Volume Normalised (mL)\"]),\n Flow_mL_min: parseFloat(data[\"Flow (mL/min)\"]),\n Flow_Normalised_mL_min: parseFloat(data[\"Flow Normalised (mL/min)\"]),\n Temperature_C: parseFloat(data[\"Temperature (°C)\"]),\n Pressure_hPa: parseFloat(data[\"Pressure (hPa)\"]),\n Power_Supply_V: parseFloat(data[\"Power Supply (V) \"]),\n Battery_V: parseFloat(data[\"Battery (V)\"])\n};\n\nreturn msg;\n", - "outputs": 1, + "z": "ac978cd8aec44371", + "name": "Modbus Watchdog (10min)", + "func": "// Configuration\nconst REBOOT_DELAY_MS = 10 * 60 * 1000; // 10 minutes\nconst REBOOT_COMMAND = \"sudo /sbin/reboot\";\nconst FLOW_VAR_REBOOT_COUNT = \"rebootCount_modbus\";\n\nlet timerId = context.get(\"watchdogTimerId\");\nlet rebootCount = flow.get(FLOW_VAR_REBOOT_COUNT) || 0;\n\n// msg.error is set by modbus-flex-getter on its second output for errors.\n// If msg comes from first output (success), msg.error will not be present or be falsy.\nlet isError = msg.hasOwnProperty('error') && msg.error !== null;\n\nif (isError) {\n // Error detected\n if (!timerId) { // Start timer only if not already running\n node.status({ fill: \"red\", shape: \"dot\", text: `Error. Timer (10m) started. Reboots: ${rebootCount}` });\n \n // Store the first error message that triggers the timer\n context.set(\"triggeringErrorMsg\", msg);\n\n timerId = setTimeout(() => {\n let currentRebootCount = flow.get(FLOW_VAR_REBOOT_COUNT) || 0; // Get latest count\n currentRebootCount++;\n flow.set(FLOW_VAR_REBOOT_COUNT, currentRebootCount);\n \n let originalErrorMsg = context.get(\"triggeringErrorMsg\") || {payload: \"Unknown error (context cleared)\"}; // Get the stored error\n \n node.status({ fill: \"red\", shape: \"ring\", text: `Rebooting! Total: ${currentRebootCount}` });\n \n node.send([\n null, \n { \n payload: REBOOT_COMMAND, \n rebootCount: currentRebootCount,\n triggeringError: originalErrorMsg // Send the error that caused the reboot\n }\n ]);\n context.set(\"watchdogTimerId\", null); // Clear timerId after firing\n context.set(\"triggeringErrorMsg\", null); // Clear stored error\n }, REBOOT_DELAY_MS);\n context.set(\"watchdogTimerId\", timerId);\n } else {\n // Timer already running, error persists\n node.status({ fill: \"red\", shape: \"dot\", text: `Error ongoing. Timer active. Reboots: ${rebootCount}` });\n }\n} else {\n // Success\n if (timerId) {\n clearTimeout(timerId);\n context.set(\"watchdogTimerId\", null);\n context.set(\"triggeringErrorMsg\", null); // Clear stored error on success\n node.status({ fill: \"green\", shape: \"dot\", text: `Success. Timer cleared. Reboots: ${rebootCount}` });\n } else {\n node.status({ fill: \"green\", shape: \"dot\", text: `Success. Healthy. Reboots: ${rebootCount}` });\n }\n}\n\n// Always pass through the original incoming message (success or error) on output 1\n// The timer, when it fires, sends its own message on output 2.\nreturn [msg, null];", + "outputs": 2, "timeout": 0, "noerr": 0, - "initialize": "", - "finalize": "", + "initialize": "// Code here will be run when the node is started/deployed\nconst FLOW_VAR_REBOOT_COUNT = \"rebootCount_modbus\";\nlet rebootCount = flow.get(FLOW_VAR_REBOOT_COUNT) || 0;\nflow.set(FLOW_VAR_REBOOT_COUNT, rebootCount); // Ensure it's initialized in flow context\nnode.status({fill:\"blue\", shape:\"ring\", text: \"Watchdog ready. Reboots: \" + rebootCount});\n\n// Clear any orphaned timer from a previous unclean shutdown/deploy\nlet oldTimerId = context.get(\"watchdogTimerId\");\nif (oldTimerId) {\n clearTimeout(oldTimerId);\n context.set(\"watchdogTimerId\", null);\n node.warn(\"Cleared orphaned watchdog timer on initialize.\");\n}\ncontext.set(\"triggeringErrorMsg\", null);", + "finalize": "// Code here will be run when the node is stopped/removed\nlet timerId = context.get(\"watchdogTimerId\");\nif (timerId) {\n clearTimeout(timerId);\n context.set(\"watchdogTimerId\", null);\n}\nnode.status({});", "libs": [], - "x": 940, - "y": 320, + "x": 840, + "y": 740, "wires": [ [ - "90bab4670523a2a9", - "9e75a0cb14e9d129" + "passthrough_debug" + ], + [ + "reboot_debug", + "rpi_reboot_exec" ] ] }, { - "id": "c1fda35a740f781d", - "type": "function", - "z": "1389333f9aa0bc16", - "name": "function 3", - "func": "const data = msg.payload;\n\n// Define a fixed device ID since it's only coming from gas meter 1\nconst deviceId = 'gas_meter_3';\n\n// Convert timestamp to milliseconds (assuming precision in InfluxDB config is 'ms')\nconst timestamp = Math.floor(data[\"Timestamp\"] * 1000);\n\nmsg.measurement = 'gas_meter_3';\nmsg.tags = { device: deviceId };\nmsg.timestamp = timestamp;\nmsg.fields = {\n Volume_mL: parseFloat(data[\"Volume (mL)\"]),\n Volume_Normalised_mL: parseFloat(data[\"Volume Normalised (mL)\"]),\n Flow_mL_min: parseFloat(data[\"Flow (mL/min)\"]),\n Flow_Normalised_mL_min: parseFloat(data[\"Flow Normalised (mL/min)\"]),\n Temperature_C: parseFloat(data[\"Temperature (°C)\"]),\n Pressure_hPa: parseFloat(data[\"Pressure (hPa)\"]),\n Power_Supply_V: parseFloat(data[\"Power Supply (V) \"]),\n Battery_V: parseFloat(data[\"Battery (V)\"])\n};\n\nreturn msg;\n", - "outputs": 1, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 940.2000122070312, - "y": 387.1999816894531, + "id": "c2e01c6472b93059", + "type": "inject", + "z": "ac978cd8aec44371", + "name": "Get Stored Reboot Count", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "getRebootCount", + "payload": "", + "payloadType": "date", + "x": 350, + "y": 880, "wires": [ [ - "90bab4670523a2a9", - "9e75a0cb14e9d129" + "8489a18d77ca87d5" ] ] }, { - "id": "157bdeaf2b6a360a", + "id": "8489a18d77ca87d5", "type": "function", - "z": "1389333f9aa0bc16", - "name": "function 4", - "func": "const data = msg.payload;\n\n// Define a fixed device ID since it's only coming from gas meter 1\nconst deviceId = 'gas_meter_4';\n\n// Convert timestamp to milliseconds (assuming precision in InfluxDB config is 'ms')\nconst timestamp = Math.floor(data[\"Timestamp\"] * 1000);\n\nmsg.measurement = 'gas_meter_4';\nmsg.tags = { device: deviceId };\nmsg.timestamp = timestamp;\nmsg.fields = {\n Volume_mL: parseFloat(data[\"Volume (mL)\"]),\n Volume_Normalised_mL: parseFloat(data[\"Volume Normalised (mL)\"]),\n Flow_mL_min: parseFloat(data[\"Flow (mL/min)\"]),\n Flow_Normalised_mL_min: parseFloat(data[\"Flow Normalised (mL/min)\"]),\n Temperature_C: parseFloat(data[\"Temperature (°C)\"]),\n Pressure_hPa: parseFloat(data[\"Pressure (hPa)\"]),\n Power_Supply_V: parseFloat(data[\"Power Supply (V) \"]),\n Battery_V: parseFloat(data[\"Battery (V)\"])\n};\n\nreturn msg;\n", + "z": "ac978cd8aec44371", + "name": "Retrieve Reboot Count", + "func": "const FLOW_VAR_REBOOT_COUNT = \"rebootCount_modbus\";\nlet count = flow.get(FLOW_VAR_REBOOT_COUNT) || 0;\nmsg.payload = \"Current Stored Reboot Count: \" + count;\n// You could also update this node's status if desired\n// node.status({text: \"Reboots: \" + count});\nreturn msg;", "outputs": 1, - "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 918.2000122070312, - "y": 445.1999816894531, + "x": 610, + "y": 880, "wires": [ [ - "90bab4670523a2a9", - "9e75a0cb14e9d129" + "0a8c33543b3987a7" ] ] + }, + { + "id": "0a8c33543b3987a7", + "type": "debug", + "z": "ac978cd8aec44371", + "name": "Current Reboot Count", + "active": true, + "tosidebar": true, + "console": true, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 860, + "y": 880, + "wires": [] + }, + { + "id": "a1f84dec681b9b29", + "type": "modbus-client", + "name": "pico-2", + "clienttype": "simpleser", + "bufferCommands": true, + "stateLogEnabled": true, + "queueLogEnabled": false, + "failureLogEnabled": true, + "tcpHost": "127.0.0.1", + "tcpPort": "502", + "tcpType": "DEFAULT", + "serialPort": "/dev/pico-2", + "serialType": "RTU", + "serialBaudrate": "115200", + "serialDatabits": "8", + "serialStopbits": "1", + "serialParity": "none", + "serialConnectionDelay": "100", + "serialAsciiResponseStartDelimiter": "0x3A", + "unit_id": "1", + "commandDelay": "1", + "clientTimeout": "1000", + "reconnectOnTimeout": true, + "reconnectTimeout": "2000", + "parallelUnitIdsAllowed": true, + "showErrors": true, + "showWarnings": true, + "showLogs": true + }, + { + "id": "ef99d96f993392a8", + "type": "modbus-client", + "name": "pico-3", + "clienttype": "simpleser", + "bufferCommands": true, + "stateLogEnabled": false, + "queueLogEnabled": false, + "failureLogEnabled": true, + "tcpHost": "127.0.0.1", + "tcpPort": "502", + "tcpType": "DEFAULT", + "serialPort": "/dev/pico-3", + "serialType": "RTU", + "serialBaudrate": "115200", + "serialDatabits": "8", + "serialStopbits": "1", + "serialParity": "none", + "serialConnectionDelay": "100", + "serialAsciiResponseStartDelimiter": "0x3A", + "unit_id": 1, + "commandDelay": 1, + "clientTimeout": 1000, + "reconnectOnTimeout": true, + "reconnectTimeout": 2000, + "parallelUnitIdsAllowed": true, + "showErrors": false, + "showWarnings": true, + "showLogs": true + }, + { + "id": "mbclient1", + "type": "modbus-client", + "name": "pico-4", + "clienttype": "serial", + "bufferCommands": false, + "stateLogEnabled": true, + "queueLogEnabled": false, + "failureLogEnabled": true, + "tcpHost": "127.0.0.1", + "tcpPort": "502", + "tcpType": "DEFAULT", + "serialPort": "/dev/pico-4", + "serialType": "RTU", + "serialBaudrate": "115200", + "serialDatabits": "8", + "serialStopbits": "1", + "serialParity": "none", + "serialConnectionDelay": "100", + "serialAsciiResponseStartDelimiter": "", + "unit_id": 1, + "commandDelay": 1, + "clientTimeout": 1000, + "reconnectOnTimeout": false, + "reconnectTimeout": 2000, + "parallelUnitIdsAllowed": false, + "showErrors": false, + "showWarnings": true, + "showLogs": true + }, + { + "id": "61951d0040aac1f3", + "type": "modbus-client", + "name": "pico-1", + "clienttype": "simpleser", + "bufferCommands": true, + "stateLogEnabled": true, + "queueLogEnabled": false, + "failureLogEnabled": true, + "tcpHost": "127.0.0.1", + "tcpPort": "502", + "tcpType": "DEFAULT", + "serialPort": "/dev/pico-1", + "serialType": "RTU", + "serialBaudrate": "115200", + "serialDatabits": "8", + "serialStopbits": "1", + "serialParity": "none", + "serialConnectionDelay": "100", + "serialAsciiResponseStartDelimiter": "0x3A", + "unit_id": "1", + "commandDelay": "1", + "clientTimeout": "5000", + "reconnectOnTimeout": true, + "reconnectTimeout": "2000", + "parallelUnitIdsAllowed": true, + "showErrors": true, + "showWarnings": true, + "showLogs": true + }, + { + "id": "d9a29a25883258e7", + "type": "influxdb", + "hostname": "127.0.0.1", + "port": "8086", + "protocol": "http", + "database": "reactor_db", + "name": "Local InfluxDB", + "usetls": false, + "tls": "", + "influxdbVersion": "2.0", + "url": "http://localhost:8086", + "timeout": "", + "rejectUnauthorized": false + }, + { + "id": "influxconfig", + "type": "influxdb", + "hostname": "127.0.0.1", + "port": "8086", + "protocol": "http", + "database": "database", + "name": "Scion InfluxDB", + "usetls": false, + "tls": "", + "influxdbVersion": "2.0", + "url": "https://influxdb.scionresearch.com", + "timeout": "10", + "rejectUnauthorized": false } ] \ No newline at end of file diff --git a/grafana.json b/grafana.json index c7dee71..ac79d50 100644 --- a/grafana.json +++ b/grafana.json @@ -1,617 +1,2015 @@ { - "annotations": { - "list": [ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Dashboard for monitoring the Low Rate Reactors (LRR) as part of the BPA project", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 7, + "links": [], + "liveNow": true, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 25, + "panels": [], + "title": "Reactor Values", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 9, + "axisSoftMin": 6, + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "pH" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ { - "builtIn": 1, "datasource": { - "type": "grafana", - "uid": "-- Grafana --" + "type": "influxdb", + "uid": "aeerm33apqrr4c" }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] =~ /^pico_[1-4]$/) // Matches pico_1, pico_2, pico_3, pico_4\r\n |> filter(fn: (r) => r[\"_field\"] == \"pH\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Reactor \" + strings.substring(v: r.device, start: 5, end: 6) + \" pH\", // Labels as \"Pico 1 pH\"\r\n\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")\r\n", + "refId": "B" } - ] + ], + "title": "pH", + "type": "timeseries" }, - "description": "Dashboard for monitoring the Low Rate Reactors (LRR) as part of the BPA project", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 1, - "id": 8, - "links": [], - "liveNow": true, - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": true, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": -600, + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "dashed" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "dark-red", + "value": 0 + } + ] + } }, - "id": 7, - "panels": [], - "title": "Overview", - "type": "row" + "overrides": [] }, - { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] =~ /^pico_[1-4]$/) // Matches pico_1, pico_2, pico_3, pico_4\r\n |> filter(fn: (r) => r[\"_field\"] == \"ORP\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Reactor \" + strings.substring(v: r.device, start: 5, end: 6) + \" ORP\", \r\n\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")\r\n", + "refId": "B" + } + ], + "title": "ORP", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 39, + "axisSoftMin": 35, + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "dashed" } }, - "overrides": [ - { - "matcher": { - "id": "byValue", - "options": { - "op": "gte", - "reducer": "lastNotNull", - "value": 200 - } + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null }, - "properties": [ - { - "id": "custom.axisPlacement", - "value": "right" + { + "color": "dark-red", + "value": 36 + }, + { + "color": "dark-green", + "value": 37 + }, + { + "color": "dark-red", + "value": 38 + } + ] + }, + "unit": "celsius" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + }, + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "Reactor 1 Temperature", + "Reactor 2 Temperature", + "Reactor 4 Temperature" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true } - ] - } - ] + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 9 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 1 + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": true, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\" \r\n or r[\"_field\"] == \"Power Supply (V)\" \r\n or r[\"_field\"] == \"Flow Normalised (mL/min)\" \r\n or r[\"_field\"] == \"Pressure (hPa)\" \r\n or r[\"_field\"] == \"Temperature (°C)\" \r\n or r[\"_field\"] == \"Volume (mL)\" \r\n or r[\"_field\"] == \"Volume Normalised (mL)\" \r\n or r[\"_field\"] == \"Battery (V)\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter 1 - \" + r._field\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")\r\n", + "refId": "A" }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "hideZeros": false, - "mode": "single", - "sort": "none" + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] =~ /^pico_[1-4]$/) // Matches pico_1, pico_2, pico_3, pico_4\r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Reactor \" + strings.substring(v: r.device, start: 5, end: 6) + \" Temperature\", \r\n\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")\r\n", + "refId": "B" + } + ], + "title": "Temperature", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "max": 20, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "pluginVersion": "11.5.2", - "targets": [ + "overrides": [ { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } }, - "hide": false, - "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\" \r\n or r[\"_field\"] == \"Power Supply (V)\" \r\n or r[\"_field\"] == \"Flow Normalised (mL/min)\" \r\n or r[\"_field\"] == \"Pressure (hPa)\" \r\n or r[\"_field\"] == \"Temperature (°C)\" \r\n or r[\"_field\"] == \"Volume (mL)\" \r\n or r[\"_field\"] == \"Volume Normalised (mL)\" \r\n or r[\"_field\"] == \"Battery (V)\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter 1 - \" + r._field\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")\r\n", - "refId": "A" + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 9 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] =~ /^pico_[1-4]$/) // Matches pico_1, pico_2, pico_3, pico_4\r\n |> filter(fn: (r) => r[\"_field\"] == \"RPM\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Reactor \" + strings.substring(v: r.device, start: 5, end: 6) + \" RPM\", \r\n\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")\r\n", + "refId": "B" + } + ], + "title": "RPM", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } }, - "hide": false, - "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] == \"pico_1\") \r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature\" or r[\"_field\"] == \"pH\" or r[\"_field\"] == \"ORP\" or r[\"_field\"] == \"motor_mA\" or r[\"_field\"] == \"RPM\")\r\n// // Filter out unreasonable values (adjust thresholds as needed). Use the *engineering unit* ranges.\r\n// |> filter(fn: (r) => \r\n// (r._field == \"motor_mA\" and r._value < 2000 and r._value >= -5) and\r\n// (r._field == \"Temperature\" and r._value < 110 and r._value >= -1) and\r\n// (r._field == \"pH\" and r._value < 14.5 and r._value >= -0.5) and\r\n// (r._field == \"ORP\" and r._value < 2100 and r._value >= -2100) and\r\n// (r._field == \"RPM\" and r._value < 60000 and r._value >= -1)\r\n// )\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Pico \" + strings.substring(v: r.device, start: 5, end: 6) + \" - \" + r._field //Simplified field naming\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")", - "refId": "B" + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] } - ], - "title": "Reactor 1", - "type": "timeseries" + ] }, - { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 9 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] =~ /^pico_[1-2]$/) // Matches pico_1, pico_2, pico_3, pico_4\r\n |> filter(fn: (r) => r[\"_field\"] == \"motor_mA\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Reactor \" + strings.substring(v: r.device, start: 5, end: 6) + \" Motor Current (mA)\", \r\n\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")\r\n", + "refId": "B" + } + ], + "title": "Motor Current", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 6, + "panels": [], + "title": "Flow Meter Values", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "description": "Moving Average over last 10readings", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [ - { - "matcher": { - "id": "byValue", - "options": { - "op": "gte", - "reducer": "lastNotNull", - "value": 200 - } + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null }, - "properties": [ - { - "id": "custom.axisPlacement", - "value": "right" - } - ] - } - ] + { + "color": "red", + "value": 80 + } + ] + } }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 1 + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] =~ /^gas_meter_[1-4]$/) \r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow Normalised (mL/min)\")\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) // Smooths based on the dashboard time window\r\n |> movingAverage(n: 10) \r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter \" + strings.substring(v: r._measurement, start: 10, end: 11) + \" - Flow Normalised (mL/min)\"\r\n }))\r\n |> yield(name: \"gas_meters\")\r\n", + "refId": "A" }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "hideZeros": false, - "mode": "single", - "sort": "none" + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": true, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] =~ /^gas_meter_[1-4]$/) // Matches gas_meter_1 to gas_meter_4\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow Normalised (mL/min)\")\r\n |> movingAverage(n: 1000)\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter \" + strings.substring(v: r._measurement, start: 10, end: 11) + \" - Flow Normalised (mL/min)\",\r\n\r\n }))\r\n |> yield(name: \"gas_meters\")", + "refId": "B" + } + ], + "title": "Flow Normalised (mL/min)", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "pluginVersion": "11.5.2", - "targets": [ + "overrides": [ { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "matcher": { + "id": "byName", + "options": "gas_meter_1 Volume Normalised (mL)" }, - "hide": false, - "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\" \r\n or r[\"_field\"] == \"Power Supply (V)\" \r\n or r[\"_field\"] == \"Flow Normalised (mL/min)\" \r\n or r[\"_field\"] == \"Pressure (hPa)\" \r\n or r[\"_field\"] == \"Temperature (°C)\" \r\n or r[\"_field\"] == \"Volume (mL)\" \r\n or r[\"_field\"] == \"Volume Normalised (mL)\" \r\n or r[\"_field\"] == \"Battery (V)\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter 1 - \" + r._field\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")\r\n", - "refId": "A" + "properties": [ + { + "id": "displayName", + "value": "Gas Meter 1 - Volume Normalised (mL)" + } + ] }, { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "matcher": { + "id": "byName", + "options": "gas_meter_2 Volume Normalised (mL)" }, - "hide": false, - "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] == \"pico_2\") \r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature\" or r[\"_field\"] == \"pH\" or r[\"_field\"] == \"ORP\" or r[\"_field\"] == \"motor_mA\" or r[\"_field\"] == \"RPM\")\r\n// // Filter out unreasonable values (adjust thresholds as needed). Use the *engineering unit* ranges.\r\n// |> filter(fn: (r) => \r\n// (r._field == \"motor_mA\" and r._value < 2000 and r._value >= -5) and\r\n// (r._field == \"Temperature\" and r._value < 110 and r._value >= -1) and\r\n// (r._field == \"pH\" and r._value < 14.5 and r._value >= -0.5) and\r\n// (r._field == \"ORP\" and r._value < 2100 and r._value >= -2100) and\r\n// (r._field == \"RPM\" and r._value < 60000 and r._value >= -1)\r\n// )\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Pico \" + strings.substring(v: r.device, start: 5, end: 6) + \" - \" + r._field //Simplified field naming\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")", - "refId": "B" + "properties": [ + { + "id": "displayName", + "value": "Gas Meter 2 - Volume Normalised (mL)" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "gas_meter_3 Volume Normalised (mL)" + }, + "properties": [ + { + "id": "displayName", + "value": "Gas Meter 3 - Volume Normalised (mL)" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "gas_meter_4 Volume Normalised (mL)" + }, + "properties": [ + { + "id": "displayName", + "value": "Gas Meter 4 - Volume Normalised (mL)" + } + ] } - ], - "title": "Reactor 2", - "type": "timeseries" + ] }, - { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 23, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_4\" or r[\"_measurement\"] == \"gas_meter_3\" or r[\"_measurement\"] == \"gas_meter_2\" or r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Volume Normalised (mL)\")\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Volume Normalised (mL)", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "description": "Moving average over last 1 reading", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [ - { - "matcher": { - "id": "byValue", - "options": { - "op": "gte", - "reducer": "lastNotNull", - "value": 200 - } + "mappings": [], + "max": 8, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null }, - "properties": [ - { - "id": "custom.axisPlacement", - "value": "right" - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 9 - }, - "id": 3, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "hideZeros": false, - "mode": "single", - "sort": "none" + { + "color": "red", + "value": 80 + } + ] } }, - "pluginVersion": "11.5.2", - "targets": [ + "overrides": [ { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } }, - "hide": false, - "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\" \r\n or r[\"_field\"] == \"Power Supply (V)\" \r\n or r[\"_field\"] == \"Flow Normalised (mL/min)\" \r\n or r[\"_field\"] == \"Pressure (hPa)\" \r\n or r[\"_field\"] == \"Temperature (°C)\" \r\n or r[\"_field\"] == \"Volume (mL)\" \r\n or r[\"_field\"] == \"Volume Normalised (mL)\" \r\n or r[\"_field\"] == \"Battery (V)\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter 1 - \" + r._field\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")\r\n", - "refId": "A" + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 26 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" }, - { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] =~ /^gas_meter_[1-4]$/) // Matches gas_meter_1 to gas_meter_4\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\")\r\n |> movingAverage(n: 1) // Smooths spikes by averaging over the last 1000 readings\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter \" + strings.substring(v: r._measurement, start: 10, end: 11) + \" - Flow (mL/min)\",\r\n\r\n }))\r\n |> yield(name: \"gas_meters\")\r\n", + "refId": "A" + } + ], + "title": "Flow (mL/min)", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "hide": false, - "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] == \"pico_3\") \r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature\" or r[\"_field\"] == \"pH\" or r[\"_field\"] == \"ORP\" or r[\"_field\"] == \"motor_mA\" or r[\"_field\"] == \"RPM\")\r\n// // Filter out unreasonable values (adjust thresholds as needed). Use the *engineering unit* ranges.\r\n// |> filter(fn: (r) => \r\n// (r._field == \"motor_mA\" and r._value < 2000 and r._value >= -5) and\r\n// (r._field == \"Temperature\" and r._value < 110 and r._value >= -1) and\r\n// (r._field == \"pH\" and r._value < 14.5 and r._value >= -0.5) and\r\n// (r._field == \"ORP\" and r._value < 2100 and r._value >= -2100) and\r\n// (r._field == \"RPM\" and r._value < 60000 and r._value >= -1)\r\n// )\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Pico \" + strings.substring(v: r.device, start: 5, end: 6) + \" - \" + r._field //Simplified field naming\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")", - "refId": "B" + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } - ], - "title": "Reactor 3", - "type": "timeseries" + }, + "overrides": [] }, - { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 26 + }, + "id": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_4\" or r[\"_measurement\"] == \"gas_meter_3\" or r[\"_measurement\"] == \"gas_meter_2\" or r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Volume (mL)\")\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")", + "refId": "A" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": true, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] =~ /^gas_meter_[1-4]$/) // Matches gas_meter_1 to gas_meter_4\r\n |> filter(fn: (r) => r[\"_field\"] == \"Volume (mL)\") // Direct match instead of contains()\r\n |> aggregateWindow(every: 30m, fn: mean, createEmpty: false) // Ensures data every 10 min\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter \" + strings.substring(v: r._measurement, start: 10, end: 11) + \" - \" + r._field,\r\n meter: r._measurement // Retain meter identifier for Grafana filtering\r\n }))\r\n |> yield(name: \"gas_meters\")\r\n", + "refId": "B" + } + ], + "title": "Volume (mL)", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [ - { - "matcher": { - "id": "byValue", - "options": { - "op": "gte", - "reducer": "lastNotNull", - "value": 200 - } + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null }, - "properties": [ - { - "id": "custom.axisPlacement", - "value": "right" - } - ] - } - ] + { + "color": "red", + "value": 80 + } + ] + } }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 9 + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 26 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "hideZeros": false, - "mode": "single", - "sort": "none" + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_4\" or r[\"_measurement\"] == \"gas_meter_3\" or r[\"_measurement\"] == \"gas_meter_2\" or r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Pressure (hPa)\")\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Pressure (hPa)", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 30, + "axisSoftMin": 0, + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "pluginVersion": "11.5.2", - "targets": [ + "overrides": [ { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } }, - "hide": false, - "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\" \r\n or r[\"_field\"] == \"Power Supply (V)\" \r\n or r[\"_field\"] == \"Flow Normalised (mL/min)\" \r\n or r[\"_field\"] == \"Pressure (hPa)\" \r\n or r[\"_field\"] == \"Temperature (°C)\" \r\n or r[\"_field\"] == \"Volume (mL)\" \r\n or r[\"_field\"] == \"Volume Normalised (mL)\" \r\n or r[\"_field\"] == \"Battery (V)\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter 1 - \" + r._field\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")\r\n", - "refId": "A" + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 33 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" }, + "hide": false, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_4\" or r[\"_measurement\"] == \"gas_meter_3\" or r[\"_measurement\"] == \"gas_meter_2\" or r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature (°C)\")\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Flow Meter Temp (Room Temp)", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 4.5, + "axisSoftMin": 4, + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ { - "datasource": { - "type": "influxdb", - "uid": "aeerm33apqrr4c" + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } }, - "hide": false, - "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] == \"pico_4\") \r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature\" or r[\"_field\"] == \"pH\" or r[\"_field\"] == \"ORP\" or r[\"_field\"] == \"motor_mA\" or r[\"_field\"] == \"RPM\")\r\n// // Filter out unreasonable values (adjust thresholds as needed). Use the *engineering unit* ranges.\r\n// |> filter(fn: (r) => \r\n// (r._field == \"motor_mA\" and r._value < 2000 and r._value >= -5) and\r\n// (r._field == \"Temperature\" and r._value < 110 and r._value >= -1) and\r\n// (r._field == \"pH\" and r._value < 14.5 and r._value >= -0.5) and\r\n// (r._field == \"ORP\" and r._value < 2100 and r._value >= -2100) and\r\n// (r._field == \"RPM\" and r._value < 60000 and r._value >= -1)\r\n// )\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Pico \" + strings.substring(v: r.device, start: 5, end: 6) + \" - \" + r._field //Simplified field naming\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")", - "refId": "B" + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] } - ], - "title": "Reactor 4", - "type": "timeseries" - } - ], - "preload": true, - "refresh": "5s", - "schemaVersion": 40, - "tags": [], - "templating": { - "list": [ + ] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 33 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ { - "allowCustomValue": true, - "current": { - "text": "Volume (mL)", - "value": "Volume (mL)" - }, - "label": "Temperature", - "name": "Temperature", - "options": [ + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_4\" or r[\"_measurement\"] == \"gas_meter_3\" or r[\"_measurement\"] == \"gas_meter_2\" or r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Battery (V)\")\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Battery (V)", + "type": "timeseries" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 40 + }, + "id": 7, + "panels": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 41 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_1\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\" \r\n or r[\"_field\"] == \"Power Supply (V)\" \r\n or r[\"_field\"] == \"Flow Normalised (mL/min)\" \r\n or r[\"_field\"] == \"Pressure (hPa)\" \r\n or r[\"_field\"] == \"Temperature (°C)\" \r\n or r[\"_field\"] == \"Volume (mL)\" \r\n or r[\"_field\"] == \"Volume Normalised (mL)\" \r\n or r[\"_field\"] == \"Battery (V)\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter 1 - \" + r._field\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")\r\n", + "refId": "A" + }, { - "selected": true, - "text": "Volume (mL)", - "value": "Volume (mL)" + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] == \"pico_1\") \r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature\" or r[\"_field\"] == \"pH\" or r[\"_field\"] == \"ORP\" or r[\"_field\"] == \"motor_mA\" or r[\"_field\"] == \"RPM\")\r\n// // Filter out unreasonable values (adjust thresholds as needed). Use the *engineering unit* ranges.\r\n// |> filter(fn: (r) => \r\n// (r._field == \"motor_mA\" and r._value < 2000 and r._value >= -5) and\r\n// (r._field == \"Temperature\" and r._value < 110 and r._value >= -1) and\r\n// (r._field == \"pH\" and r._value < 14.5 and r._value >= -0.5) and\r\n// (r._field == \"ORP\" and r._value < 2100 and r._value >= -2100) and\r\n// (r._field == \"RPM\" and r._value < 60000 and r._value >= -1)\r\n// )\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Pico \" + strings.substring(v: r.device, start: 5, end: 6) + \" - \" + r._field //Simplified field naming\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")", + "refId": "B" } ], - "query": "Volume (mL)", - "type": "custom" + "title": "Reactor 1", + "type": "timeseries" }, { - "current": { - "text": [ - "Flow (mL/min)" - ], - "value": [ - "Flow (mL/min)" + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + } ] }, - "includeAll": true, - "multi": true, - "name": "Filter", - "options": [ - { - "selected": true, - "text": "Flow (mL/min)", - "value": "Flow (mL/min)" + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 41 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ { - "selected": false, - "text": "Power Supply (V)", - "value": "Power Supply (V)" + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_2\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\" \r\n or r[\"_field\"] == \"Power Supply (V)\" \r\n or r[\"_field\"] == \"Flow Normalised (mL/min)\" \r\n or r[\"_field\"] == \"Pressure (hPa)\" \r\n or r[\"_field\"] == \"Temperature (°C)\" \r\n or r[\"_field\"] == \"Volume (mL)\" \r\n or r[\"_field\"] == \"Volume Normalised (mL)\" \r\n or r[\"_field\"] == \"Battery (V)\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter 2 - \" + r._field\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")\r\n", + "refId": "A" }, { - "selected": false, - "text": "Flow Normalised (mL/min)", - "value": "Flow Normalised (mL/min)" + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] == \"pico_2\") \r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature\" or r[\"_field\"] == \"pH\" or r[\"_field\"] == \"ORP\" or r[\"_field\"] == \"motor_mA\" or r[\"_field\"] == \"RPM\")\r\n// // Filter out unreasonable values (adjust thresholds as needed). Use the *engineering unit* ranges.\r\n// |> filter(fn: (r) => \r\n// (r._field == \"motor_mA\" and r._value < 2000 and r._value >= -5) and\r\n// (r._field == \"Temperature\" and r._value < 110 and r._value >= -1) and\r\n// (r._field == \"pH\" and r._value < 14.5 and r._value >= -0.5) and\r\n// (r._field == \"ORP\" and r._value < 2100 and r._value >= -2100) and\r\n// (r._field == \"RPM\" and r._value < 60000 and r._value >= -1)\r\n// )\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Pico \" + strings.substring(v: r.device, start: 5, end: 6) + \" - \" + r._field //Simplified field naming\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")", + "refId": "B" + } + ], + "title": "Reactor 2", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } }, - { - "selected": false, - "text": "Pressure (hPa)", - "value": "Pressure (hPa)" + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + }, + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "Pico 3 - ORP" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 49 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ { - "selected": false, - "text": "Temperature (°C)", - "value": "Temperature (°C)" + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_3\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\" \r\n or r[\"_field\"] == \"Power Supply (V)\" \r\n or r[\"_field\"] == \"Flow Normalised (mL/min)\" \r\n or r[\"_field\"] == \"Pressure (hPa)\" \r\n or r[\"_field\"] == \"Temperature (°C)\" \r\n or r[\"_field\"] == \"Volume (mL)\" \r\n or r[\"_field\"] == \"Volume Normalised (mL)\" \r\n or r[\"_field\"] == \"Battery (V)\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter 3 - \" + r._field\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")\r\n", + "refId": "A" }, { - "selected": false, - "text": "Volume (mL)", - "value": "Volume (mL)" + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] == \"pico_3\") \r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature\" or r[\"_field\"] == \"pH\" or r[\"_field\"] == \"ORP\" or r[\"_field\"] == \"motor_mA\" or r[\"_field\"] == \"RPM\")\r\n// // Filter out unreasonable values (adjust thresholds as needed). Use the *engineering unit* ranges.\r\n// |> filter(fn: (r) => \r\n// (r._field == \"motor_mA\" and r._value < 2000 and r._value >= -5) and\r\n// (r._field == \"Temperature\" and r._value < 110 and r._value >= -1) and\r\n// (r._field == \"pH\" and r._value < 14.5 and r._value >= -0.5) and\r\n// (r._field == \"ORP\" and r._value < 2100 and r._value >= -2100) and\r\n// (r._field == \"RPM\" and r._value < 60000 and r._value >= -1)\r\n// )\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Pico \" + strings.substring(v: r.device, start: 5, end: 6) + \" - \" + r._field //Simplified field naming\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")", + "refId": "B" + } + ], + "title": "Reactor 3", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "lastNotNull", + "value": 200 + } + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + }, + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "Pico 4 - ORP" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 49 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ { - "selected": false, - "text": "Volume Normalised (mL)", - "value": "Volume Normalised (mL)" + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "from(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"gas_meter_4\")\r\n |> filter(fn: (r) => r[\"_field\"] == \"Flow (mL/min)\" \r\n or r[\"_field\"] == \"Power Supply (V)\" \r\n or r[\"_field\"] == \"Flow Normalised (mL/min)\" \r\n or r[\"_field\"] == \"Pressure (hPa)\" \r\n or r[\"_field\"] == \"Temperature (°C)\" \r\n or r[\"_field\"] == \"Volume (mL)\" \r\n or r[\"_field\"] == \"Volume Normalised (mL)\" \r\n or r[\"_field\"] == \"Battery (V)\")\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Gas Meter 4 - \" + r._field\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"mean\")\r\n", + "refId": "A" }, { - "selected": false, - "text": "Battery (V)", - "value": "Battery (V)" + "datasource": { + "type": "influxdb", + "uid": "aeerm33apqrr4c" + }, + "hide": false, + "query": "import \"strings\"\r\n\r\nfrom(bucket: \"reactor-monitoring\")\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"pico_measurements\")\r\n |> filter(fn: (r) => r[\"device\"] == \"pico_4\") \r\n |> filter(fn: (r) => r[\"_field\"] == \"Temperature\" or r[\"_field\"] == \"pH\" or r[\"_field\"] == \"ORP\" or r[\"_field\"] == \"motor_mA\" or r[\"_field\"] == \"RPM\")\r\n// // Filter out unreasonable values (adjust thresholds as needed). Use the *engineering unit* ranges.\r\n// |> filter(fn: (r) => \r\n// (r._field == \"motor_mA\" and r._value < 2000 and r._value >= -5) and\r\n// (r._field == \"Temperature\" and r._value < 110 and r._value >= -1) and\r\n// (r._field == \"pH\" and r._value < 14.5 and r._value >= -0.5) and\r\n// (r._field == \"ORP\" and r._value < 2100 and r._value >= -2100) and\r\n// (r._field == \"RPM\" and r._value < 60000 and r._value >= -1)\r\n// )\r\n |> map(fn: (r) => ({\r\n _time: r._time,\r\n _value: r._value,\r\n _field: \"Pico \" + strings.substring(v: r.device, start: 5, end: 6) + \" - \" + r._field //Simplified field naming\r\n }))\r\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\r\n |> yield(name: \"picos\")", + "refId": "B" } ], - "query": "Flow (mL/min), Power Supply (V) , Flow Normalised (mL/min), Pressure (hPa), Temperature (°C), Volume (mL), Volume Normalised (mL), Battery (V)", - "type": "custom" + "title": "Reactor 4", + "type": "timeseries" } - ] - }, - "time": { - "from": "now-3h", - "to": "now" - }, - "timepicker": {}, - "timezone": "browser", - "title": "Cetogenix Reactors Test", - "uid": "aef21xt93ta80c", - "version": 2, - "weekStart": "" - } \ No newline at end of file + ], + "title": "Overview", + "type": "row" + } + ], + "preload": true, + "refresh": "5m", + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-2d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Cetogenix Reactors", + "uid": "aef21xt93ta80b", + "version": 61, + "weekStart": "" +} \ No newline at end of file