はじめに
M5Stack Core2とサーマルカメラユニット2(MLX90640)を組み合わせ、UIFlow 1で32×24画素の温度分布をヒートマップ表示しました。

実装した機能は次のとおりです。
- 32×24画素のサーマル画像表示
- 青→水色→緑→黄→赤のカラーマップ
- 最高温度・平均温度の表示
- 温度レベルバーの表示
- 設定温度を超えた場合の警告表示
- Thermal2本体の警告LED制御
使用したハードウェア
- M5Stack Core2
- サーマルカメラユニット2(MLX90640)
- Groveケーブル
- Core2のPORT A
Thermal2の主な仕様は次のとおりです。
| 項目 | 内容 |
|---|---|
| 温度センサー | MLX90640 |
| 解像度 | 32×24画素 |
| 画角 | 110°×75° |
| 測定範囲 | -40~300℃ |
| 通信方式 | I2C |
開発環境
- UIFlow 1
- MicroPython
- M5Stack Core2
- Thermal2 Unit
UIFlow 1でUnitから「THERMAL2」を追加すると、初期コードは次の形になります。
from m5stack import *
from m5stack_ui import *
from uiflow import *
import unit
screen = M5Screen()
screen.clean_screen()
screen.set_screen_bg_color(0xFFFFFF)
thermal2_0 = unit.get(unit.THERMAL2, unit.PORTA)
AIモデルを比較して感じたこと
最初にGemini 3.1 Proで開発したプログラムは、センサーには反応したものの、サーマル画像がほぼ真っ赤になり、手の形も把握できませんでした。
一方、GPT 5.6 Solでは、768バイトのデータ構造、16bit温度値、サブページの合成、色形式という原因に短時間でたどり着けました。今回のThermal2/UIFlow 1の開発に限っては、GPT 5.6 Solの方が短時間で正解に到達でき、優秀でした。
AIが生成したコードは、そのまま動くかどうかだけでなく、センサーの通信仕様やデータ形式と一致しているかを確認することが重要だと感じました。
完成したプログラム(残像対策・人体検知最適化版)
以下のコードをUIFlow 1のPython編集画面へ貼り付けます。
# =========================================================
# M5Stack Core2 + Thermal2
# 残像対策・人体検知最適化版
# =========================================================
from m5stack import *
from m5stack_ui import *
from uiflow import *
import unit
import time
# =========================================================
# 画面とThermal2の初期化
# =========================================================
screen = M5Screen()
screen.clean_screen()
screen.set_screen_bg_color(0x0F172A)
# Thermal2をCore2のPORT Aへ接続
thermal2_0 = unit.get(unit.THERMAL2, unit.PORTA)
# =========================================================
# センサー・表示設定
# =========================================================
SENSOR_W = 32
SENSOR_H = 24
PIXEL_COUNT = SENSOR_W * SENSOR_H
MAP_X = 12
MAP_Y = 55
PIXEL_SIZE = 6
BACKGROUND_COLOR = 0x0F172A
PANEL_COLOR = 0x1E293B
BORDER_COLOR = 0x475569
TEXT_COLOR = 0xE2E8F0
SUB_TEXT_COLOR = 0x94A3B8
NORMAL_COLOR = 0x4ADE80
ALERT_COLOR = 0xF87171
PERSON_BOX_COLOR = 0xFFFFFF
# カメラの向きに応じて変更
FLIP_X = False
FLIP_Y = False
# =========================================================
# 人体検知設定
# =========================================================
# 周囲温度より1.5℃以上高い画素を人体候補とする
HUMAN_DELTA_TEMP = 1.5
# 人体候補として扱う最低温度
HUMAN_MIN_TEMP = 26.0
# 10画素以上が閾値を超えた場合に人体と判定
MIN_HUMAN_PIXELS = 10
# 高温警告温度
ALERT_TEMP = 38.0
# 新しい完全フレームが届かない場合に画像を消す時間
STALE_TIMEOUT_MS = 1500
# =========================================================
# フレームバッファ
# =========================================================
# subpage 0と1を合成する32×24画素のバッファ
build_frame = [20.0] * PIXEL_COUNT
# bit0=subpage 0、bit1=subpage 1
received_subpages = 0
last_complete_frame_ms = time.ticks_ms()
stale_screen_cleared = False
# =========================================================
# 固定画面の描画
# =========================================================
lcd.font(lcd.FONT_DejaVu18)
lcd.setTextColor(0x38BDF8, BACKGROUND_COLOR)
lcd.print("THERMAL HUMAN", 12, 9)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(SUB_TEXT_COLOR, BACKGROUND_COLOR)
lcd.print("Waiting for full frame...", 12, 34)
lcd.drawRect(
MAP_X - 1,
MAP_Y - 1,
SENSOR_W * PIXEL_SIZE + 2,
SENSOR_H * PIXEL_SIZE + 2,
BORDER_COLOR
)
lcd.fillRect(210, 55, 105, 145, PANEL_COLOR)
lcd.drawRect(210, 55, 105, 145, BORDER_COLOR)
# =========================================================
# RGB888色生成
# =========================================================
def rgb888(r, g, b):
r = max(0, min(255, int(r)))
g = max(0, min(255, int(g)))
b = max(0, min(255, int(b)))
return (r << 16) | (g << 8) | b
# =========================================================
# 温度をヒートマップ色へ変換
# =========================================================
def temperature_to_color(temp, display_min, display_max):
if display_max <= display_min:
display_max = display_min + 1.0
ratio = (temp - display_min) / (display_max - display_min)
ratio = max(0.0, min(1.0, ratio))
# 青 → 水色 → 緑 → 黄 → 赤
if ratio < 0.25:
p = ratio / 0.25
r = 0
g = 255 * p
b = 255
elif ratio < 0.50:
p = (ratio - 0.25) / 0.25
r = 0
g = 255
b = 255 * (1.0 - p)
elif ratio < 0.75:
p = (ratio - 0.50) / 0.25
r = 255 * p
g = 255
b = 0
else:
p = (ratio - 0.75) / 0.25
r = 255
g = 255 * (1.0 - p)
b = 0
return rgb888(r, g, b)
# =========================================================
# Thermal2の生データを摂氏へ変換
# =========================================================
def raw_to_temperature(raw_value):
# Thermal2公式変換式
return (float(raw_value) / 128.0) - 64.0
# =========================================================
# temp_data_buffer()を384個の温度へ変換
# =========================================================
def decode_buffer(raw_buffer):
if raw_buffer is None:
return []
data = list(raw_buffer)
data_length = len(data)
temperatures = []
# 768バイトの場合は2バイトで1温度
if data_length == 768:
for index in range(0, 768, 2):
low_byte = int(data[index])
high_byte = int(data[index + 1])
raw_value = low_byte | (high_byte << 8)
temperatures.append(raw_to_temperature(raw_value))
# UIFlowのバージョンにより384要素で返る場合にも対応
elif data_length == 384:
for value in data:
numeric_value = float(value)
if numeric_value > 500:
numeric_value = raw_to_temperature(numeric_value)
temperatures.append(numeric_value)
return temperatures
# =========================================================
# 1サブページを32×24フレームへ合成
# =========================================================
def merge_subpage(subpage, temperatures):
global received_subpages
if len(temperatures) != 384:
return False
subpage = int(subpage) & 1
for y in range(SENSOR_H):
for half_x in range(16):
source_index = y * 16 + half_x
# 市松模様状のサブページを実際のX座標へ戻す
x_parity = subpage ^ (y & 1)
x = half_x * 2 + x_parity
frame_index = y * SENSOR_W + x
build_frame[frame_index] = temperatures[source_index]
received_subpages |= (1 << subpage)
# subpage 0と1が両方揃った場合だけTrue
return received_subpages == 3
# =========================================================
# 周囲温度と人体候補を解析
# =========================================================
def analyze_frame(frame):
# 中央値を周囲温度として使用する
sorted_values = list(frame)
sorted_values.sort()
ambient_temp = sorted_values[len(sorted_values) // 2]
maximum_temp = sorted_values[-1]
# 室温に応じて人体検知閾値を自動調整する
human_threshold = max(
HUMAN_MIN_TEMP,
ambient_temp + HUMAN_DELTA_TEMP
)
human_pixel_count = 0
min_x = SENSOR_W
min_y = SENSOR_H
max_x = -1
max_y = -1
for y in range(SENSOR_H):
for x in range(SENSOR_W):
temp = frame[y * SENSOR_W + x]
if temp >= human_threshold:
human_pixel_count += 1
if x < min_x:
min_x = x
if x > max_x:
max_x = x
if y < min_y:
min_y = y
if y > max_y:
max_y = y
person_detected = human_pixel_count >= MIN_HUMAN_PIXELS
if not person_detected:
min_x = -1
min_y = -1
max_x = -1
max_y = -1
return (
ambient_temp,
maximum_temp,
human_threshold,
human_pixel_count,
person_detected,
min_x,
min_y,
max_x,
max_y
)
# =========================================================
# 完全な32×24フレームを全画素再描画
#
# サブページ単位では描画せず、0と1が揃ってから描画する。
# 古い半フレームが残る「残像」を防止できる。
# =========================================================
def draw_complete_frame(frame, display_min, display_max):
for y in range(SENSOR_H):
for x in range(SENSOR_W):
temperature = frame[y * SENSOR_W + x]
draw_x = x
draw_y = y
if FLIP_X:
draw_x = SENSOR_W - 1 - draw_x
if FLIP_Y:
draw_y = SENSOR_H - 1 - draw_y
screen_x = MAP_X + draw_x * PIXEL_SIZE
screen_y = MAP_Y + draw_y * PIXEL_SIZE
color = temperature_to_color(
temperature,
display_min,
display_max
)
# 全画素を上書きして以前の画像を消す
lcd.fillRect(
screen_x,
screen_y,
PIXEL_SIZE,
PIXEL_SIZE,
color
)
# =========================================================
# 人体候補の範囲を白枠で表示
# =========================================================
def draw_person_box(min_x, min_y, max_x, max_y):
if min_x < 0:
return
if FLIP_X:
box_min_x = SENSOR_W - 1 - max_x
box_max_x = SENSOR_W - 1 - min_x
else:
box_min_x = min_x
box_max_x = max_x
if FLIP_Y:
box_min_y = SENSOR_H - 1 - max_y
box_max_y = SENSOR_H - 1 - min_y
else:
box_min_y = min_y
box_max_y = max_y
x = MAP_X + box_min_x * PIXEL_SIZE
y = MAP_Y + box_min_y * PIXEL_SIZE
width = (box_max_x - box_min_x + 1) * PIXEL_SIZE
height = (box_max_y - box_min_y + 1) * PIXEL_SIZE
lcd.drawRect(x, y, width, height, PERSON_BOX_COLOR)
# =========================================================
# 情報パネルを更新
# =========================================================
def draw_information(
maximum_temp,
ambient_temp,
human_threshold,
human_pixel_count,
person_detected
):
# パネルを毎回消去して古い文字を残さない
lcd.fillRect(211, 56, 103, 143, PANEL_COLOR)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(SUB_TEXT_COLOR, PANEL_COLOR)
lcd.print("MAX", 217, 64)
lcd.print("ROOM", 217, 100)
lcd.print("THRESH", 217, 136)
lcd.font(lcd.FONT_DejaVu18)
lcd.setTextColor(ALERT_COLOR, PANEL_COLOR)
lcd.print("{:.1f}".format(maximum_temp), 217, 76)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(TEXT_COLOR, PANEL_COLOR)
lcd.print("{:.1f}C".format(ambient_temp), 217, 113)
lcd.print("{:.1f}C".format(human_threshold), 217, 149)
if person_detected:
lcd.setTextColor(NORMAL_COLOR, PANEL_COLOR)
lcd.print("PERSON:{}".format(human_pixel_count), 217, 178)
else:
lcd.setTextColor(SUB_TEXT_COLOR, PANEL_COLOR)
lcd.print("NO TARGET", 217, 178)
lcd.fillRect(190, 8, 125, 22, BACKGROUND_COLOR)
lcd.font(lcd.FONT_DejaVu18)
if maximum_temp >= ALERT_TEMP:
lcd.setTextColor(0xFF0000, BACKGROUND_COLOR)
lcd.print("ALERT", 242, 9)
elif person_detected:
lcd.setTextColor(NORMAL_COLOR, BACKGROUND_COLOR)
lcd.print("PERSON", 230, 9)
else:
lcd.setTextColor(SUB_TEXT_COLOR, BACKGROUND_COLOR)
lcd.print("CLEAR", 242, 9)
# =========================================================
# 古い画像を消去
# =========================================================
def clear_stale_image():
lcd.fillRect(
MAP_X,
MAP_Y,
SENSOR_W * PIXEL_SIZE,
SENSOR_H * PIXEL_SIZE,
BACKGROUND_COLOR
)
lcd.drawRect(
MAP_X - 1,
MAP_Y - 1,
SENSOR_W * PIXEL_SIZE + 2,
SENSOR_H * PIXEL_SIZE + 2,
BORDER_COLOR
)
lcd.fillRect(12, 33, 190, 15, BACKGROUND_COLOR)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(ALERT_COLOR, BACKGROUND_COLOR)
lcd.print("Waiting for fresh frame", 12, 34)
# =========================================================
# Thermal2設定
# =========================================================
try:
device_id = thermal2_0.device_info(0x00)
thermal2_0.func_ctrl(0x02, True)
# 8Hzにして動きへの追従を改善する
thermal2_0.refresh_rate(4)
# フィルターを弱め、時間方向の残りを抑える
thermal2_0.noise_filter(1)
thermal2_0.temp_threshold(0x30, ALERT_TEMP)
thermal2_0.temp_alarm_led(0x35, 200, 0, 0)
thermal2_0.temp_alarm_ctrl(0x80, True)
lcd.fillRect(12, 33, 190, 15, BACKGROUND_COLOR)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(NORMAL_COLOR, BACKGROUND_COLOR)
lcd.print("Sensor Ready ID:{}".format(device_id), 12, 34)
except Exception as initialization_error:
lcd.fillRect(12, 33, 190, 15, BACKGROUND_COLOR)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(ALERT_COLOR, BACKGROUND_COLOR)
lcd.print(
"Init:{}".format(str(initialization_error)[:24]),
12,
34
)
# =========================================================
# メインループ
# =========================================================
while True:
try:
update_available = thermal2_0.data_refresh_ctrl()
if update_available:
subpage = thermal2_0.subpage_info()
raw_buffer = thermal2_0.temp_data_buffer()
temperatures = decode_buffer(raw_buffer)
if len(temperatures) == 384:
full_frame_ready = merge_subpage(
subpage,
temperatures
)
# 2つのサブページが揃った場合だけ描画
if full_frame_ready:
analysis = analyze_frame(build_frame)
ambient_temp = analysis[0]
maximum_temp = analysis[1]
human_threshold = analysis[2]
human_pixel_count = analysis[3]
person_detected = analysis[4]
min_x = analysis[5]
min_y = analysis[6]
max_x = analysis[7]
max_y = analysis[8]
# 室温に応じて表示温度範囲も自動調整
display_min = ambient_temp - 2.0
display_max = max(
ambient_temp + 8.0,
maximum_temp + 1.0
)
display_min = max(5.0, display_min)
display_max = min(45.0, display_max)
draw_complete_frame(
build_frame,
display_min,
display_max
)
if person_detected:
draw_person_box(
min_x,
min_y,
max_x,
max_y
)
draw_information(
maximum_temp,
ambient_temp,
human_threshold,
human_pixel_count,
person_detected
)
last_complete_frame_ms = time.ticks_ms()
stale_screen_cleared = False
# 次の完全フレームを待つ
received_subpages = 0
# 完全フレームが一定時間届かなければ古い画像を消す
elapsed_ms = time.ticks_diff(
time.ticks_ms(),
last_complete_frame_ms
)
if elapsed_ms > STALE_TIMEOUT_MS:
if not stale_screen_cleared:
clear_stale_image()
stale_screen_cleared = True
received_subpages = 0
wait_ms(10)
except Exception as error:
lcd.fillRect(12, 207, 303, 14, BACKGROUND_COLOR)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(ALERT_COLOR, BACKGROUND_COLOR)
lcd.print(
"Error:{}".format(str(error)[:34]),
12,
207
)
print("Thermal2 error:")
print(error)
wait_ms(300)
旧版コード(参考)
# =========================================================
# ライブラリの読み込み
# =========================================================
# M5Stack本体、画面制御、UIFlowの標準機能を読み込む
from m5stack import *
from m5stack_ui import *
from uiflow import *
# Unit制御用ライブラリ
import unit
import time
# =========================================================
# Core2の画面とThermal2を初期化
# =========================================================
# Core2の画面オブジェクトを作成し、既存の表示を消去する
screen = M5Screen()
screen.clean_screen()
# 画面背景をディープネイビーに設定する
screen.set_screen_bg_color(0x0F172A)
# Thermal2をCore2のPORT Aに接続して初期化する
thermal2_0 = unit.get(unit.THERMAL2, unit.PORTA)
# =========================================================
# サーマル画像と画面表示に関する設定
# =========================================================
# MLX90640のセンサー解像度は32×24画素
SENSOR_W = 32
SENSOR_H = 24
# Core2画面上でサーマル画像を描き始める座標
START_X = 12
START_Y = 55
# センサーの1画素をCore2画面上の6×6ピクセルで表示する
# 32×6=192、24×6=144の表示領域になる
PIXEL_SIZE = 6
# カラーマップへ割り当てる温度範囲
# 15℃以下を青、40℃以上を赤として表示する
DISPLAY_MIN_TEMP = 15.0
DISPLAY_MAX_TEMP = 40.0
# 高温警告を出す温度
ALERT_TEMP = 38.0
# カメラの取り付け方向に合わせて画像を反転する設定
FLIP_X = False
FLIP_Y = False
# 32×24=768画素分の温度を保持するフレームバッファ
# 最初は全画素を20℃で初期化する
# Thermal2から届く2つのサブページをこの配列へ順番に合成する
temperature_frame = [20.0] * 768
# =========================================================
# 画面の固定部分を描画
# =========================================================
# 画面上部へタイトルを表示する
lcd.font(lcd.FONT_DejaVu18)
lcd.setTextColor(0x38BDF8, 0x0F172A)
lcd.print("UNIT THERMAL 2", 15, 10)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(0x94A3B8, 0x0F172A)
lcd.print("Connecting to sensor...", 15, 34)
lcd.drawRect(
START_X - 1,
START_Y - 1,
SENSOR_W * PIXEL_SIZE + 2,
SENSOR_H * PIXEL_SIZE + 2,
0x475569
)
lcd.drawRect(210, 55, 105, 145, 0x475569)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(0x94A3B8, 0x0F172A)
lcd.print("MAX", 218, 62)
lcd.print("AVG", 218, 105)
lcd.print("LEVEL", 275, 62)
# =========================================================
# 色変換関数
# =========================================================
def rgb888(r, g, b):
# RGBの各値を0~255の範囲に制限する
r = max(0, min(255, int(r)))
g = max(0, min(255, int(g)))
b = max(0, min(255, int(b)))
# lcd.fillRect()で使用する0xRRGGBB形式へ変換する
# RGB565へ変換しないことが重要
return (r << 16) | (g << 8) | b
def get_color(temp):
# 温度を0.0~1.0の割合へ正規化する
ratio = (
(temp - DISPLAY_MIN_TEMP)
/ (DISPLAY_MAX_TEMP - DISPLAY_MIN_TEMP)
)
ratio = max(0.0, min(1.0, ratio))
# 表示範囲外の温度を0.0~1.0に収める
ratio = max(0.0, min(1.0, ratio))
# 低温から高温へ、青→水色→緑→黄→赤と変化させる
if ratio < 0.25:
# 青から水色
p = ratio / 0.25
r = 0
g = 255 * p
b = 255
elif ratio < 0.50:
p = (ratio - 0.25) / 0.25
r = 0
g = 255
b = 255 * (1.0 - p)
elif ratio < 0.75:
p = (ratio - 0.50) / 0.25
r = 255 * p
g = 255
b = 0
else:
p = (ratio - 0.75) / 0.25
r = 255
g = 255 * (1.0 - p)
b = 0
return rgb888(r, g, b)
# =========================================================
# Thermal2の生データを温度へ変換
# =========================================================
def raw_to_temperature(raw_value):
# Thermal2の公式変換式
# 例:8192なら0℃、12032なら30℃になる
return (float(raw_value) / 128.0) - 64.0
def decode_buffer(raw_buffer):
# データを取得できなかった場合は空配列を返す
if raw_buffer is None:
return []
data = list(raw_buffer)
data_length = len(data)
temperatures = []
# 768バイトの場合:2バイトで1画素なので384画素分
if data_length == 768:
for index in range(0, 768, 2):
# little endian形式の下位・上位バイトを取り出す
low_byte = int(data[index])
high_byte = int(data[index + 1])
# 2バイトを1つの16bit整数へ戻す
raw_value = low_byte | (high_byte << 8)
# 生データを摂氏へ変換して配列へ追加する
temperatures.append(raw_to_temperature(raw_value))
# UIFlowのバージョンによって384要素で返る場合にも対応する
elif data_length == 384:
for value in data:
numeric_value = float(value)
if numeric_value > 500:
temperature = raw_to_temperature(numeric_value)
else:
temperature = numeric_value
temperatures.append(temperature)
return temperatures
# =========================================================
# サブページを32×24画素のフレームへ合成
# =========================================================
def update_temperature_frame(subpage, temperatures):
# 1サブページは16×24=384画素
if len(temperatures) != 384:
return False
# サブページ番号を0または1に正規化する
subpage = int(subpage) & 1
for y in range(SENSOR_H):
for half_x in range(16):
# 16×24配列内の読み取り位置
source_index = y * 16 + half_x
# MLX90640は市松模様状にsubpage 0/1を更新するため、
# 行の偶奇とサブページ番号から実際のX座標を計算する
x_parity = subpage ^ (y & 1)
x = half_x * 2 + x_parity
# 32×24フレーム上の位置へ温度を書き込む
frame_index = y * SENSOR_W + x
temperature_frame[frame_index] = temperatures[source_index]
return True
# =========================================================
# 更新されたサブページをCore2画面へ描画
# =========================================================
def draw_subpage(subpage):
subpage = int(subpage) & 1
for y in range(SENSOR_H):
for half_x in range(16):
x_parity = subpage ^ (y & 1)
x = half_x * 2 + x_parity
frame_index = y * SENSOR_W + x
temperature = temperature_frame[frame_index]
draw_x = x
draw_y = y
if FLIP_X:
draw_x = SENSOR_W - 1 - draw_x
if FLIP_Y:
draw_y = SENSOR_H - 1 - draw_y
screen_x = START_X + draw_x * PIXEL_SIZE
screen_y = START_Y + draw_y * PIXEL_SIZE
color = get_color(temperature)
lcd.fillRect(
screen_x,
screen_y,
PIXEL_SIZE,
PIXEL_SIZE,
color
)
# =========================================================
# 最高温度、平均温度、警告、レベルバーを描画
# =========================================================
def draw_information(high_temp, average_temp):
# 前回表示した数値とステータスを背景色で消去する
lcd.fillRect(215, 77, 58, 24, 0x0F172A)
lcd.fillRect(215, 120, 58, 24, 0x0F172A)
lcd.fillRect(215, 158, 58, 20, 0x0F172A)
lcd.font(lcd.FONT_DejaVu18)
if high_temp >= ALERT_TEMP:
lcd.setTextColor(0xFF0000, 0x0F172A)
else:
lcd.setTextColor(0xF87171, 0x0F172A)
lcd.print("{:4.1f}".format(high_temp), 218, 78)
lcd.setTextColor(0x38BDF8, 0x0F172A)
lcd.print("{:4.1f}".format(average_temp), 218, 121)
lcd.font(lcd.FONT_Default)
if high_temp >= ALERT_TEMP:
lcd.setTextColor(0xFF0000, 0x0F172A)
lcd.print("ALERT", 218, 160)
else:
lcd.setTextColor(0x4ADE80, 0x0F172A)
lcd.print("NORMAL", 218, 160)
bar_x = 282
bar_y = 78
bar_w = 18
bar_h = 100
ratio = (
(high_temp - DISPLAY_MIN_TEMP)
/ (DISPLAY_MAX_TEMP - DISPLAY_MIN_TEMP)
)
ratio = max(0.0, min(1.0, ratio))
fill_height = int(ratio * bar_h)
lcd.fillRect(bar_x, bar_y, bar_w, bar_h, 0x1E293B)
if fill_height > 0:
bar_color = get_color(high_temp)
lcd.fillRect(
bar_x,
bar_y + bar_h - fill_height,
bar_w,
fill_height,
bar_color
)
lcd.drawRect(
bar_x - 1,
bar_y - 1,
bar_w + 2,
bar_h + 2,
0x64748B
)
# =========================================================
# Thermal2本体の動作設定
# =========================================================
try:
# 接続確認を兼ねてデバイスIDを取得する
device_id = thermal2_0.device_info(0x00)
# Thermal2本体のRGB LED機能を有効化する
thermal2_0.func_ctrl(0x02, True)
# センサーの更新速度を4Hzに設定する
thermal2_0.refresh_rate(3)
# ノイズフィルターを設定する
thermal2_0.noise_filter(4)
thermal2_0.temp_threshold(0x30, ALERT_TEMP)
thermal2_0.temp_alarm_led(0x35, 200, 0, 0)
thermal2_0.temp_alarm_ctrl(0x80, True)
lcd.fillRect(15, 34, 290, 16, 0x0F172A)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(0x4ADE80, 0x0F172A)
lcd.print("Sensor Ready ID:{}".format(device_id), 15, 34)
except Exception as initialization_error:
lcd.fillRect(15, 34, 290, 16, 0x0F172A)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(0xF87171, 0x0F172A)
lcd.print(
"Init:{}".format(str(initialization_error)[:25]),
15,
34
)
# =========================================================
# メインループ
# =========================================================
while True:
try:
# Thermal2に新しい温度データがあるか確認する
update_available = thermal2_0.data_refresh_ctrl()
if update_available:
# 現在更新されたサブページ番号を取得する
subpage = thermal2_0.subpage_info()
# 16×24画素分の温度バッファを取得する
raw_buffer = thermal2_0.temp_data_buffer()
# 生データを384個の摂氏温度へ変換する
temperatures = decode_buffer(raw_buffer)
if len(temperatures) == 384:
update_temperature_frame(subpage, temperatures)
draw_subpage(subpage)
high_temp = float(
thermal2_0.temp_measure(0x0C, False)
)
average_temp = float(
thermal2_0.temp_measure(0x02, False)
)
draw_information(high_temp, average_temp)
else:
lcd.fillRect(12, 207, 295, 14, 0x0F172A)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(0xF87171, 0x0F172A)
lcd.print(
"Buffer length:{}".format(
len(raw_buffer)
if raw_buffer is not None
else 0
),
12,
207
)
wait_ms(20)
except Exception as error:
lcd.fillRect(12, 207, 295, 14, 0x0F172A)
lcd.font(lcd.FONT_Default)
lcd.setTextColor(0xFF0000, 0x0F172A)
lcd.print(
"Error:{}".format(str(error)[:35]),
12,
207
)
print("Thermal2 error:")
print(error)
wait_ms(500)