把行空板戴在手腕上?开源全部9个功能!
本帖最后由 zoey不种土豆 于 2025-1-24 20:01 编辑WatchHIKER是一个基于行空板M10的“智能手表”,其外壳由3D打印制作,通过Mind+进行低代码可视化编程。除了可定制的UI界面,作者为该手表设计超过10个功能,包括计步、心率监测、路况查询、天气情况和空气质量查询、紧急联系人、一键录音、噪音检测等,甚至还能查看实时货币行情。
所需材料
硬件
[*]行空板M10
[*]Beetle ESP32-C3
[*]心率传感器
软件
[*]Mind+
[*]Autodesk Fusion
其他
[*]3D 打印机及打印材料
外壳设计和硬件组装
使用Fusion设计WatchHIKER的外壳,作者在背壳上预留了方便腕带穿过的空隙。打印完成后,将行空板M10以屏幕朝外的方向放入外壳内,接下来,连接电池到充电器模块,将组装完成的电池组安装在外壳里。完成以上步骤后,封闭外壳。最后给手表加上表带,作者选择的是“魔术贴”腕带。
功能与程序实现加载Python脚本
在行空板上打开文件共享并复制完整的Git文件夹。
这里有几段代码用来实现以下功能:
1.GUI(图形用户界面)自定义
使用 tkinter 库来创建GUI(图形用户界面),并设计了三种不同风格的时钟界面。
2.心率监测
要实现这个功能,我们还需要添加以下两个硬件:Beetle ESP32-C3和心率传感器。首先将Beetle ESP32 C3编程,让它通过I2C通信从心率传感器读取数据,并将其写入串行端口。这样,行空板就可以读取并绘制血氧饱和度和心率数据了。
输出心率与血氧饱和度的代码:
<blockquote><span style="white-space: normal;"><span style="white-space:pre"> </span>#include <Wire.h></span>
以下是串行终端响应:
现在,用程序来解码和填充数据:
接下来,在Mind+中运行该脚本
3.计步功能
行空板M10的板载三轴陀螺仪和加速度传感器,因此可以实现WatchHIKER的计步功能。
<blockquote><span style="white-space: normal;"><span style="white-space:pre"> </span>import tkinter as tk</span>
4.天气情况和空气质量查询
此脚本允许添加若干城市,并从开放的天气API中读取并显示当地的天气、温度、湿度和风力级别等。
该脚本可以通过HTTP请求从远程API获取数据,并在界面上显示所在城市的PM2.5、PM10和其他空气污染物的浓度。使用者还可以通过点击“Refresh Data(刷新数据)”按钮来更新信息。
5.紧急联系人
一键SOS呼叫,快速将所在位置发送给紧急联系人。
6.路况查询
路况检测器提供实时路况更新,帮助使用者选择最快的路线。
7.录音功能
#-*- coding: UTF-8 -*-
# MindPlus
# Python
from unihiker import Audio
import tkinter as tk
from tkinter import ttk
import time
import threading
from datetime import datetime
import os
class AudioRecorder:
def __init__(self):
self.root = tk.Tk()
self.root.title("Audio Recorder")
self.root.geometry("240x320")
self.root.configure(bg="#1e1e1e")
self.audio = Audio()
self.recording = False
self.elapsed_time = 0
self.recordings_dir = "recordings"
if not os.path.exists(self.recordings_dir):
os.makedirs(self.recordings_dir)
self.setup_ui()
def setup_ui(self):
# Main container frame
main_frame = tk.Frame(self.root, bg="#1e1e1e")
main_frame.pack(expand=True, fill="both")
# Toggle button for start/stop
self.toggle_btn = tk.Button(
main_frame,
text="START",
command=self.toggle_recording,
bg="#00ff00",
fg="#000000",
width=20,
height=2,
font=("Arial", 16, "bold")
)
self.toggle_btn.pack(pady=10)
# Status text
self.status_label = tk.Label(
main_frame,
text="Ready to Record",
font=("Arial", 16),
bg="#1e1e1e",
fg="#00ff00"
)
self.status_label.pack(pady=10)
# Recording indicator
self.canvas = tk.Canvas(
main_frame,
width=100,
height=100,
bg="#1e1e1e",
highlightthickness=0
)
self.canvas.pack(pady=10)
self.indicator = self.canvas.create_oval(
25, 25, 75, 75,
fill="#1e1e1e",
outline="#ff0000",
width=2
)
# Timer display
self.timer_label = tk.Label(
main_frame,
text="00:00",
font=("Arial", 24),
bg="#1e1e1e",
fg="#ffffff"
)
self.timer_label.pack(pady=10)
def update_timer(self):
while self.recording:
self.elapsed_time += 1
minutes = self.elapsed_time // 60
seconds = self.elapsed_time % 60
self.timer_label.config(text=f"{minutes:02d}:{seconds:02d}")
time.sleep(1)
def animate_indicator(self):
pulse_state = True
while self.recording:
self.canvas.itemconfig(
self.indicator,
fill="#ff0000" if pulse_state else "#1e1e1e"
)
pulse_state = not pulse_state
time.sleep(0.5)
def toggle_recording(self):
if not self.recording:
# Start Recording
self.recording = True
self.elapsed_time = 0
# Generate unique filename
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
self.current_file = os.path.join(self.recordings_dir, f"recording_{timestamp}.wav")
self.status_label.config(text="Recording...")
self.toggle_btn.config(
text="STOP",
bg="#ff0000",
fg="#ffffff"
)
# Start recording in separate thread
threading.Thread(target=self.audio.start_record, args=(self.current_file,), daemon=True).start()
# Start timer and animation
threading.Thread(target=self.update_timer, daemon=True).start()
threading.Thread(target=self.animate_indicator, daemon=True).start()
else:
# Stop Recording
self.recording = False
self.audio.stop_record()
self.status_label.config(text=f"Saved: {os.path.basename(self.current_file)}")
self.toggle_btn.config(
text="START",
bg="#00ff00",
fg="#000000"
)
self.canvas.itemconfig(self.indicator, fill="#1e1e1e")
def run(self):
self.root.mainloop()
if __name__ == "__main__":
app = AudioRecorder()
app.run()
使用者可以通过点击按钮开始或停止录音,录音文件会保存到指定的目录中。
8.噪音监测
#-*- coding: UTF-8 -*-
# MindPlus
# Python
from pinpong.extension.unihiker import *
from pinpong.board import Board,Pin
from unihiker import Audio
import tkinter as tk
from tkinter import ttk
import time
import threading
import math
class NoiseMonitor:
def __init__(self):
self.root = tk.Tk()
self.root.title("Noise Monitor")
self.root.geometry("240x320")
self.root.configure(bg="#1e1e1e")
self.audio = Audio()
self.setup_ui()
self.start_monitoring()
def setup_ui(self):
# Title
self.title_label = tk.Label(
self.root,
text="NOISE MONITOR",
font=("Arial", 16, "bold"),
bg="#1e1e1e",
fg="#00ff00"
)
self.title_label.pack(pady=10)
# Canvas for visualization
self.canvas = tk.Canvas(
self.root,
width=200,
height=150,
bg="#1e1e1e",
highlightthickness=0
)
self.canvas.pack(pady=10)
# Create bars with peak indicators
self.bars = []
self.peaks = []
self.peak_speeds = []
num_bars = 16
for i in range(num_bars):
# Create main bar
bar = self.canvas.create_rectangle(
i*12 + 5, 150,
i*12 + 13, 150,
fill="#00ff00"
)
self.bars.append(bar)
# Create peak indicator
peak = self.canvas.create_rectangle(
i*12 + 5, 150,
i*12 + 13, 148,
fill="#ffffff"
)
self.peaks.append(peak)
self.peak_speeds.append(0)
# Numerical display
self.level_label = tk.Label(
self.root,
text="0",
font=("Arial", 36, "bold"),
bg="#1e1e1e",
fg="#00ff00"
)
self.level_label.pack(pady=10)
# Status bar
self.status_bar = tk.Label(
self.root,
text="Monitoring...",
bg="#1e1e1e",
fg="#ffffff",
bd=1,
relief=tk.SUNKEN
)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
def update_visualization(self, level):
max_height = 150
peak_fall_speed = 0.5
for i, (bar, peak) in enumerate(zip(self.bars, self.peaks)):
# Calculate bar height with some randomness
variation = math.sin(time.time() * 10 + i) * 5
adjusted_level = max(0, min(100, level + variation))
# Calculate height and color
height = max_height - (adjusted_level * 1.2)
# Rainbow color effect
hue = (i / len(self.bars)) * 360
rgb = self.hsv_to_rgb(hue, 1, 1 if adjusted_level > 0 else 0.2)
color = f'#{rgb:02x}{rgb:02x}{rgb:02x}'
# Update bar
self.canvas.coords(bar, i*12 + 5, height, i*12 + 13, max_height)
self.canvas.itemconfig(bar, fill=color)
# Update peak
peak_y = float(self.canvas.coords(peak))
if height < peak_y:# New peak
self.canvas.coords(peak, i*12 + 5, height, i*12 + 13, height + 2)
self.peak_speeds = 0
else:# Peak falling
self.peak_speeds += peak_fall_speed
new_y = min(max_height, peak_y + self.peak_speeds)
self.canvas.coords(peak, i*12 + 5, new_y, i*12 + 13, new_y + 2)
# Update level display
self.level_label.config(text=str(int(level)))
# Update status with smooth color transition
if level > 80:
status = "Very Loud!"
color = "#ff0000"
elif level > 60:
status = "Loud"
color = "#ffff00"
else:
status = "Normal"
color = "#00ff00"
self.status_bar.config(text=status, fg=color)
def hsv_to_rgb(self, h, s, v):
h = float(h)
s = float(s)
v = float(v)
h60 = h / 60.0
h60f = math.floor(h60)
hi = int(h60f) % 6
f = h60 - h60f
p = v * (1 - s)
q = v * (1 - f * s)
t = v * (1 - (1 - f) * s)
r, g, b = 0, 0, 0
if hi == 0: r, g, b = v, t, p
elif hi == 1: r, g, b = q, v, p
elif hi == 2: r, g, b = p, v, t
elif hi == 3: r, g, b = p, q, v
elif hi == 4: r, g, b = t, p, v
elif hi == 5: r, g, b = v, p, q
return (
int(r * 255),
int(g * 255),
int(b * 255)
)
def monitor_audio(self):
while True:
try:
level = self.audio.sound_level()
self.root.after(0, self.update_visualization, level)
time.sleep(0.1)
except Exception as e:
print(f"Error: {e}")
time.sleep(1)
def start_monitoring(self):
threading.Thread(target=self.monitor_audio, daemon=True).start()
def run(self):
self.root.mainloop()
if __name__ == "__main__":
app = NoiseMonitor()
app.run()
监测周围噪音水平,并在分贝超过正常范围时进行提醒。
9.查看实时货币行情
数据的实时更新,对于货币交易的操盘者来说十分重要,而这款手表也能为你做到信息的实时更新。
其它功能
除了以上功能,你还可以开动你的想象力,为WatchHIKER增加更多功能,比如,连接Beetle ESP32 C6,用于监控并在手表上探测到的家里是否有物体移动情况......
项目文件
原文链接: https://community.dfrobot.com/makelog-315081.html
项目作者:pradeeplogu0
发表时间:2025.01.12
页:
[1]