diff --git a/.github/workflows/ArduinoBuild_2.yml b/.github/workflows/ArduinoBuild_2.yml index 98554f4..7c08da6 100644 --- a/.github/workflows/ArduinoBuild_2.yml +++ b/.github/workflows/ArduinoBuild_2.yml @@ -3,8 +3,8 @@ name: ArduinoBuild(platform-version 2.x) env: SKETCH_NAMES_FIND_START: ./examples/UnitUnified REQUIRED_LIBRARIES: M5Unified -# M5UNITUNIFIED_BRANCH: main - M5UNITUNIFIED_BRANCH: develop + M5UNITUNIFIED_BRANCH: main +# M5UNITUNIFIED_BRANCH: develop on: push: @@ -40,6 +40,7 @@ jobs: unit: - UnitAmeter - UnitVmeter + - UnitKMeterISO sketch: - PlotToSerial @@ -83,7 +84,6 @@ jobs: # Checkout library from specific URL and branch # Note that dependent libraries are not automatically installed. - - name: Checkout M5Utility uses: actions/checkout@v3 with: diff --git a/.github/workflows/ArduinoBuild_3.yml b/.github/workflows/ArduinoBuild_3.yml index 253f315..455614e 100644 --- a/.github/workflows/ArduinoBuild_3.yml +++ b/.github/workflows/ArduinoBuild_3.yml @@ -40,6 +40,7 @@ jobs: unit: - UnitAmeter - UnitVmeter + - UnitKMeterISO sketch: - PlotToSerial diff --git a/.github/workflows/PlatformioBuild.yml b/.github/workflows/PlatformioBuild.yml index 55b4cc1..5ef30a7 100644 --- a/.github/workflows/PlatformioBuild.yml +++ b/.github/workflows/PlatformioBuild.yml @@ -35,6 +35,7 @@ jobs: unit: - UnitAmeter - UnitVmeter + - UnitKmeterISO example: - PlotToSerial diff --git a/README.md b/README.md index 04a2918..585aa38 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Overview -Library for AMeter/VMeter using [M5UnitUnified](https://github.com/m5stack/M5UnitUnified). +Library for Meters using [M5UnitUnified](https://github.com/m5stack/M5UnitUnified). M5UnitUnified is a library for unified handling of various M5 units products. ### SKU:U086 & SKU:U087 @@ -24,11 +24,22 @@ In order to ensure the measurement accuracy, there is a built-in DC-DC isolated This prevents noise and surges on the data bus or other circuits from entering the local ground terminal to interfere or damage sensitive circuits. Each Unit is individually calibrated when leaving the factory, initial accuracy of 0.1%FS, ±1 count, and a maximum measurement voltage of ±36V. +### SKU:U133-V11 + +KMeterISO unitis an integrated K-type thermocouple sensor unit that integrates the functions of "acquisition + isolation + communication", using STM32F030+MAX31855KASA 14bit thermocouple digital conversion chip scheme to achieve high-precision temperature acquisition and conversion, MCU using STM32F030 to realize data acquisition and I2C communication interface, + +using CA-IS3641HW as a signal isolator. The unit supports access to thermocouple probes with a measurement range of -200°C to 1350°C, and adopts a universal standard K-type flat interface, which is convenient for subsequent replacement of different measuring probes to match different needs. + +This module is widely used in application scenarios such as temperature collection, control, and monitoring in industrial automation, instrumentation, power and electrical, heat treatment and other fields. + + ## Related Link See also examples using conventional methods here. - [Unit Ameter & Datasheet](https://docs.m5stack.com/en/unit/ameter) - [Unit Vmeter & Datasheet](https://docs.m5stack.com/en/unit/vmeter) +- [Unit KMeterISO & Datasheet](https://docs.m5stack.com/en/unit/KMeterISO%20Unit) + ## Required Libraries: @@ -40,7 +51,6 @@ See also examples using conventional methods here. - [M5Unit-METER- MIT](LICENSE) - ## Examples See also [examples/UnitUnified](examples/UnitUnified) diff --git a/examples/UnitUnified/UnitKMeterISO/PlotToSerial/PlotToSerial.ino b/examples/UnitUnified/UnitKMeterISO/PlotToSerial/PlotToSerial.ino new file mode 100644 index 0000000..11b13d4 --- /dev/null +++ b/examples/UnitUnified/UnitKMeterISO/PlotToSerial/PlotToSerial.ino @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example using M5UnitUnified for UnitKmeterISO +*/ +#include "main/PlotToSerial.cpp" diff --git a/examples/UnitUnified/UnitKMeterISO/PlotToSerial/main/PlotToSerial.cpp b/examples/UnitUnified/UnitKMeterISO/PlotToSerial/main/PlotToSerial.cpp new file mode 100644 index 0000000..67bdab3 --- /dev/null +++ b/examples/UnitUnified/UnitKMeterISO/PlotToSerial/main/PlotToSerial.cpp @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example: UnitKmeterISO +*/ + +// #define USING_M5HAL // When using M5HAL + +#include +#include +#include +#if !defined(USING_M5HAL) +#include +#endif + +namespace { +m5::unit::UnitUnified Units; +m5::unit::UnitKmeterISO unit; +auto& lcd = M5.Display; +} // namespace + +void setup() +{ + M5.begin(); + + m5::utility::delay(3000); + + auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda); + auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl); + M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl); + +#if defined(USING_M5HAL) +#pragma message "Using M5HAL" + // Using M5HAL + m5::hal::bus::I2CBusConfig i2c_cfg; + i2c_cfg.pin_sda = m5::hal::gpio::getPin(pin_num_sda); + i2c_cfg.pin_scl = m5::hal::gpio::getPin(pin_num_scl); + auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg); + M5_LOGI("Bus:%d", i2c_bus.has_value()); + if (!Units.add(unit, i2c_bus ? i2c_bus.value() : nullptr) || !Units.begin()) { + M5_LOGE("Failed to begin"); + lcd.clear(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } +#else +#pragma message "Using Wire" + // Using TwoWire + Wire.begin(pin_num_sda, pin_num_scl, 100000U); + for (int i = 0; i < 10; ++i) { + Wire.beginTransmission(unit.address()); + auto wret = Wire.endTransmission(); + M5_LOGW(">>%d", wret); + delay(10); + } + + if (!Units.add(unit, Wire) || !Units.begin()) { + M5_LOGE("Failed to begin"); + lcd.clear(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } +#endif + + M5_LOGI("M5UnitUnified has been begun"); + M5_LOGI("%s", Units.debugInfo().c_str()); + lcd.clear(TFT_DARKGREEN); +} + +void loop() +{ + M5.update(); + Units.update(); + if (unit.updated()) { + M5_LOGI("\n>Temperature:%f", unit.temperature()); + ; + } +} diff --git a/library.json b/library.json index 069400c..60005f3 100644 --- a/library.json +++ b/library.json @@ -11,9 +11,7 @@ "url": "https://github.com/m5stack/M5Unit-METER.git" }, "dependencies": { - "M5UnitUnified": "https://github.com/m5stack/M5UnitUnified.git", - "M5Utility": "https://github.com/m5stack/M5Utility.git", - "M5HAL": "https://github.com/m5stack/M5HAL.git" + "M5UnitUnified": "https://github.com/m5stack/M5UnitUnified.git" }, "version": "0.0.1", "frameworks": [ diff --git a/platformio.ini b/platformio.ini index a5be9fa..c94b126 100644 --- a/platformio.ini +++ b/platformio.ini @@ -3,8 +3,9 @@ ; For UnitTest and examples (Using M5UnitUnified) ;----------------------------------------------------------------------- [platformio] -;default_envs = test_Ameter_Core, test_Ameter_Core2, test_Ameter_CoreS3, test_Ameter_Fire, test_Ameter_StampS3, test_Ameter_Dial, test_Ameter_AtomS3, test_Ameter_NanoC6, test_Ameter_StickCPlus, test_Ameter_Paper, test_Vmeter_Core, test_Vmeter_Core2, test_Vmeter_CoreS3, test_Vmeter_Fire, test_Vmeter_StampS3, test_Vmeter_Dial, test_Vmeter_AtomS3, test_Vmeter_NanoC6, test_Vmeter_StickCPlus, test_Vmeter_Paper -;default_envs =UnitAmeter_PlotToSerial_Core_Arduino_latest, UnitAmeter_PlotToSerial_Core_Arduino_5_4_0, UnitAmeter_PlotToSerial_Core_Arduino_4_4_0, UnitAmeter_PlotToSerial_Core2_Arduino_latest, UnitAmeter_PlotToSerial_Core2_Arduino_5_4_0, UnitAmeter_PlotToSerial_Core2_Arduino_4_4_0, UnitAmeter_PlotToSerial_CoreS3_Arduino_latest, UnitAmeter_PlotToSerial_StampS3_Arduino_latest, UnitAmeter_PlotToSerial_AtomS3_Arduino_latest, UnitAmeter_PlotToSerial_Dial_Arduino_latest, UnitAmeter_PlotToSerial_NanoC6_Arduino_latest, UnitAmeter_PlotToSerial_StickCPlus_Arduino_latest, UnitAmeter_PlotToSerial_Paper_Arduino_latest, UnitAmeter_PlotToSerial_Fire_Arduino_latest, UnitAmeter_PlotToSerial_Fire_Arduino_5_4_0, UnitAmeter_PlotToSerial_Fire_Arduino_4_4_0, UnitVmeter_PlotToSerial_Core_Arduino_latest, UnitVmeter_PlotToSerial_Core_Arduino_5_4_0, UnitVmeter_PlotToSerial_Core_Arduino_4_4_0, UnitVmeter_PlotToSerial_Core2_Arduino_latest, UnitVmeter_PlotToSerial_Core2_Arduino_5_4_0, UnitVmeter_PlotToSerial_Core2_Arduino_4_4_0, UnitVmeter_PlotToSerial_CoreS3_Arduino_latest, UnitVmeter_PlotToSerial_StampS3_Arduino_latest, UnitVmeter_PlotToSerial_AtomS3_Arduino_latest, UnitVmeter_PlotToSerial_Dial_Arduino_latest, UnitVmeter_PlotToSerial_NanoC6_Arduino_latest, UnitVmeter_PlotToSerial_StickCPlus_Arduino_latest, UnitVmeter_PlotToSerial_Paper_Arduino_latest, UnitVmeter_PlotToSerial_Fire_Arduino_latest, UnitVmeter_PlotToSerial_Fire_Arduino_5_4_0, UnitVmeter_PlotToSerial_Fire_Arduino_4_4_0 +;default_envs = test_Ameter_Core, test_Ameter_Core2, test_Ameter_CoreS3, test_Ameter_Fire, test_Ameter_StampS3, test_Ameter_Dial, test_Ameter_AtomS3, test_Ameter_NanoC6, test_Ameter_StickCPlus, test_Ameter_Paper, test_Vmeter_Core, test_Vmeter_Core2, test_Vmeter_CoreS3, test_Vmeter_Fire, test_Vmeter_StampS3, test_Vmeter_Dial, test_Vmeter_AtomS3, test_Vmeter_NanoC6, test_Vmeter_StickCPlus, test_Vmeter_Paper, test_UnitKmeterISO_Core, test_UnitKmeterISO_Core2, test_UnitKmeterISO_CoreS3, test_UnitKmeterISO_Fire, test_UnitKmeterISO_StampS3, test_UnitKmeterISO_Dial, test_UnitKmeterISO_AtomS3, test_UnitKmeterISO_NanoC6, test_UnitKmeterISO_StickCPlus, test_UnitKmeterISO_Paper +;default_envs = UnitAmeter_PlotToSerial_Core_Arduino_latest, UnitAmeter_PlotToSerial_Core_Arduino_5_4_0, UnitAmeter_PlotToSerial_Core_Arduino_4_4_0, UnitAmeter_PlotToSerial_Core2_Arduino_latest, UnitAmeter_PlotToSerial_Core2_Arduino_5_4_0, UnitAmeter_PlotToSerial_Core2_Arduino_4_4_0, UnitAmeter_PlotToSerial_CoreS3_Arduino_latest, UnitAmeter_PlotToSerial_StampS3_Arduino_latest, UnitAmeter_PlotToSerial_AtomS3_Arduino_latest, UnitAmeter_PlotToSerial_Dial_Arduino_latest, UnitAmeter_PlotToSerial_NanoC6_Arduino_latest, UnitAmeter_PlotToSerial_StickCPlus_Arduino_latest, UnitAmeter_PlotToSerial_Paper_Arduino_latest, UnitAmeter_PlotToSerial_Fire_Arduino_latest, UnitAmeter_PlotToSerial_Fire_Arduino_5_4_0, UnitAmeter_PlotToSerial_Fire_Arduino_4_4_0, UnitVmeter_PlotToSerial_Core_Arduino_latest, UnitVmeter_PlotToSerial_Core_Arduino_5_4_0, UnitVmeter_PlotToSerial_Core_Arduino_4_4_0, UnitVmeter_PlotToSerial_Core2_Arduino_latest, UnitVmeter_PlotToSerial_Core2_Arduino_5_4_0, UnitVmeter_PlotToSerial_Core2_Arduino_4_4_0, UnitVmeter_PlotToSerial_CoreS3_Arduino_latest, UnitVmeter_PlotToSerial_StampS3_Arduino_latest, UnitVmeter_PlotToSerial_AtomS3_Arduino_latest, UnitVmeter_PlotToSerial_Dial_Arduino_latest, UnitVmeter_PlotToSerial_NanoC6_Arduino_latest, UnitVmeter_PlotToSerial_StickCPlus_Arduino_latest, UnitVmeter_PlotToSerial_Paper_Arduino_latest, UnitVmeter_PlotToSerial_Fire_Arduino_latest, UnitVmeter_PlotToSerial_Fire_Arduino_5_4_0, UnitVmeter_PlotToSerial_Fire_Arduino_4_4_0, UnitKmeterISO_PlotToSerial_Core_Arduino_latest, UnitKmeterISO_PlotToSerial_Core_Arduino_5_4_0, UnitKmeterISO_PlotToSerial_Core_Arduino_4_4_0, UnitKmeterISO_PlotToSerial_Core2_Arduino_latest, UnitKmeterISO_PlotToSerial_Core2_Arduino_5_4_0, UnitKmeterISO_PlotToSerial_Core2_Arduino_4_4_0, UnitKmeterISO_PlotToSerial_CoreS3_Arduino_latest, UnitKmeterISO_PlotToSerial_StampS3_Arduino_latest, UnitKmeterISO_PlotToSerial_AtomS3_Arduino_latest, UnitKmeterISO_PlotToSerial_Dial_Arduino_latest, UnitKmeterISO_PlotToSerial_NanoC6_Arduino_latest, UnitKmeterISO_PlotToSerial_StickCPlus_Arduino_latest, UnitKmeterISO_PlotToSerial_Paper_Arduino_latest, UnitKmeterISO_PlotToSerial_Fire_Arduino_latest, UnitKmeterISO_PlotToSerial_Fire_Arduino_5_4_0, UnitKmeterISO_PlotToSerial_Fire_Arduino_4_4_0 + [env] build_flags =-Wall -Wextra -Wreturn-local-addr -Werror=format -Werror=return-local-addr @@ -288,10 +289,71 @@ lib_deps = ${Paper.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_vmeter +; KMeterISO +[env:test_UnitKmeterISO_Core] +extends=Core, option_release, arduino_latest +lib_deps = ${Core.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + +[env:test_UnitKmeterISO_Core2] +extends=Core2, option_release, arduino_latest +lib_deps = ${Core2.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + +[env:test_UnitKmeterISO_CoreS3] +extends=CoreS3, option_release, arduino_latest +lib_deps = ${CoreS3.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + +[env:test_UnitKmeterISO_Fire] +extends=Fire, option_release, arduino_latest +lib_deps = ${Fire.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + +[env:test_UnitKmeterISO_StampS3] +extends=StampS3, option_release, arduino_latest +lib_deps = ${StampS3.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + +[env:test_UnitKmeterISO_Dial] +extends=Dial, option_release, arduino_latest +lib_deps = ${Dial.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + +[env:test_UnitKmeterISO_AtomS3] +extends=AtomS3, option_release, arduino_latest +lib_deps = ${AtomS3.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + +[env:test_UnitKmeterISO_NanoC6] +extends=NanoC6, option_release, arduino_latest +lib_deps = ${NanoC6.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + +[env:test_UnitKmeterISO_StickCPlus] +extends=StickCPlus, option_release, arduino_latest +lib_deps = ${StickCPlus.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + +[env:test_UnitKmeterISO_Paper] +extends=Paper, option_release, arduino_latest +lib_deps = ${Paper.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_kmeterISO + ; -------------------------------- ;Examples by M5UnitUnified ; -------------------------------- -;UnitAmeter +;Ameter [env:UnitAmeter_PlotToSerial_Core_Arduino_latest] extends=Core, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitAmeter/PlotToSerial> @@ -329,10 +391,8 @@ extends=AtomS3, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitAmeter/PlotToSerial> [env:UnitAmeter_PlotToSerial_Dial_Arduino_latest] -extends=StampS3, option_release, arduino_latest +extends=Dial, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitAmeter/PlotToSerial> -lib_deps = ${StampS3.lib_deps} - m5stack/M5Dial [env:UnitAmeter_PlotToSerial_NanoC6_Arduino_latest] extends=NanoC6, option_release, arduino_latest @@ -358,7 +418,7 @@ build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitAmeter/P extends=Fire, option_release, arduino_4_4_0 build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitAmeter/PlotToSerial> -;UnitVmeter +;Vmeter [env:UnitVmeter_PlotToSerial_Core_Arduino_latest] extends=Core, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitVmeter/PlotToSerial> @@ -396,10 +456,8 @@ extends=AtomS3, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitVmeter/PlotToSerial> [env:UnitVmeter_PlotToSerial_Dial_Arduino_latest] -extends=StampS3, option_release, arduino_latest +extends=Dial, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitVmeter/PlotToSerial> -lib_deps = ${StampS3.lib_deps} - m5stack/M5Dial [env:UnitVmeter_PlotToSerial_NanoC6_Arduino_latest] extends=NanoC6, option_release, arduino_latest @@ -424,3 +482,68 @@ build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitVmeter/P [env:UnitVmeter_PlotToSerial_Fire_Arduino_4_4_0] extends=Fire, option_release, arduino_4_4_0 build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitVmeter/PlotToSerial> + +; KMeterISO +[env:UnitKmeterISO_PlotToSerial_Core_Arduino_latest] +extends=Core, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Core_Arduino_5_4_0] +extends=Core, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Core_Arduino_4_4_0] +extends=Core, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Core2_Arduino_latest] +extends=Core2, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Core2_Arduino_5_4_0] +extends=Core2, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Core2_Arduino_4_4_0] +extends=Core2, option_release, arduino_4_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_CoreS3_Arduino_latest] +extends=CoreS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_StampS3_Arduino_latest] +extends=StampS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_AtomS3_Arduino_latest] +extends=AtomS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Dial_Arduino_latest] +extends=Dial, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_NanoC6_Arduino_latest] +extends=NanoC6, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_StickCPlus_Arduino_latest] +extends=StickCPlus, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Paper_Arduino_latest] +extends=Paper, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Fire_Arduino_latest] +extends=Fire, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Fire_Arduino_5_4_0] +extends=Fire, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> + +[env:UnitKmeterISO_PlotToSerial_Fire_Arduino_4_4_0] +extends=Fire, option_release, arduino_4_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitKmeterISO/PlotToSerial> diff --git a/src/M5UnitUnifiedMETER.hpp b/src/M5UnitUnifiedMETER.hpp index 34e74ae..2ed6e23 100644 --- a/src/M5UnitUnifiedMETER.hpp +++ b/src/M5UnitUnifiedMETER.hpp @@ -19,6 +19,8 @@ #include "unit/unit_Ameter.hpp" #include "unit/unit_Vmeter.hpp" +#include "unit/unit_KmeterISO.hpp" + /*! @namespace m5 @brief Top level namespace of M5stack diff --git a/src/unit/unit_KmeterISO.cpp b/src/unit/unit_KmeterISO.cpp new file mode 100644 index 0000000..08f83dc --- /dev/null +++ b/src/unit/unit_KmeterISO.cpp @@ -0,0 +1,190 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/*! + @file unit_KmeterISO.cpp + @brief KmeterISO Unit for M5UnitUnified +*/ +#include "unit_KmeterISO.hpp" +#include +#include +#include + +using namespace m5::utility::mmh3; +using namespace m5::unit::types; +using namespace m5::unit::kmeterISO; +using namespace m5::unit::kmeterISO::command; + +namespace { +template +T array_to_type(const std::array& a) +{ + static_assert(std::is_integral::value && sizeof(T) == 4, "Invalid type"); + return static_cast((((uint32_t)a[3]) << 24) | (((uint32_t)a[2]) << 16) | (((uint32_t)a[1]) << 8) | + (((uint32_t)a[0]) << 0)); +} + +constexpr uint8_t reg_temperature_table[] = { + TEMPERATURE_CELSIUS_VAL_REG, + TEMPERATURE_FAHRENHEIT_VAL_REG, +}; + +constexpr uint8_t reg_internal_temperature_table[] = { + INTERNAL_TEMPERATURE_CELSIUS_VAL_REG, + INTERNAL_TEMPERATURE_FAHRENHEIT_VAL_REG, +}; + +} // namespace + +namespace m5 { +namespace unit { +// class UnitKmeterISO +const char UnitKmeterISO::name[] = "UnitKmeterISO"; +const types::uid_t UnitKmeterISO::uid{"UnitKmeterISO"_mmh3}; +const types::uid_t UnitKmeterISO::attr{0}; + +bool UnitKmeterISO::begin() +{ + auto ssize = stored_size(); + assert(ssize && "stored_size must be greater than zero"); + if (ssize != _data->capacity()) { + _data.reset(new m5::container::CircularBuffer(ssize)); + if (!_data) { + M5_LIB_LOGE("Failed to allocate"); + return false; + } + } + + uint8_t ver{0x00}; + if (!readFirmwareVersion(ver) && (ver == 0x00)) { + M5_LIB_LOGE("Failed to read version"); + return false; + } + return _cfg.start_periodic ? startPeriodicMeasurement(_cfg.interval, _cfg.measurement_unit) : true; +} + +void UnitKmeterISO::update(const bool force) +{ + _updated = false; + if (inPeriodic()) { + elapsed_time_t at{m5::utility::millis()}; + if (force || !_latest || at >= _latest + _interval) { + Data d{}; + _updated = isReady() && read_measurement(d, _munit); + if (_updated) { + _latest = at; + _data->push_back(d); + } + } + } +} + +bool UnitKmeterISO::start_periodic_measurement() +{ + if (inPeriodic()) { + M5_LIB_LOGD("Periodic measurements are running"); + return false; + } + _periodic = true; + _latest = 0; + return true; +} + +bool UnitKmeterISO::start_periodic_measurement(const uint32_t interval, const MeasurementUnit munit) +{ + if (start_periodic_measurement()) { + _interval = interval; + _munit = munit; + return true; + } + return false; +} + +bool UnitKmeterISO::stop_periodic_measurement() +{ + _periodic = _updated = false; + return true; +} + +bool UnitKmeterISO::readStatus(uint8_t& status) +{ + status = 0xFF; + return readRegister8(ERROR_STATUS_REG, status, 0); +} + +bool UnitKmeterISO::readFirmwareVersion(uint8_t& ver) +{ + return readRegister8(FIRMWARE_VERSION_REG, ver, 0); +} + +bool UnitKmeterISO::measureSingleshot(kmeterISO::Data& d, kmeterISO::MeasurementUnit munit, const uint32_t timeoutMs) +{ + auto timeout_at = m5::utility::millis() + timeoutMs; + do { + if (isReady()) { + return read_measurement(d, munit); + } + m5::utility::delay(1); + } while (m5::utility::millis() <= timeout_at); + + M5_LIB_LOGW("Failed due to timeout"); + return false; +} + +bool UnitKmeterISO::measureInternalSingleshot(kmeterISO::Data& d, kmeterISO::MeasurementUnit munit, + const uint32_t timeoutMs) +{ + auto timeout_at = m5::utility::millis() + timeoutMs; + do { + if (isReady()) { + return read_internal_measurement(d, munit); + } + m5::utility::delay(1); + } while (m5::utility::millis() <= timeout_at); + + M5_LIB_LOGW("Failed due to timeout"); + return false; +} + +bool UnitKmeterISO::changeI2CAddress(const uint8_t i2c_address) +{ + if (!m5::utility::isValidI2CAddress(i2c_address)) { + M5_LIB_LOGE("Invalid address : %02X", i2c_address); + return false; + } + if (writeRegister8(I2C_ADDRESS_REG, i2c_address) && changeAddress(i2c_address)) { + // Wait wakeup + uint8_t v{}; + bool done{}; + auto timeout_at = m5::utility::millis() + 1000; + do { + done = readRegister8(I2C_ADDRESS_REG, v, 0) && v == i2c_address; + m5::utility::delay(1); + } while (!done && m5::utility::millis() <= timeout_at); + return done; + } + return false; +} + +bool UnitKmeterISO::readI2CAddress(uint8_t& i2c_address) +{ + return readRegister8(I2C_ADDRESS_REG, i2c_address, 1); +} + +// +bool UnitKmeterISO::read_measurement(Data& d, const kmeterISO::MeasurementUnit munit) +{ + assert(m5::stl::to_underlying(munit) < m5::stl::size(reg_temperature_table)); + return readRegister(reg_temperature_table[m5::stl::to_underlying(munit)], d.raw.data(), d.raw.size(), 0); +} + +bool UnitKmeterISO::read_internal_measurement(Data& d, const kmeterISO::MeasurementUnit munit) +{ + assert(m5::stl::to_underlying(munit) < m5::stl::size(reg_internal_temperature_table)); + return readRegister(reg_internal_temperature_table[m5::stl::to_underlying(munit)], d.raw.data(), d.raw.size(), 0); +} + +} // namespace unit +} // namespace m5 diff --git a/src/unit/unit_KmeterISO.hpp b/src/unit/unit_KmeterISO.hpp new file mode 100644 index 0000000..8793998 --- /dev/null +++ b/src/unit/unit_KmeterISO.hpp @@ -0,0 +1,253 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/*! + @file unit_KmeterISO.hpp + @brief KmeterISO Unit for M5UnitUnified +*/ +#ifndef M5_UNIT_UNIFIED_KMETERISO_UNIT_KMETERISO_HPP +#define M5_UNIT_UNIFIED_KMETERISO_UNIT_KMETERISO_HPP + +#include +#include +#include // NaN +#include + +namespace m5 { +namespace unit { + +namespace kmeterISO { + +/*! + @enum MeasurementUnit + @brief measurement unit on periodic measurement + */ +enum MeasurementUnit : uint8_t { + Celsius, //!< Temperature is celsius + Fahrenheit, //!< Temperature is fahrenheit +}; + +/*! + @struct Data + @brief Measurement data group + */ +struct Data { + std::array raw{}; // raw data + inline float temperature() const + { + return static_cast(((uint32_t)raw[3] << 24) | ((uint32_t)raw[2] << 16) | ((uint32_t)raw[1] << 8) | + ((uint32_t)raw[0] << 0)) * + 0.01f; + } +}; +} // namespace kmeterISO + +/*! + @class UnitKmeterISO + @brief KMeterISO unitis an integrated K-type thermocouple sensor unit that integrates the functions of "acquisition + + isolation + communication" + */ +class UnitKmeterISO : public Component, public PeriodicMeasurementAdapter { + M5_UNIT_COMPONENT_HPP_BUILDER(UnitKmeterISO, 0x66); + +public: + /*! + @struct config_t + @brief Settings for begin + */ + struct config_t { + //! Start periodic measurement on begin? + bool start_periodic{true}; + //! periodic interval(ms) if start on begin + uint32_t interval{100}; + //! //!< measurement unit if start on begin + kmeterISO::MeasurementUnit measurement_unit{kmeterISO::MeasurementUnit::Celsius}; + }; + + explicit UnitKmeterISO(const uint8_t addr = DEFAULT_ADDRESS) + : Component(addr), _data{new m5::container::CircularBuffer(1)} + { + auto ccfg = component_config(); + ccfg.clock = 100 * 1000U; + component_config(ccfg); + } + virtual ~UnitKmeterISO() + { + } + + virtual bool begin() override; + virtual void update(const bool force = false) override; + + ///@name Settings for begin + ///@{ + /*! @brief Gets the configration */ + inline config_t config() + { + return _cfg; + } + //! @brief Set the configration + inline void config(const config_t& cfg) + { + _cfg = cfg; + } + ///@} + + ///@name Properties + ///@{ + /*! Gets the measurement unit on periodic measurement */ + kmeterISO::MeasurementUnit measurementUnit() const + { + return _munit; + } + /*! Set the measurement unit on periodic measurement */ + void setMeasurementUnit(const kmeterISO::MeasurementUnit munit) + { + _munit = munit; + } + ///@} + + ///@name Measurement data by periodic + ///@{ + //! @brief Oldest temperature + inline float temperature() const + { + return !empty() ? oldest().temperature() : std::numeric_limits::quiet_NaN(); + } + ///@} + + ///@name Periodic measurement + ///@{ + /*! + @brief Start periodic measurement in the current settings + @return True if successful + */ + inline bool startPeriodicMeasurement() + { + return PeriodicMeasurementAdapter::startPeriodicMeasurement(); + } + /*! + @brief Start periodic measurement + @param interval Periodic interval(ms) + @param munit Measurement unit + @return True if successful + */ + inline bool startPeriodicMeasurement(const uint32_t interval, + const kmeterISO::MeasurementUnit munit = kmeterISO::Celsius) + { + return PeriodicMeasurementAdapter::startPeriodicMeasurement(interval, munit); + } + /*! + @brief Stop periodic measurement + @return True if successful + */ + inline bool stopPeriodicMeasurement() + { + return PeriodicMeasurementAdapter::stopPeriodicMeasurement(); + } + ///@} + + /*! + @brief Read status + @param[out] status Status + @return True if successful + */ + bool readStatus(uint8_t& status); + /*! + @brief Ready to read data? + @return True if ready to read data + */ + bool isReady() + { + uint8_t s{}; + return readStatus(s) && (s == 0U); + } + + /*! + @brief Read firmware version + @param[out] ver version + @return True if successful + */ + bool readFirmwareVersion(uint8_t& ver); + + ///@name Single shot measurement + ///@{ + /*! + @brief Measure temperature single shot + @param[out] data Measuerd data + @param munit measurement unit + @param timeoutMs Measurement timeout time(ms) + @return True if successful + */ + bool measureSingleshot(kmeterISO::Data& d, kmeterISO::MeasurementUnit munit = kmeterISO::MeasurementUnit::Celsius, + const uint32_t timeoutMs = 0); + /*! + @brief Measure internal temperature single shot + @param[out] data Measuerd data + @param munit measurement unit + @param timeoutMs Measurement timeout time(ms) + @return True if successful + */ + bool measureInternalSingleshot(kmeterISO::Data& d, + kmeterISO::MeasurementUnit munit = kmeterISO::MeasurementUnit::Celsius, + const uint32_t timeoutMs = 0); + ///@} + + ///@warning Handling warning + ///@name I2C Address + ///@{ + /*! + @brief Change device I2C address + @param i2c_address I2C address + @return True if successful + + */ + bool changeI2CAddress(const uint8_t i2c_address); + /*! + @brief Read device I2C address + @param[out] i2c_address I2C address + @return True if successful + */ + bool readI2CAddress(uint8_t& i2c_address); + ///@} + +protected: + bool start_periodic_measurement(); + bool start_periodic_measurement(const uint32_t interval, + const kmeterISO::MeasurementUnit munit = kmeterISO::Celsius); + bool stop_periodic_measurement(); + + bool read_measurement(kmeterISO::Data& d, const kmeterISO::MeasurementUnit munit); + bool read_internal_measurement(kmeterISO::Data& d, const kmeterISO::MeasurementUnit munit); + + M5_UNIT_COMPONENT_PERIODIC_MEASUREMENT_ADAPTER_HPP_BUILDER(UnitKmeterISO, kmeterISO::Data); + +protected: + std::unique_ptr> _data{}; + kmeterISO::MeasurementUnit _munit{kmeterISO::MeasurementUnit::Celsius}; + config_t _cfg{}; +}; + +namespace kmeterISO { +namespace command { +///@cond +// See also https://github.com/m5stack/M5Unit-KMeterISO/blob/main/docs/Unit_KmeterISO_I2C_Protocol.pdf +constexpr uint8_t TEMPERATURE_CELSIUS_VAL_REG{0X00}; +constexpr uint8_t TEMPERATURE_FAHRENHEIT_VAL_REG{0X04}; +constexpr uint8_t INTERNAL_TEMPERATURE_CELSIUS_VAL_REG{0X10}; +constexpr uint8_t INTERNAL_TEMPERATURE_FAHRENHEIT_VAL_REG{0X14}; +constexpr uint8_t ERROR_STATUS_REG{0x20}; +constexpr uint8_t TEMPERATURE_CELSIUS_STRING_REG{0x30}; +constexpr uint8_t TEMPERATURE__FAHRENHEIT_STRING_REG{0x40}; +constexpr uint8_t INTERNAL_TEMPERATURE_CELSIUS_STRING_REG{0x50}; +constexpr uint8_t INTERNAL_TEMPERATURE_FAHRENHEIT_STRING_REG{0x60}; +constexpr uint8_t FIRMWARE_VERSION_REG{0xFE}; +constexpr uint8_t I2C_ADDRESS_REG{0xFF}; +///@endcond +} // namespace command +} // namespace kmeterISO + +} // namespace unit +} // namespace m5 +#endif diff --git a/test/embedded/test_kmeterISO/kmeterISO_test.cpp b/test/embedded/test_kmeterISO/kmeterISO_test.cpp new file mode 100644 index 0000000..2c36c5d --- /dev/null +++ b/test/embedded/test_kmeterISO/kmeterISO_test.cpp @@ -0,0 +1,178 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + UnitTest for KMeterISO +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace m5::unit::googletest; +using namespace m5::unit; +using namespace m5::unit::kmeterISO; +using namespace m5::unit::kmeterISO::command; + +namespace { +void check_measurement_values(UnitKmeterISO* u) +{ + EXPECT_TRUE(std::isfinite(u->latest().temperature())); + // M5_LOGI("T:%f", u->latest().temperature()); +} + +constexpr MeasurementUnit mu_table[] = {MeasurementUnit::Celsius, MeasurementUnit::Fahrenheit}; + +} // namespace + +const ::testing::Environment* global_fixture = ::testing::AddGlobalTestEnvironment(new GlobalFixture<100000U>()); + +class TestKmeterISO : public ComponentTestBase { +protected: + virtual UnitKmeterISO* get_instance() override + { + auto ptr = new m5::unit::UnitKmeterISO(); + auto ccfg = ptr->component_config(); + ccfg.stored_size = 8; + ptr->component_config(ccfg); + return ptr; + } + virtual bool is_using_hal() const override + { + return GetParam(); + }; +}; + +// INSTANTIATE_TEST_SUITE_P(ParamValues, TestKmeterISO, ::testing::Values(false, true)); +// INSTANTIATE_TEST_SUITE_P(ParamValues, TestKmeterISO, ::testing::Values(true)); +INSTANTIATE_TEST_SUITE_P(ParamValues, TestKmeterISO, ::testing::Values(false)); + +TEST_P(TestKmeterISO, Basic) +{ + SCOPED_TRACE(ustr); + + // Version + uint8_t ver{0x00}; + EXPECT_TRUE(unit->readFirmwareVersion(ver)); + EXPECT_NE(ver, 0x00); + + // Properties + EXPECT_EQ(unit->measurementUnit(), MeasurementUnit::Celsius); + + unit->setMeasurementUnit(MeasurementUnit::Fahrenheit); + EXPECT_EQ(unit->measurementUnit(), MeasurementUnit::Fahrenheit); + unit->setMeasurementUnit(MeasurementUnit::Celsius); + EXPECT_EQ(unit->measurementUnit(), MeasurementUnit::Celsius); +} + +TEST_P(TestKmeterISO, Singleshot) +{ + SCOPED_TRACE(ustr); + + Data d{}; + for (auto&& mu : mu_table) { + uint32_t cnt{16}; + while (cnt--) { + EXPECT_TRUE(unit->measureSingleshot(d, mu, 1000)); + EXPECT_TRUE(std::isfinite(d.temperature())); + // M5_LOGI("T:%f", d.temperature()); + m5::utility::delay(100); + } + } +} + +TEST_P(TestKmeterISO, Periodic) +{ + SCOPED_TRACE(ustr); + + constexpr uint32_t interval{25}; + + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + EXPECT_FALSE(unit->inPeriodic()); + + for (auto&& mu : mu_table) { + std::string s{}; + s = m5::utility::formatString("Munit:%u", m5::stl::to_underlying(mu)); + SCOPED_TRACE(s.c_str()); + + EXPECT_TRUE(unit->startPeriodicMeasurement(interval, mu)); + EXPECT_TRUE(unit->inPeriodic()); + + test_periodic_measurement(unit.get(), 8, check_measurement_values); + + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + EXPECT_FALSE(unit->inPeriodic()); + + EXPECT_EQ(unit->available(), 8U); + EXPECT_FALSE(unit->empty()); + EXPECT_TRUE(unit->full()); + + int32_t cnt{0}; + do { + ++cnt; + unit->discard(); + EXPECT_FALSE(unit->empty()); + EXPECT_FALSE(unit->full()); + EXPECT_EQ(unit->available(), 8U - cnt); + } while (cnt < 3); + + unit->flush(); + EXPECT_TRUE(unit->empty()); + EXPECT_FALSE(unit->full()); + } +} + +/* + WARNING!! + Failure of this test will result in an unexpected I2C address being set! +*/ +TEST_P(TestKmeterISO, I2CAddress) +{ + SCOPED_TRACE(ustr); + + Data d{}; + uint8_t ver{}, addr{}; + + EXPECT_FALSE(unit->changeI2CAddress(0x07)); // Invalid + EXPECT_FALSE(unit->changeI2CAddress(0x78)); // Invalid + + // Change to 0x08 + EXPECT_TRUE(unit->changeI2CAddress(0x08)); + EXPECT_TRUE(unit->readI2CAddress(addr)); + EXPECT_EQ(addr, 0x08); + EXPECT_TRUE(unit->readFirmwareVersion(ver)); + EXPECT_NE(ver, 0x00); + EXPECT_TRUE(unit->measureSingleshot(d)); + + // Change to 0x77 + EXPECT_TRUE(unit->changeI2CAddress(0x77)); + EXPECT_TRUE(unit->readI2CAddress(addr)); + EXPECT_EQ(addr, 0x77); + EXPECT_TRUE(unit->readFirmwareVersion(ver)); + EXPECT_NE(ver, 0x00); + EXPECT_TRUE(unit->measureSingleshot(d)); + + // Change to 0x52 + EXPECT_TRUE(unit->changeI2CAddress(0x52)); + EXPECT_TRUE(unit->readI2CAddress(addr)); + EXPECT_EQ(addr, 0x52); + EXPECT_TRUE(unit->readFirmwareVersion(ver)); + EXPECT_NE(ver, 0x00); + EXPECT_TRUE(unit->measureSingleshot(d)); + + // Change to default + EXPECT_TRUE(unit->changeI2CAddress(UnitKmeterISO::DEFAULT_ADDRESS)); + EXPECT_TRUE(unit->readI2CAddress(addr)); + EXPECT_EQ(addr, +UnitKmeterISO::DEFAULT_ADDRESS); + EXPECT_TRUE(unit->readFirmwareVersion(ver)); + EXPECT_NE(ver, 0x00); + EXPECT_TRUE(unit->measureSingleshot(d)); +}