本帖最后由 zoey不种土豆 于 2025-1-24 20:01 编辑
WatchHIKER是一个基于行空板M10的“智能手表”,其外壳由3D打印制作,通过Mind+进行低代码可视化编程。除了可定制的UI界面,作者为该手表设计超过10个功能,包括计步、心率监测、路况查询、天气情况和空气质量查询、紧急联系人、一键录音、噪音检测等,甚至还能查看实时货币行情。
所需材料
硬件
软件
其他
外壳设计和硬件组装
使用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[0]:02x}{rgb[1]:02x}{rgb[2]: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)[1])
- if height < peak_y: # New peak
- self.canvas.coords(peak, i*12 + 5, height, i*12 + 13, height + 2)
- self.peak_speeds[i] = 0
- else: # Peak falling
- self.peak_speeds[i] += peak_fall_speed
- new_y = min(max_height, peak_y + self.peak_speeds[i])
- 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,用于监控并在手表上探测到的家里是否有物体移动情况......
项目文件 watchhiker.rar
原文链接: https://community.dfrobot.com/makelog-315081.html
项目作者:pradeeplogu0
发表时间:2025.01.12
|