はじめに
Picoで自作しているGPSロガーですが、動作状況がわからないので、心拍数やGPS情報を低消費電力のOLEDディスプレイで表示しました。
前提
の続きでございます。
配線とケース収納
よくワンコイン前後で売られている「SSD1306」のOLEDディスプレイを接続します。私の場合、GPSでUART0(GP0,GP1)を使っていたので、I2C0が使えずI2C1(GP18,GP19)に接続しています。
パッケージ追加とプログラム(main.py)
PicoをPCに接続して、ThonnyからOLEDディスプレイ用のパッケージをインストールします。
メニューバーから[ツール]-[パッケージを管理...]をクリックします。
「ssd1306」で検索して、一番上の「micropython-ssd1306」です。
CSVファイルにログを書き込むタイミングでOLEDの表示も更新するようにしました。表示する項目は以下です。
- 心拍数
- 時間(GPS)
- 経度緯度(GPS)
- 高度(GPS)
表示の更新は、衛星を掴んでから5秒間隔です。
from pico import l76x
from pico.micropyGPS.micropyGPS import MicropyGPS
from machine import Pin, PWM
import time
import ssd1306
# OLEDの初期設定
sda = machine.Pin(18)
scl = machine.Pin(19)
i2c = machine.I2C(1,sda=sda, scl=scl, freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# GPSの初期設定
gnss_l76b=l76x.L76X(uartx=0,_baudrate = 9600)
gnss_l76b.l76x_exit_backup_mode()
gnss_l76b.l76x_send_command(gnss_l76b.SET_SYNC_PPS_NMEA_ON)
parser = MicropyGPS(location_formatting='dd')
sec = 0
tpfile = open("gpslog.csv","a")
# 心拍センサー(デジタル出力)とブザーの設定
hrm_pin = Pin(26, Pin.IN, Pin.PULL_DOWN)
buzzer = PWM(Pin(14))
heart_rates = []
last_heartbeat_time = 0
def add_hours_to_time(hh, mm, ss, add_hours):
hh += add_hours
if hh >= 24:
hh -= 24
return hh, mm, ss
def beep(slp):
buzzer.freq(3000) # ブザーの周波数を設定
buzzer.duty_u16(32768) # ブザーをオン(半分のデューティサイクル)
time.sleep(slp) # ブザーを鳴らす時間
buzzer.duty_u16(0) # ブザーをオフ
def filter_heart_rates(heart_rate, window_size=3):
if len(heart_rates) < window_size:
return True
avg = sum(heart_rates[-window_size:]) / window_size
return abs(heart_rate - avg) < 20 # 許容する最大の逸脱値
def on_heartbeat(pin):
global last_heartbeat_time, heart_rates
current_time = time.ticks_ms()
if last_heartbeat_time > 0:
interval = time.ticks_diff(current_time, last_heartbeat_time)
heart_rate = 60000 // interval
if 50 <= heart_rate <= 200: # 心拍数50~200が有効データ範囲
if filter_heart_rates(heart_rate):
heart_rates.append(heart_rate)
print("Heart Rate:", heart_rate)
# 心拍数に基づいてビープ音の長さを変更
if heart_rate > 160:
beep(0.3)
elif heart_rate > 120:
beep(0.1)
else:
print("Heart Rate [spike!]:", heart_rate)
last_heartbeat_time = current_time
# 心拍センサーの割り込み設定
hrm_pin.irq(trigger=Pin.IRQ_RISING, handler=on_heartbeat)
prev_hr = 0
# メインループ
while True:
if gnss_l76b.uart_any():
try:
sentence = parser.update(chr(gnss_l76b.uart_receive_byte()[0]))
except:
pass
if sentence and parser.satellites_in_use > 0 and sec != parser.timestamp[2] and (parser.timestamp[2] % 5) == 0:
datetime = f"20{parser.date[2]:02}-{parser.date[1]:02}-{parser.date[0]:02}T{parser.timestamp[0]:02}:{parser.timestamp[1]:02}:{parser.timestamp[2]:02}Z"
lat = f"{parser.latitude[0]:.9f}"
lon = f"{parser.longitude[0]:.9f}"
alt = int(parser.altitude)
avg_heart_rate = sum(heart_rates) // len(heart_rates) if heart_rates else 0
if avg_heart_rate == 0:
avg_heart_rate = prev_hr # 心拍数を計測していない場合は前回値を出力
else:
prev_hr = avg_heart_rate
data = f"{datetime},{lat},{lon},{alt},{parser.geoid_height},{parser.fix_stat},{parser.hdop},{parser.satellites_in_use},{avg_heart_rate}\n"
print(data)
sec = parser.timestamp[2]
tpfile.write(data)
tpfile.flush()
heart_rates.clear() # 心拍数リストをクリア
hour, minute, second = add_hours_to_time(parser.timestamp[0], parser.timestamp[1], parser.timestamp[2], 9)
oled.fill(0)
oled.rect(0, 0, 127, 63, 1)
oled.text(f"H:{avg_heart_rate:3} {hour:02}:{minute:02}:{second:02}",2,5)
oled.text(f"Geopoint:",2,20)
oled.text(f" {parser.latitude[0]:.3f},{parser.longitude[0]:.3f}",0,30)
oled.text(f"Altitude:",2,43)
oled.text(f" {alt:4}m",0,53)
oled.show()
使ってみた
いつもトレーニングで登っている吾妻山公園へ。
二宮、小田原、真鶴まで途切れて続く街灯りが好きです。
OLEDは、ばっちり綺麗に表示しています。
何気に時間と高度が分かるのは嬉しいかも。
おわりに
まだ本体の制作費に10K円もかかってないと思います(バッテリーは別です)。ディスプレイをもっと大きくしてサイクルコンピュータ?みたいなのも作れそうな気がしますね。
普通なら「ガーミン買って終わり」で既製品もカッコいいのですが、データを自由に流用できる自作デバイスも良いですね!安く作れると満足感もあります。
Pico Wがどこまでできるか調べてませんが、スマホアプリにデータ転送ができるように作れるならPico Wで作ってみたいですね。