Merge branch 'develop'

This commit is contained in:
GOB
2026-04-03 10:37:55 +09:00
49 changed files with 1723 additions and 1832 deletions
@@ -12,28 +12,28 @@ on:
branches: branches:
- '*' - '*'
paths: paths:
- 'src/unit/**.cpp' - 'src/**.cpp'
- 'src/unit/**.hpp' - 'src/**.hpp'
- 'src/unit/**.h' - 'src/**.h'
- 'src/unit/**.c' - 'src/**.c'
- 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.ino'
- 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.cpp'
- 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.hpp'
- 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.h'
- 'examples/UnitUnified/**.c' - 'examples/UnitUnified/**.c'
- '**arduino-esp-v2-build-check.yml' - '.github/workflows/arduino-esp-v2-build-check.yml'
pull_request: pull_request:
paths: paths:
- 'src/unit/**.cpp' - 'src/**.cpp'
- 'src/unit/**.hpp' - 'src/**.hpp'
- 'src/unit/**.h' - 'src/**.h'
- 'src/unit/**.c' - 'src/**.c'
- 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.ino'
- 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.cpp'
- 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.hpp'
- 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.h'
- 'examples/UnitUnified/**.c' - 'examples/UnitUnified/**.c'
- '**arduino-esp-v2-build-check.yml' - '.github/workflows/arduino-esp-v2-build-check.yml'
workflow_dispatch: workflow_dispatch:
defaults: defaults:
@@ -46,13 +46,13 @@ concurrency:
jobs: jobs:
build: build:
name: ${{ matrix.unit }}:${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}} name: ${{ matrix.build-properties }}:${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}}
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5 timeout-minutes: 12
strategy: strategy:
fail-fast: false fail-fast: false
#max-parallel: 1 max-parallel: 20
matrix: matrix:
platform-url: platform-url:
- https://espressif.github.io/arduino-esp32/package_esp32_index.json - https://espressif.github.io/arduino-esp32/package_esp32_index.json
@@ -60,8 +60,8 @@ jobs:
sketch: sketch:
- PlotToSerial - PlotToSerial
unit: build-properties:
- UnitHeart - "-DUSING_UNIT_HEART"
board: board:
- m5stack-atom - m5stack-atom
@@ -84,29 +84,54 @@ jobs:
include: include:
# Specific sketches # Specific sketches
- sketch: GraphicalMeter - sketch: GraphicalMeter
unit: UnitHeart
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32 platform: esp32
archi: esp32 archi: esp32
platform-version: 2.0.17 platform-version: 2.0.17
board: m5stack-core-esp32 board: m5stack-core-esp32
build-properties: "-DUSING_UNIT_HEART"
- sketch: PlotToSerial
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32
archi: esp32
platform-version: 2.0.17
board: m5stack-coreink
build-properties: "-DUSING_HAT_HEART"
- sketch: DualSensor
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32
archi: esp32
platform-version: 2.0.17
board: m5stack-coreink
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
# Build - name: Prepare libraries list
id: libs
run: |
{
echo "yaml<<EOF"
echo "$REQUIRED_LIBRARIES" | tr ',' '\n' | while read -r lib; do
echo "- name: $lib"
done
echo "- source-path: ./"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Compile examples - name: Compile examples
uses: ArminJo/arduino-test-compile@master uses: arduino/compile-sketches@v1
with: with:
arduino-board-fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }}
arduino-platform: ${{ matrix.platform }}:${{ matrix.archi }}@${{ matrix.platform-version }} platforms: |
platform-url: ${{ matrix.platform-url }} - name: ${{ matrix.platform }}:${{ matrix.archi }}
required-libraries: ${{ env.REQUIRED_LIBRARIES }} source-url: ${{ matrix.platform-url }}
extra-arduino-cli-args: ${{ matrix.cli-args }} version: ${{ matrix.platform-version }}
#build-properties: ${{ toJson(matrix.build-properties) }} libraries: ${{ steps.libs.outputs.yaml }}
sketch-names: ${{ matrix.sketch }}.ino sketch-paths: |
sketch-names-find-start: ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.unit }} - ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.sketch }}
#sketches-exclude: ${{ matrix.sketches-exclude }} cli-compile-flags: |
- --build-property
- build.extra_flags=${{ matrix.build-properties }}
@@ -12,28 +12,28 @@ on:
branches: branches:
- '*' - '*'
paths: paths:
- 'src/unit/**.cpp' - 'src/**.cpp'
- 'src/unit/**.hpp' - 'src/**.hpp'
- 'src/unit/**.h' - 'src/**.h'
- 'src/unit/**.c' - 'src/**.c'
- 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.ino'
- 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.cpp'
- 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.hpp'
- 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.h'
- 'examples/UnitUnified/**.c' - 'examples/UnitUnified/**.c'
- '**arduino-esp-v3-build-check.yml' - '.github/workflows/arduino-esp-v3-build-check.yml'
pull_request: pull_request:
paths: paths:
- 'src/unit/**.cpp' - 'src/**.cpp'
- 'src/unit/**.hpp' - 'src/**.hpp'
- 'src/unit/**.h' - 'src/**.h'
- 'src/unit/**.c' - 'src/**.c'
- 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.ino'
- 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.cpp'
- 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.hpp'
- 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.h'
- 'examples/UnitUnified/**.c' - 'examples/UnitUnified/**.c'
- '**arduino-esp-v3-build-check.yml' - '.github/workflows/arduino-esp-v3-build-check.yml'
workflow_dispatch: workflow_dispatch:
defaults: defaults:
@@ -46,13 +46,13 @@ concurrency:
jobs: jobs:
build: build:
name: ${{ matrix.unit }}:${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}} name: ${{ matrix.build-properties }}:${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}}
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5 timeout-minutes: 12
strategy: strategy:
fail-fast: false fail-fast: false
#max-parallel: 1 max-parallel: 20
matrix: matrix:
platform-url: platform-url:
- https://espressif.github.io/arduino-esp32/package_esp32_index.json - https://espressif.github.io/arduino-esp32/package_esp32_index.json
@@ -60,19 +60,21 @@ jobs:
sketch: sketch:
- PlotToSerial - PlotToSerial
unit: build-properties:
- UnitHeart - "-DUSING_UNIT_HEART"
board: board:
- arduino_nesso_n1
- m5stack_atom - m5stack_atom
- m5stack_atoms3 - m5stack_atoms3
- m5stack_capsule - m5stack_capsule
# - m5stack_cardputer - m5stack_cardputer
- m5stack_core - m5stack_core
- m5stack_core2 - m5stack_core2
- m5stack_coreink - m5stack_coreink
- m5stack_cores3 - m5stack_cores3
- m5stack_dial - m5stack_dial
- m5stack_dinmeter
- m5stack_fire - m5stack_fire
- m5stack_nanoc6 - m5stack_nanoc6
- m5stack_paper - m5stack_paper
@@ -84,13 +86,14 @@ jobs:
# - m5stack_stickc # - m5stack_stickc
- m5stack_stickc_plus - m5stack_stickc_plus
- m5stack_stickc_plus2 - m5stack_stickc_plus2
- m5stack_tab5
# - m5stack_timer_cam # - m5stack_timer_cam
# - m5stack_tough # - m5stack_tough
# - m5stack_unit_cam # - m5stack_unit_cam
# - m5stack_unit_cams3 # - m5stack_unit_cams3
platform-version: platform-version:
- 3.0.4 - 3.3.6
platform: platform:
- esp32 - esp32
@@ -100,70 +103,84 @@ jobs:
include: include:
# Specific sketches # Specific sketches
# HAT
- sketch: PlotToSerial - sketch: PlotToSerial
unit: HatHeart
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32 platform: esp32
archi: esp32 archi: esp32
platform-version: 3.0.4 platform-version: 3.3.6
board: m5stack_stickc_plus board: m5stack_stickc_plus
build-properties: "-DUSING_HAT_HEART"
- sketch: PlotToSerial - sketch: PlotToSerial
unit: HatHeart
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32 platform: esp32
archi: esp32 archi: esp32
platform-version: 3.0.4 platform-version: 3.3.6
board: m5stack_stickc_plus2 board: m5stack_stickc_plus2
- sketch: GraphicalMeter build-properties: "-DUSING_HAT_HEART"
unit: UnitHeart - sketch: PlotToSerial
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32 platform: esp32
archi: esp32 archi: esp32
platform-version: 3.0.4 platform-version: 3.3.6
board: m5stack_coreink
build-properties: "-DUSING_HAT_HEART"
- sketch: PlotToSerial
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32
archi: esp32
platform-version: 3.3.6
board: arduino_nesso_n1
build-properties: "-DUSING_HAT_HEART"
#GraphicalMeter
- sketch: GraphicalMeter
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32
archi: esp32
platform-version: 3.3.6
board: m5stack_core board: m5stack_core
build-properties: "-DUSING_UNIT_HEART"
- sketch: GraphicalMeter - sketch: GraphicalMeter
unit: HatHeart
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32 platform: esp32
archi: esp32 archi: esp32
platform-version: 3.0.4 platform-version: 3.3.6
board: m5stack_stickc_plus
- sketch: GraphicalMeter
unit: HatHeart
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32
archi: esp32
platform-version: 3.0.4
board: m5stack_stickc_plus2 board: m5stack_stickc_plus2
build-properties: "-DUSING_HAT_HEART"
- sketch: DualSensor - sketch: DualSensor
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32 platform: esp32
archi: esp32 archi: esp32
platform-version: 3.0.4 platform-version: 3.3.6
board: m5stack_stickc_plus
- sketch: DualSensor
platform-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
platform: esp32
archi: esp32
platform-version: 3.0.4
board: m5stack_stickc_plus2 board: m5stack_stickc_plus2
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
# Build - name: Prepare libraries list
id: libs
run: |
{
echo "yaml<<EOF"
echo "$REQUIRED_LIBRARIES" | tr ',' '\n' | while read -r lib; do
echo "- name: $lib"
done
echo "- source-path: ./"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Compile examples - name: Compile examples
uses: ArminJo/arduino-test-compile@master uses: arduino/compile-sketches@v1
with: with:
arduino-board-fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }}
arduino-platform: ${{ matrix.platform }}:${{ matrix.archi }}@${{ matrix.platform-version }} platforms: |
platform-url: ${{ matrix.platform-url }} - name: ${{ matrix.platform }}:${{ matrix.archi }}
required-libraries: ${{ env.REQUIRED_LIBRARIES }} source-url: ${{ matrix.platform-url }}
extra-arduino-cli-args: ${{ matrix.cli-args }} version: ${{ matrix.platform-version }}
#build-properties: ${{ toJson(matrix.build-properties) }} libraries: ${{ steps.libs.outputs.yaml }}
sketch-names: ${{ matrix.sketch }}.ino sketch-paths: |
sketch-names-find-start: ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.unit }} - ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.sketch }}
#sketches-exclude: ${{ matrix.sketches-exclude }} cli-compile-flags: |
- --build-property
- build.extra_flags=${{ matrix.build-properties }}
+86 -54
View File
@@ -12,28 +12,28 @@ on:
branches: branches:
- '*' - '*'
paths: paths:
- 'src/unit/**.cpp' - 'src/**.cpp'
- 'src/unit/**.hpp' - 'src/**.hpp'
- 'src/unit/**.h' - 'src/**.h'
- 'src/unit/**.c' - 'src/**.c'
- 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.ino'
- 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.cpp'
- 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.hpp'
- 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.h'
- 'examples/UnitUnified/**.c' - 'examples/UnitUnified/**.c'
- '**arduino-m5-build-check.yml' - '.github/workflows/arduino-m5-build-check.yml'
pull_request: pull_request:
paths: paths:
- 'src/unit/**.cpp' - 'src/**.cpp'
- 'src/unit/**.hpp' - 'src/**.hpp'
- 'src/unit/**.h' - 'src/**.h'
- 'src/unit/**.c' - 'src/**.c'
- 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.ino'
- 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.cpp'
- 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.hpp'
- 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.h'
- 'examples/UnitUnified/**.c' - 'examples/UnitUnified/**.c'
- '**arduino-m5-build-check.yml' - '.github/workflows/arduino-m5-build-check.yml'
workflow_dispatch: workflow_dispatch:
defaults: defaults:
@@ -46,13 +46,13 @@ concurrency:
jobs: jobs:
build: build:
name: ${{ matrix.unit }}:${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}} name: ${{ matrix.build-properties }}:${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}}
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5 timeout-minutes: 12
strategy: strategy:
fail-fast: false fail-fast: false
# max-parallel: 1 max-parallel: 20
matrix: matrix:
platform-url: platform-url:
- https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json - https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
@@ -60,15 +60,17 @@ jobs:
sketch: sketch:
- PlotToSerial - PlotToSerial
unit: build-properties:
- UnitHeart - "-DUSING_UNIT_HEART"
board: board:
- arduino_nesso_n1
- m5stack_atom - m5stack_atom
- m5stack_atoms3 - m5stack_atoms3
- m5stack_atoms3r - m5stack_atoms3r
- m5stack_capsule - m5stack_capsule
# - m5stack_cardputer - m5stack_cardputer
# - m5stack_chain_dualkey
- m5stack_core - m5stack_core
- m5stack_core2 - m5stack_core2
- m5stack_coreink - m5stack_coreink
@@ -76,22 +78,30 @@ jobs:
- m5stack_dial - m5stack_dial
- m5stack_dinmeter - m5stack_dinmeter
- m5stack_fire - m5stack_fire
- m5stack_nano_c6
# - m5stack_nano_h2
- m5stack_paper - m5stack_paper
- m5stack_papers3
# - m5stack_poe_cam # - m5stack_poe_cam
# - m5stack_powerhub
# - m5stack_stamp_c3 # - m5stack_stamp_c3
# - m5stack_stamp_pico # - m5stack_stamp_pico
- m5stack_stamp_s3 - m5stack_stamp_s3
# - m5stack_stamplc
# - m5stack_station # - m5stack_station
# - m5stack_stickc - m5stack_stickc
- m5stack_stickc_plus - m5stack_stickc_plus
- m5stack_stickc_plus2 - m5stack_stickc_plus2
- m5stack_sticks3
- m5stack_tab5
# - m5stack_timer_cam # - m5stack_timer_cam
# - m5stack_tough # - m5stack_tough
# - m5stack_unit_c6l
# - m5stack_unit_cam # - m5stack_unit_cam
# - m5stack_unit_cams3 # - m5stack_unit_cams3
platform-version: platform-version:
- 2.1.2 - 3.2.5
platform: platform:
- m5stack - m5stack
@@ -101,71 +111,93 @@ jobs:
include: include:
# Specific sketches # Specific sketches
# HAT
- sketch: PlotToSerial - sketch: PlotToSerial
unit: HatHeart
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack platform: m5stack
archi: esp32 archi: esp32
platform-version: 2.1.2 platform-version: 3.2.5
board: m5stack_stickc_plus board: m5stack_stickc_plus
build-properties: "-DUSING_HAT_HEART"
- sketch: PlotToSerial - sketch: PlotToSerial
unit: HatHeart
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack platform: m5stack
archi: esp32 archi: esp32
platform-version: 2.1.2 platform-version: 3.2.5
board: m5stack_stickc_plus2 board: m5stack_stickc_plus2
- sketch: GraphicalMeter build-properties: "-DUSING_HAT_HEART"
unit: UnitHeart - sketch: PlotToSerial
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack platform: m5stack
archi: esp32 archi: esp32
platform-version: 2.1.2 platform-version: 3.2.5
board: m5stack_sticks3
build-properties: "-DUSING_HAT_HEART"
- sketch: PlotToSerial
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack
archi: esp32
platform-version: 3.2.5
board: m5stack_coreink
build-properties: "-DUSING_HAT_HEART"
- sketch: PlotToSerial
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack
archi: esp32
platform-version: 3.2.5
board: arduino_nesso_n1
build-properties: "-DUSING_HAT_HEART"
# GraphicalMeter
- sketch: GraphicalMeter
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack
archi: esp32
platform-version: 3.2.5
board: m5stack_core board: m5stack_core
build-properties: "-DUSING_UNIT_HEART"
- sketch: GraphicalMeter - sketch: GraphicalMeter
unit: HatHeart
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack platform: m5stack
archi: esp32 archi: esp32
platform-version: 2.1.2 platform-version: 3.2.5
board: m5stack_stickc_plus
- sketch: GraphicalMeter
unit: HatHeart
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack
archi: esp32
platform-version: 2.1.2
board: m5stack_stickc_plus2 board: m5stack_stickc_plus2
build-properties: "-DUSING_HAT_HEART"
# DualSensor
- sketch: DualSensor - sketch: DualSensor
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack platform: m5stack
archi: esp32 archi: esp32
platform-version: 2.1.2 platform-version: 3.2.5
board: m5stack_stickc_plus
- sketch: DualSensor
platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
platform: m5stack
archi: esp32
platform-version: 2.1.2
board: m5stack_stickc_plus2 board: m5stack_stickc_plus2
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
# Build - name: Prepare libraries list
id: libs
run: |
{
echo "yaml<<EOF"
echo "$REQUIRED_LIBRARIES" | tr ',' '\n' | while read -r lib; do
echo "- name: $lib"
done
echo "- source-path: ./"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Compile examples - name: Compile examples
uses: ArminJo/arduino-test-compile@master uses: arduino/compile-sketches@v1
with: with:
arduino-board-fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }}
arduino-platform: ${{ matrix.platform }}:${{ matrix.archi }}@${{ matrix.platform-version }} platforms: |
platform-url: ${{ matrix.platform-url }} - name: ${{ matrix.platform }}:${{ matrix.archi }}
required-libraries: ${{ env.REQUIRED_LIBRARIES }} source-url: ${{ matrix.platform-url }}
extra-arduino-cli-args: ${{ matrix.cli-args }} version: ${{ matrix.platform-version }}
#build-properties: ${{ toJson(matrix.build-properties) }} libraries: ${{ steps.libs.outputs.yaml }}
sketch-names: ${{ matrix.sketch }}.ino sketch-paths: |
sketch-names-find-start: ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.unit }} - ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.sketch }}
#sketches-exclude: ${{ matrix.sketches-exclude }} cli-compile-flags: |
- --build-property
- build.extra_flags=${{ matrix.build-properties }}
+3 -5
View File
@@ -17,7 +17,7 @@ on:
- '**.h' - '**.h'
- '**.c' - '**.c'
- '**.inl' - '**.inl'
- '**clang-format-check.yml' - '.github/workflows/clang-format-check.yml'
- '**.clang-format' - '**.clang-format'
pull_request: pull_request:
paths: paths:
@@ -27,7 +27,7 @@ on:
- '**.h' - '**.h'
- '**.c' - '**.c'
- '**.inl' - '**.inl'
- '**clang-format-check.yml' - '.github/workflows/clang-format-check.yml'
- '**.clang-format' - '**.clang-format'
workflow_dispatch: workflow_dispatch:
@@ -55,11 +55,9 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Run clang-format style check for C/C++/Protobuf programs. - name: Run clang-format style check for C/C++/Protobuf programs.
uses: jidicula/clang-format-action@v4.10.2 # Using include-regex 10.x or later uses: jidicula/clang-format-action@v4.16.0
with: with:
clang-format-version: '13' clang-format-version: '13'
check-path: ${{ matrix.path['check'] }} check-path: ${{ matrix.path['check'] }}
+3 -1
View File
@@ -1,8 +1,10 @@
name: Deploy Doxygen docuemnt on GitHub Pages name: Deploy Doxygen document on GitHub Pages
on: [release, workflow_dispatch] on: [release, workflow_dispatch]
# branches: # branches:
# - main # - main
# - master # - master
permissions:
contents: write
defaults: defaults:
run: run:
shell: bash shell: bash
+44 -58
View File
@@ -8,30 +8,30 @@ on:
branches: branches:
- '*' - '*'
paths: paths:
- 'src/unit/**.cpp' - 'src/**.cpp'
- 'src/unit/**.hpp' - 'src/**.hpp'
- 'src/unit/**.h' - 'src/**.h'
- 'src/unit/**.c' - 'src/**.c'
- 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.ino'
- 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.cpp'
- 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.hpp'
- 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.h'
- 'examples/UnitUnified/**.c' - 'examples/UnitUnified/**.c'
- '/platformio-build-check.yml' - '.github/workflows/platformio-build-check.yml'
- '**platformio.ini' - 'platformio.ini'
pull_request: pull_request:
paths: paths:
- 'src/unit/**.cpp' - 'src/**.cpp'
- 'src/unit/**.hpp' - 'src/**.hpp'
- 'src/unit/**.h' - 'src/**.h'
- 'src/unit/**.c' - 'src/**.c'
- 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.ino'
- 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.cpp'
- 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.hpp'
- 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.h'
- 'examples/UnitUnified/**.c' - 'examples/UnitUnified/**.c'
- '**/platformio-build-check.yml' - '.github/workflows/platformio-build-check.yml'
- '**platformio.ini' - 'platformio.ini'
workflow_dispatch: workflow_dispatch:
defaults: defaults:
@@ -46,11 +46,11 @@ jobs:
build: build:
name: ${{ matrix.unit }}:${{ matrix.example }}@${{ matrix.board }}:${{ matrix.framework }}:${{ matrix.espressif32 }} name: ${{ matrix.unit }}:${{ matrix.example }}@${{ matrix.board }}:${{ matrix.framework }}:${{ matrix.espressif32 }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5 timeout-minutes: 12
strategy: strategy:
fail-fast: false fail-fast: false
#max-parallel: 1 max-parallel: 20
matrix: matrix:
example: example:
@@ -66,55 +66,28 @@ jobs:
- Fire - Fire
- StampS3 - StampS3
- Dial - Dial
- AtomMatrix - Atom
- AtomS3 - AtomS3
- AtomS3R - AtomS3R
- NanoC6 - NanoC6
- StickCPlus - StickCPlus
- StickCPlus2 - StickCPlus2
- StickS3
- Paper - Paper
- CoreInk - CoreInk
- Cardputer
- Tab5
- NessoN1
framework: framework:
- Arduino - Arduino
espressif32: espressif32:
- latest - latest
# - '5_4_0'
# - '4_4_0'
# exclude:
# - board: CoreS3
# espressif32: '5_4_0'
# - board: CoreS3
# espressif32: '4_4_0'
# - board: StampS3
# espressif32: '5_4_0'
# - board: StampS3
# espressif32: '4_4_0'
# - board: AtomS3
# espressif32: '5_4_0'
# - board: AtomS3
# espressif32: '4_4_0'
# - board: Dial
# espressif32: '5_4_0'
# - board: Dial
# espressif32: '4_4_0'
# - board: NanoC6
# espressif32: '5_4_0'
# - board: NanoC6
# espressif32: '4_4_0'
# - board: StickCPlus
# espressif32: '5_4_0'
# - board: StickCPlus
# espressif32: '4_4_0'
# - board: Paper
# espressif32: '5_4_0'
# - board: Paper
# espressif32: '4_4_0'
include: include:
# Specific sketches # Specific sketches
# Hat
- example: PlotToSerial - example: PlotToSerial
unit: HatHeart unit: HatHeart
board: StickCPlus board: StickCPlus
@@ -125,36 +98,49 @@ jobs:
board: StickCPlus2 board: StickCPlus2
framework: Arduino framework: Arduino
espressif32: latest espressif32: latest
- example: PlotToSerial
unit: HatHeart
board: StickS3
framework: Arduino
espressif32: latest
- example: PlotToSerial
unit: HatHeart
board: CoreInk
framework: Arduino
espressif32: latest
- example: PlotToSerial
unit: HatHeart
board: NessoN1
framework: Arduino
espressif32: latest
# GraphicalMeter
- example: GraphicalMeter - example: GraphicalMeter
unit: UnitHeart unit: UnitHeart
board: Core board: Core
framework: Arduino framework: Arduino
espressif32: latest espressif32: latest
- example: GraphicalMeter
unit: HatHeart
board: StickCPlus
framework: Arduino
espressif32: latest
- example: GraphicalMeter - example: GraphicalMeter
unit: HatHeart unit: HatHeart
board: StickCPlus2 board: StickCPlus2
framework: Arduino framework: Arduino
espressif32: latest espressif32: latest
- example: DualSensor # DualSensor
board: StickCPlus
framework: Arduino
espressif32: latest
- example: DualSensor - example: DualSensor
board: StickCPlus2 board: StickCPlus2
framework: Arduino framework: Arduino
espressif32: latest espressif32: latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with: with:
ref: ${{ github.event.pull_request.head.sha }} python-version: '3.10'
- name: Install intelhex
run: pip install intelhex
- name: Build examples - name: Build examples
uses: karniv00l/platformio-run-action@v1 uses: karniv00l/platformio-run-action@v1
+21
View File
@@ -30,3 +30,24 @@
*.exe *.exe
*.out *.out
*.app *.app
# Doxygen
docs/html/
# PlatformIO
.pio/
# IDE
.vscode/
# macOS
.DS_Store
# Temporary
tmp/
# Secrets
.env*
*.pem
*.key
*.cert
+22 -11
View File
@@ -2,26 +2,20 @@
## Overview ## Overview
Library for UnitHEART [M5UnitUnified](https://github.com/m5stack/M5UnitUnified). Library for UnitHEART using [M5UnitUnified](https://github.com/m5stack/M5UnitUnified).
M5UnitUnified is a library for unified handling of various M5 units products. M5UnitUnified is a library for unified handling of various M5 units products.
### SKU:U029 ### SKU:U029
Unit Heart is a blood oxygen and heart rate sensor. Integrated MAX30100, it offers a complete pulse oximeter and heart rate sensor system solution. This is a non-invasive blood oxygen and heart rate sensor, integrating two infrared light-emitting diodes and a photodetector. The detection principle is to use infrared LEDs to illuminate and detect the proportion of red blood cells carrying oxygen versus those not carrying oxygen, thereby obtaining the oxygen content.
HEART is built using the MAX30100 chipset.
MAX30100 is a complete pulse oximetry and heart-rate sensor system solution designed for the demanding requirements of wearable devices.
The MAX30100 provides very small total solution size without sacrificing optical or electrical performance. Minimal external hardware components are needed for integration into a wearable device.
### SKU:U118 ### SKU:U118
Hat Heart is a blood oxygen and heart rate sensor. It integrates MAX30102, providing a complete pulse oximeter and heart rate sensor system solution. This is a non-invasive blood oxygen and heart rate sensor. Its detection principle is based on infrared LED light illumination, which detects the ratio of oxygenated to deoxygenated red blood cells to determine blood oxygen levels. The sensor uses an I2C communication interface and integrates infrared LEDs, photodetectors, optical components, and low-noise electronics, with some ambient light suppression for more accurate measurements.
HEART RATE HAT is a blood oxygen heart rate sensor. Integrate MAX30102 to provide a complete pulse oximeter and heart rate sensor system solution. This is a non-pluggable blood oxygen heart rate sensor. The sensor uses I2C communication interface, internally integrates infrared light-emitting diodes, photo-detectors, optical components and low-noise electronic equipment. A certain amount of ambient light suppression function can make the measurement results more accurate. .
## Related Link ## Related Link
See also examples using conventional methods here. See also examples using conventional methods here.
- [Unit Heart & Datasheet](https://docs.m5stack.com/ja/unit/heart) - [Unit Heart & Datasheet](https://docs.m5stack.com/en/unit/heart)
- [Hat Heart & Datasheet](https://docs.m5stack.com/en/hat/hat_heart_rate) - [Hat Heart & Datasheet](https://docs.m5stack.com/en/hat/hat_heart_rate)
## Required Libraries: ## Required Libraries:
@@ -32,12 +26,29 @@ See also examples using conventional methods here.
## License ## License
- [M5Unit-HEART- MIT](LICENSE) - [M5Unit-HEART - MIT](LICENSE)
## Examples ## Examples
See also [examples/UnitUnified](examples/UnitUnified) See also [examples/UnitUnified](examples/UnitUnified)
### For ArduinoIDE settings
You must choose a define symbol for the unit you will use.
(Rewrite source or specify with compile options)
- PlotToSerial / GraphicalMeter
```cpp
// *************************************************************
// Choose one define symbol to match the unit you are using
// *************************************************************
#if !defined(USING_UNIT_HEART) && !defined(USING_HAT_HEART)
// For UnitHeart (U029)
// #define USING_UNIT_HEART
// For HatHeart (U118)
// #define USING_HAT_HEART
#endif
```
## Doxygen document ## Doxygen document
[GitHub Pages](https://m5stack.github.io/M5Unit-HEART/) [GitHub Pages](https://m5stack.github.io/M5Unit-HEART/)
+1 -1
View File
@@ -42,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places. # title of most generated pages and in a few other places.
# The default value is: My Project. # The default value is: My Project.
PROJECT_NAME = M5Unit-HEART PROJECT_NAME = "M5Unit-HEART"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# Please execute on repositry root # Please execute on repository root
## Get version from library.properties ## Get version from library.properties
## Get git rev of HEAD ## Get git rev of HEAD
@@ -10,6 +10,7 @@
#include <M5UnitUnified.h> #include <M5UnitUnified.h>
#include <M5UnitUnifiedHEART.h> #include <M5UnitUnifiedHEART.h>
#include <Wire.h> #include <Wire.h>
#include <M5HAL.hpp>
#include "../src/view.hpp" #include "../src/view.hpp"
namespace { namespace {
@@ -19,6 +20,30 @@ m5::unit::UnitHeart unit;
m5::unit::HatHeart hat; m5::unit::HatHeart hat;
View* view[2]{}; View* view[2]{};
bool is_epd_panel{};
struct I2cPins {
int sda;
int scl;
};
I2cPins get_hat_i2c_pins(const m5::board_t board)
{
switch (board) {
case m5::board_t::board_M5StickC:
case m5::board_t::board_M5StickCPlus:
case m5::board_t::board_M5StickCPlus2:
return {0, 26};
case m5::board_t::board_M5StickS3:
return {8, 0};
case m5::board_t::board_M5StackCoreInk:
return {25, 26};
case m5::board_t::board_ArduinoNessoN1:
return {6, 7};
default:
return {-1, -1};
}
}
} // namespace } // namespace
@@ -32,39 +57,73 @@ void setup()
m5cfg.internal_imu = false; // Disable internal IMU m5cfg.internal_imu = false; // Disable internal IMU
m5cfg.internal_rtc = false; // Disable internal RTC m5cfg.internal_rtc = false; // Disable internal RTC
M5.begin(m5cfg); M5.begin(m5cfg);
M5.setTouchButtonHeightByRatio(100);
is_epd_panel = lcd.isEPD();
auto board = M5.getBoard(); const auto board_type = M5.getBoard();
if (board != m5::board_t::board_M5StickCPlus && board != m5::board_t::board_M5StickCPlus2) {
M5_LOGE("Example for StickCPlus/CPlus2");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
// The screen shall be in landscape mode // The screen shall be in landscape mode
if (lcd.height() > lcd.width()) { if (lcd.height() > lcd.width()) {
lcd.setRotation(1); lcd.setRotation(1);
} }
// Setup required to use HatHEART const auto pins = get_hat_i2c_pins(board_type);
pinMode(25, INPUT_PULLUP); M5_LOGI("getHatPin: SDA:%u SCL:%u", pins.sda, pins.scl);
pinMode(26, OUTPUT); if (pins.sda < 0 || pins.scl < 0) {
M5_LOGE("Hat port not exists");
lcd.fillScreen(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
// Wire settings // Setup required to use HatHEART
pinMode(pins.scl, OUTPUT);
// HatHeart settings
bool hat_ready{};
if (board_type == m5::board_t::board_ArduinoNessoN1) {
// Using wire1
Wire1.end();
Wire1.begin(pins.sda, pins.scl, 400 * 1000U);
hat_ready = Units.add(hat, Wire1);
} else {
// SoftwareI2C for Hat
m5::hal::bus::I2CBusConfig hat_i2c_cfg;
hat_i2c_cfg.pin_sda = m5::hal::gpio::getPin(pins.sda);
hat_i2c_cfg.pin_scl = m5::hal::gpio::getPin(pins.scl);
auto hat_i2c_bus = m5::hal::bus::i2c::getBus(hat_i2c_cfg);
hat_ready = Units.add(hat, hat_i2c_bus ? hat_i2c_bus.value() : nullptr);
}
// UnitHeart settings
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda); 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); auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
Wire.end(); bool unit_ready{};
Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U); if (board_type == m5::board_t::board_ArduinoNessoN1) {
// For NessoN1 GROVE
// Wire is used internally, so SoftwareI2C handles the unit
pin_num_sda = M5.getPin(m5::pin_name_t::port_b_out);
pin_num_scl = M5.getPin(m5::pin_name_t::port_b_in);
M5_LOGI("getPin(NessoN1): SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
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());
unit_ready = Units.add(unit, i2c_bus ? i2c_bus.value() : nullptr);
} else {
// UnitHeart on Wire
M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U);
unit_ready = Units.add(unit, Wire);
}
Wire1.end(); if (!hat_ready || !unit_ready || !Units.begin()) {
Wire1.begin(0, 26, 400 * 1000U); M5_LOGE("Failed to begin %u/%u", hat_ready, unit_ready);
M5_LOGE("%s", Units.debugInfo().c_str());
// UnitHeart connected to GROOVE with Wire lcd.fillScreen(TFT_RED);
// HatHeart connected to PIN sockect with Wire1
if (!Units.add(unit, Wire) || !Units.add(hat, Wire1) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) { while (true) {
m5::utility::delay(10000); m5::utility::delay(10000);
} }
@@ -73,14 +132,14 @@ void setup()
M5_LOGI("M5UnitUnified has been begun"); M5_LOGI("M5UnitUnified has been begun");
M5_LOGI("%s", Units.debugInfo().c_str()); M5_LOGI("%s", Units.debugInfo().c_str());
lcd.startWrite();
view[0] = new View(lcd.width() >> 1, lcd.height(), true); view[0] = new View(lcd.width() >> 1, lcd.height(), true);
view[1] = new View(lcd.width() >> 1, lcd.height(), false); view[1] = new View(lcd.width() >> 1, lcd.height(), false);
view[0]->_monitor.setSamplingRate(unit.caluculateSamplingRate()); view[0]->_monitor.setSamplingRate(unit.calculateSamplingRate());
view[1]->_monitor.setSamplingRate(hat.caluculateSamplingRate()); view[1]->_monitor.setSamplingRate(hat.calculateSamplingRate());
view[0]->push(&lcd, lcd.width() >> 1, 0); if (!is_epd_panel) {
view[1]->push(&lcd, 0, 0); view[0]->push(&lcd, lcd.width() >> 1, 0);
view[1]->push(&lcd, 0, 0);
}
} }
void loop() void loop()
@@ -88,34 +147,66 @@ void loop()
M5.update(); M5.update();
Units.update(); Units.update();
if (!is_epd_panel) {
lcd.startWrite();
}
if (unit.updated()) { if (unit.updated()) {
if (unit.overflow()) { if (unit.overflow()) {
M5_LOGW("OVERFLOW U:%u", unit.overflow()); M5_LOGW("OVERFLOW U:%u", unit.overflow());
} }
bool beat{};
while (unit.available()) { while (unit.available()) {
view[0]->push_back(unit.ir(), unit.red()); const auto ir = unit.ir();
const auto red = unit.red();
view[0]->push_back(ir, red);
view[0]->update(); view[0]->update();
if (is_epd_panel) {
M5.Log.printf(">UIR:%u\n>URED:%u\n", static_cast<uint32_t>(ir), static_cast<uint32_t>(red));
M5.Log.printf(">UMIR:%f\n", view[0]->_monitor.latestIR());
}
beat |= view[0]->_monitor.isBeat();
unit.discard(); unit.discard();
} }
view[0]->render(); if (is_epd_panel) {
view[0]->push(&lcd, lcd.width() >> 1, 0); M5.Log.printf(">UBPM:%f\n>USpO2:%f\n>UBEAT:%u\n", view[0]->_monitor.bpm(), view[0]->_monitor.SpO2(), beat);
}
if (!is_epd_panel) {
view[0]->render();
view[0]->push(&lcd, lcd.width() >> 1, 0);
}
} }
if (hat.updated()) { if (hat.updated()) {
if (hat.overflow()) { if (hat.overflow()) {
M5_LOGW("OVERFLOW H:%u", hat.overflow()); M5_LOGW("OVERFLOW H:%u", hat.overflow());
} }
bool beat{};
while (hat.available()) { while (hat.available()) {
view[1]->push_back(hat.ir(), hat.red()); const auto ir = hat.ir();
const auto red = hat.red();
view[1]->push_back(ir, red);
view[1]->update(); view[1]->update();
if (is_epd_panel) {
M5.Log.printf(">HIR:%u\n>HRED:%u\n", static_cast<uint32_t>(ir), static_cast<uint32_t>(red));
M5.Log.printf(">HMIR:%f\n", view[1]->_monitor.latestIR());
}
beat |= view[1]->_monitor.isBeat();
hat.discard(); hat.discard();
} }
view[1]->render(); if (is_epd_panel) {
view[1]->push(&lcd, 0, 0); M5.Log.printf(">HBPM:%f\n>HSpO2:%f\n>HBEAT:%u\n", view[1]->_monitor.bpm(), view[1]->_monitor.SpO2(), beat);
}
if (!is_epd_panel) {
view[1]->render();
view[1]->push(&lcd, 0, 0);
}
} }
if (M5.BtnA.wasClicked()) { if (M5.BtnA.wasClicked()) {
view[0]->clear(); view[0]->clear();
view[1]->clear(); view[1]->clear();
} }
if (!is_epd_panel) {
lcd.endWrite();
}
} }
@@ -12,12 +12,15 @@
class Meter { class Meter {
public: public:
Meter(const uint32_t left, const uint32_t top, const uint32_t wid, const uint32_t hgt, m5gfx::rgb565_t tcolor) Meter(const uint32_t left, const uint32_t top, const uint32_t wid, const uint32_t hgt, m5gfx::rgb565_t tcolor,
: _left(left), _top(top), _theme_color(tcolor) m5gfx::rgb565_t gcolor = TFT_DARKGRAY, m5gfx::rgb565_t bcolor = TFT_BLACK)
: _left(left), _top(top), _theme_color(tcolor), _gauge_color(gcolor), _background_color(bcolor)
{ {
_plotter = new m5::ui::Plotter(nullptr, wid, wid, hgt); _plotter = new m5::ui::Plotter(nullptr, wid, wid, hgt);
_plotter->setGaugeTextDatum(textdatum_t::top_right); _plotter->setGaugeTextDatum(textdatum_t::top_right);
_plotter->setLineColor(_theme_color); _plotter->setLineColor(_theme_color);
_plotter->setGaugeColor(_gauge_color);
_plotter->setBackgroundColor(_background_color);
} }
inline void push_back(const float value) inline void push_back(const float value)
@@ -25,7 +28,7 @@ public:
_plotter->push_back(value); _plotter->push_back(value);
} }
inline void push(LovyanGFX* target, const uint32_t x, const uint32_t y) inline void push(LovyanGFX* target)
{ {
_plotter->push(target, _left, _top); _plotter->push(target, _left, _top);
} }
@@ -39,5 +42,7 @@ private:
uint32_t _left{}, _top{}; uint32_t _left{}, _top{};
m5::ui::Plotter* _plotter{}; m5::ui::Plotter* _plotter{};
m5gfx::rgb565_t _theme_color{}; m5gfx::rgb565_t _theme_color{};
m5gfx::rgb565_t _gauge_color{TFT_DARKGRAY};
m5gfx::rgb565_t _background_color{TFT_BLACK};
}; };
#endif #endif
@@ -83,7 +83,8 @@ void Plotter::push(LovyanGFX* dst, const int32_t x, const int32_t y)
left += _wid - sz; left += _wid - sz;
} }
while (it != itend && left < _wid) { const int32_t right = x + _wid;
while (it != itend && left < right) {
int32_t s{*it}, e{*(++it)}; int32_t s{*it}, e{*(++it)};
dst->drawLine(left, y + hh - hh * (s - _min) / range, left + 1, y + hh - hh * (e - _min) / range, _lineClr); dst->drawLine(left, y + hh - hh * (s - _min) / range, left + 1, y + hh - hh * (e - _min) / range, _lineClr);
++left; ++left;
+19 -4
View File
@@ -11,9 +11,17 @@
struct View { struct View {
View(const uint32_t wid, const uint32_t hgt, bool unit) : _type{unit} View(const uint32_t wid, const uint32_t hgt, bool unit) : _type{unit}
{ {
_theme_color = unit ? TFT_BLUE : TFT_YELLOW;
_sprite.setPsram(false);
_sprite.setColorDepth(2);
_sprite.createSprite(wid, hgt); _sprite.createSprite(wid, hgt);
_sprite.setPaletteColor(palette_black, TFT_BLACK);
_sprite.setPaletteColor(palette_white, TFT_WHITE);
_sprite.setPaletteColor(palette_theme, _theme_color);
_sprite.setPaletteColor(palette_gauge, TFT_DARKGRAY);
_sprite.setTextColor(palette_white, palette_black);
_sprite.setFont(&fonts::FreeSansBold9pt7b); _sprite.setFont(&fonts::FreeSansBold9pt7b);
_meter = new Meter(0, hgt * 2 / 3, wid, hgt / 3, unit ? TFT_BLUE : TFT_YELLOW); _meter = new Meter(0, hgt * 2 / 3, wid, hgt / 3, palette_theme, palette_gauge, palette_black);
} }
inline void push_back(const float ir, const float red) inline void push_back(const float ir, const float red)
@@ -33,13 +41,13 @@ struct View {
void render() void render()
{ {
_sprite.clear(); _sprite.clear(palette_black);
_sprite.drawString(_type ? "Unit" : "Hat", 0, 0); _sprite.drawString(_type ? "Unit" : "Hat", 0, 0);
_sprite.setCursor(0, 24); _sprite.setCursor(0, 24);
_sprite.printf("BPM: %3.2f\nSpO2:%3.2f", _monitor.bpm(), _monitor.SpO2()); _sprite.printf("BPM: %3.2f\nSpO2:%3.2f", _monitor.bpm(), _monitor.SpO2());
_sprite.fillCircle(_sprite.width() - 12, 24 * 3, 7, _beat ? TFT_RED : TFT_WHITE); _sprite.fillCircle(_sprite.width() - 12, 24 * 3, 7, _beat ? palette_theme : palette_white);
_meter->push(&_sprite, 0, _sprite.height() >> 1); _meter->push(&_sprite);
} }
void clear() void clear()
@@ -53,10 +61,17 @@ struct View {
_sprite.pushSprite(target, x, y); _sprite.pushSprite(target, x, y);
} }
enum : uint8_t {
palette_black = 0,
palette_white = 1,
palette_theme = 2,
palette_gauge = 3,
};
LGFX_Sprite _sprite{}; LGFX_Sprite _sprite{};
m5::heart::PulseMonitor _monitor{100, 2}; m5::heart::PulseMonitor _monitor{100, 2};
Meter* _meter{}; Meter* _meter{};
uint32_t _left{}, _top{}, _beat{}; uint32_t _left{}, _top{}, _beat{};
bool _type{}; bool _type{};
m5gfx::rgb565_t _theme_color{};
}; };
#endif #endif
@@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Graphical meter example for UnitHeart / HatHeart
*/
// *************************************************************
// Choose one define symbol to match the unit you are using
// *************************************************************
#if !defined(USING_UNIT_HEART) && !defined(USING_HAT_HEART)
// For UnitHeart (U029)
// #define USING_UNIT_HEART
// For HatHeart (U118)
// #define USING_HAT_HEART
#endif
#include "main/GraphicalMeter.cpp"
@@ -0,0 +1,193 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Graphical meter example for UnitHeart / HatHeart
The core must be equipped with LCD
*/
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedHEART.h>
#include <Wire.h>
#include <M5HAL.hpp>
#include "../src/view.hpp"
// *************************************************************
// Choose one define symbol to match the unit you are using
// *************************************************************
#if !defined(USING_UNIT_HEART) && !defined(USING_HAT_HEART)
// #define USING_UNIT_HEART
// #define USING_HAT_HEART
#endif
#if defined(USING_UNIT_HEART)
#pragma message "Using UnitHeart"
using namespace m5::unit::max30100;
#elif defined(USING_HAT_HEART)
#pragma message "Using HatHeart"
using namespace m5::unit::max30102;
#else
#error Please choose unit!
#endif
namespace {
auto& lcd = M5.Display;
m5::unit::UnitUnified Units;
#if defined(USING_UNIT_HEART)
m5::unit::UnitHeart heart;
#elif defined(USING_HAT_HEART)
m5::unit::HatHeart heart;
#endif
View* view{};
#if defined(USING_HAT_HEART)
struct I2cPins {
int sda;
int scl;
};
I2cPins get_hat_i2c_pins(const m5::board_t board)
{
switch (board) {
case m5::board_t::board_M5StickC:
case m5::board_t::board_M5StickCPlus:
case m5::board_t::board_M5StickCPlus2:
return {0, 26};
case m5::board_t::board_M5StickS3:
return {8, 0};
case m5::board_t::board_M5StackCoreInk:
return {25, 26};
case m5::board_t::board_ArduinoNessoN1:
return {6, 7};
default:
return {-1, -1};
}
}
#endif
} // namespace
void setup()
{
auto m5cfg = M5.config();
#if defined(USING_HAT_HEART)
m5cfg.pmic_button = false; // Disable BtnPWR
m5cfg.internal_imu = false; // Disable internal IMU
m5cfg.internal_rtc = false; // Disable internal RTC
#endif
M5.begin(m5cfg);
M5.setTouchButtonHeightByRatio(100);
auto board = M5.getBoard();
// The screen shall be in landscape mode (skip for M5Tab5)
if (board != m5::board_t::board_M5Tab5 && lcd.height() > lcd.width()) {
lcd.setRotation(1);
}
#if defined(USING_HAT_HEART)
const auto pins = get_hat_i2c_pins(board);
M5_LOGI("getHatPin: SDA:%u SCL:%u", pins.sda, pins.scl);
if (pins.sda < 0 || pins.scl < 0) {
M5_LOGE("Illegal pin number");
lcd.fillScreen(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
// Setup required to use HatHEART
pinMode(pins.scl, OUTPUT);
auto& wire = (board == m5::board_t::board_ArduinoNessoN1) ? Wire1 : Wire;
wire.end();
wire.begin(pins.sda, pins.scl, 400 * 1000U);
if (!Units.add(heart, wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.fillScreen(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#else
// NessoN1: Arduino Wire (I2C_NUM_0) cannot be used for GROVE port.
// Wire is used by M5Unified In_I2C for internal devices (IOExpander etc.).
// Wire1 exists but is reserved for HatPort — cannot be used for GROVE.
// Reconfiguring Wire to GROVE pins breaks In_I2C, causing ESP_ERR_INVALID_STATE in M5.update().
// Solution: Use SoftwareI2C via M5HAL (bit-banging) for the GROVE port.
// NanoC6: Wire.begin() on GROVE pins conflicts with m5::I2C_Class registered by Ex_I2C.setPort()
// on the same I2C_NUM_0, causing sporadic NACK errors.
// Solution: Use M5.Ex_I2C (m5::I2C_Class) directly instead of Arduino Wire.
bool unit_ready{};
if (board == m5::board_t::board_ArduinoNessoN1) {
// NessoN1: GROVE is on port_b (GPIO 5/4), not port_a (which maps to Wire pins 8/10)
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_b_out);
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_b_in);
M5_LOGI("getPin(M5HAL): SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
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());
unit_ready = Units.add(heart, i2c_bus ? i2c_bus.value() : nullptr) && Units.begin();
} else if (board == m5::board_t::board_M5NanoC6) {
// NanoC6: Use M5.Ex_I2C (m5::I2C_Class, not Arduino Wire)
M5_LOGI("Using M5.Ex_I2C");
unit_ready = Units.add(heart, M5.Ex_I2C) && Units.begin();
} else {
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);
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400000U);
unit_ready = Units.add(heart, Wire) && Units.begin();
}
if (!unit_ready) {
M5_LOGE("Failed to begin");
lcd.fillScreen(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
#endif
M5_LOGI("M5UnitUnified has been begun");
M5_LOGI("%s", Units.debugInfo().c_str());
lcd.fillScreen(0);
#if defined(USING_UNIT_HEART)
view = new View(lcd.width(), lcd.height(), true);
#else
view = new View(lcd.width(), lcd.height(), false);
#endif
view->_monitor.setSamplingRate(heart.calculateSamplingRate());
view->push(&lcd, 0, 0);
M5_LOGI("periodic:%d", heart.inPeriodic());
}
void loop()
{
M5.update();
Units.update();
if (heart.updated()) {
if (heart.overflow()) {
M5_LOGW("OVERFLOW:%u", heart.overflow());
}
while (heart.available()) {
view->push_back(heart.ir(), heart.red());
view->update();
heart.discard();
}
view->render();
view->push(&lcd, 0, 0);
}
if (M5.BtnA.wasClicked()) {
view->clear();
}
}
@@ -12,12 +12,15 @@
class Meter { class Meter {
public: public:
Meter(const uint32_t left, const uint32_t top, const uint32_t wid, const uint32_t hgt, m5gfx::rgb565_t tcolor) Meter(const uint32_t left, const uint32_t top, const uint32_t wid, const uint32_t hgt, m5gfx::rgb565_t tcolor,
: _left(left), _top(top), _theme_color(tcolor) m5gfx::rgb565_t gcolor = TFT_DARKGRAY, m5gfx::rgb565_t bcolor = TFT_BLACK)
: _left(left), _top(top), _theme_color(tcolor), _gauge_color(gcolor), _background_color(bcolor)
{ {
_plotter = new m5::ui::Plotter(nullptr, wid, wid, hgt); _plotter = new m5::ui::Plotter(nullptr, wid, wid, hgt);
_plotter->setGaugeTextDatum(textdatum_t::top_right); _plotter->setGaugeTextDatum(textdatum_t::top_right);
_plotter->setLineColor(_theme_color); _plotter->setLineColor(_theme_color);
_plotter->setGaugeColor(_gauge_color);
_plotter->setBackgroundColor(_background_color);
} }
inline void push_back(const float value) inline void push_back(const float value)
@@ -25,7 +28,7 @@ public:
_plotter->push_back(value); _plotter->push_back(value);
} }
inline void push(LovyanGFX* target, const uint32_t x, const uint32_t y) inline void push(LovyanGFX* target)
{ {
_plotter->push(target, _left, _top); _plotter->push(target, _left, _top);
} }
@@ -39,5 +42,7 @@ private:
uint32_t _left{}, _top{}; uint32_t _left{}, _top{};
m5::ui::Plotter* _plotter{}; m5::ui::Plotter* _plotter{};
m5gfx::rgb565_t _theme_color{}; m5gfx::rgb565_t _theme_color{};
m5gfx::rgb565_t _gauge_color{TFT_DARKGRAY};
m5gfx::rgb565_t _background_color{TFT_BLACK};
}; };
#endif #endif
@@ -83,7 +83,8 @@ void Plotter::push(LovyanGFX* dst, const int32_t x, const int32_t y)
left += _wid - sz; left += _wid - sz;
} }
while (it != itend && left < _wid) { const int32_t right = x + _wid;
while (it != itend && left < right) {
int32_t s{*it}, e{*(++it)}; int32_t s{*it}, e{*(++it)};
dst->drawLine(left, y + hh - hh * (s - _min) / range, left + 1, y + hh - hh * (e - _min) / range, _lineClr); dst->drawLine(left, y + hh - hh * (s - _min) / range, left + 1, y + hh - hh * (e - _min) / range, _lineClr);
++left; ++left;
@@ -0,0 +1,124 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#ifndef M5_UNIT_HEART_EXAMPLE_VIEW_HPP
#define M5_UNIT_HEART_EXAMPLE_VIEW_HPP
#include "meter.hpp"
struct View {
View(const uint32_t wid, const uint32_t hgt, bool unit) : _type{unit}
{
_theme_color = unit ? TFT_BLUE : TFT_YELLOW;
_screen_w = wid;
_screen_h = hgt;
_plot_top = hgt * 2 / 3;
_plot_hgt = hgt / 3;
const uint32_t spr_w = wid;
const uint32_t spr_h = _plot_hgt;
_plot_sprite.setPsram(false);
_plot_sprite.setColorDepth(2);
_plot_sprite.createSprite(spr_w, spr_h);
_plot_sprite.setPaletteColor(palette_black, TFT_BLACK);
_plot_sprite.setPaletteColor(palette_white, TFT_WHITE);
_plot_sprite.setPaletteColor(palette_theme, _theme_color);
_plot_sprite.setPaletteColor(palette_gauge, TFT_DARKGRAY);
_plot_sprite.setTextColor(palette_white, palette_black);
_plot_sprite.setFont(&fonts::FreeSansBold9pt7b);
_text_sprite.setPsram(false);
_text_sprite.setColorDepth(2);
_text_sprite.setPaletteColor(palette_black, TFT_BLACK);
_text_sprite.setPaletteColor(palette_white, TFT_WHITE);
_text_sprite.setPaletteColor(palette_theme, _theme_color);
_text_sprite.setPaletteColor(palette_gauge, TFT_DARKGRAY);
_text_sprite.setTextColor(palette_white, palette_black);
_text_sprite.setFont(&fonts::FreeSansBold9pt7b);
const int32_t line_h = _text_sprite.fontHeight();
const int32_t title_w = _text_sprite.textWidth("Unit");
const int32_t bpm_w = _text_sprite.textWidth("BPM: 000.00");
const int32_t spo2_w = _text_sprite.textWidth("SpO2:100.00");
_text_w = std::max(title_w, std::max(bpm_w, spo2_w));
_text_h = line_h * 3;
_text_sprite.createSprite(_text_w, _text_h);
_text_sprite.setPaletteColor(palette_black, TFT_BLACK);
_text_sprite.setPaletteColor(palette_white, TFT_WHITE);
_text_sprite.setPaletteColor(palette_theme, _theme_color);
_text_sprite.setPaletteColor(palette_gauge, TFT_DARKGRAY);
_text_sprite.setTextColor(palette_white, palette_black);
_text_sprite.setFont(&fonts::FreeSansBold9pt7b);
_beat_size = 16;
_beat_x = _screen_w - _beat_size - 2;
_beat_y = (line_h * 2) + (line_h / 2) - (_beat_size / 2);
_beat_sprite.setPsram(false);
_beat_sprite.setColorDepth(2);
_beat_sprite.createSprite(_beat_size, _beat_size);
_beat_sprite.setPaletteColor(palette_black, TFT_BLACK);
_beat_sprite.setPaletteColor(palette_white, TFT_WHITE);
_beat_sprite.setPaletteColor(palette_theme, _theme_color);
_beat_sprite.setPaletteColor(palette_gauge, TFT_DARKGRAY);
_meter = new Meter(0, 0, spr_w, spr_h, palette_theme, palette_gauge, palette_black);
}
inline void push_back(const float ir, const float red)
{
_monitor.push_back(ir, red);
}
void update()
{
if (_beat) {
--_beat;
}
_meter->push_back(_monitor.latestIR());
_monitor.update();
_beat += _monitor.isBeat() * 8;
}
void render()
{
_plot_sprite.clear(palette_black);
_meter->push(&_plot_sprite);
_text_sprite.clear(palette_black);
_text_sprite.drawString(_type ? "Unit" : "Hat", 0, 0);
_text_sprite.setCursor(0, _text_sprite.fontHeight());
_text_sprite.printf("BPM: %3.2f\nSpO2:%3.2f", _monitor.bpm(), _monitor.SpO2());
_beat_sprite.clear(palette_black);
_beat_sprite.fillCircle(_beat_size / 2, _beat_size / 2, 7, _beat ? palette_theme : palette_white);
}
void clear()
{
_monitor.clear();
_meter->clear();
_plot_sprite.clear(palette_black);
_text_sprite.clear(palette_black);
_beat_sprite.clear(palette_black);
}
void push(LovyanGFX* target, const uint32_t x, const uint32_t y)
{
_text_sprite.pushSprite(target, x, y);
_beat_sprite.pushSprite(target, x + _beat_x, y + _beat_y);
_plot_sprite.pushSprite(target, x, y + _plot_top);
}
enum : uint8_t {
palette_black = 0,
palette_white = 1,
palette_theme = 2,
palette_gauge = 3,
};
LGFX_Sprite _plot_sprite{};
LGFX_Sprite _text_sprite{};
LGFX_Sprite _beat_sprite{};
m5::heart::PulseMonitor _monitor{100, 2};
Meter* _meter{};
uint32_t _left{}, _top{}, _beat{};
uint32_t _screen_w{}, _screen_h{}, _plot_top{}, _plot_hgt{};
int32_t _text_w{}, _text_h{}, _beat_x{}, _beat_y{}, _beat_size{};
bool _type{};
m5gfx::rgb565_t _theme_color{};
};
#endif

Some files were not shown because too many files have changed in this diff Show More