1401浏览
查看: 1401|回复: 0

[M10项目] 把行空板戴在手腕上?开源全部9个功能!

[复制链接]
本帖最后由 zoey不种土豆 于 2025-1-24 20:01 编辑

把行空板戴在手腕上?开源全部9个功能!图17

WatchHIKER是一个基于行空板M10的“智能手表”,其外壳由3D打印制作,通过Mind+进行低代码可视化编程。除了可定制的UI界面,作者为该手表设计超过10个功能,包括计步、心率监测、路况查询、天气情况和空气质量查询、紧急联系人、一键录音、噪音检测等,甚至还能查看实时货币行情。

所需材料
硬件


软件

其他
  • 3D 打印机及打印材料

外壳设计和硬件组装
使用Fusion设计WatchHIKER的外壳,作者在背壳上预留了方便腕带穿过的空隙。打印完成后,将行空板M10以屏幕朝外的方向放入外壳内,接下来,连接电池到充电器模块,将组装完成的电池组安装在外壳里。完成以上步骤后,封闭外壳。最后给手表加上表带,作者选择的是“魔术贴”腕带。
把行空板戴在手腕上?开源全部9个功能!图12

功能与程序实现
加载Python脚本
在行空板上打开文件共享并复制完整的Git文件夹。
把行空板戴在手腕上?开源全部9个功能!图1

这里有几段代码用来实现以下功能:

1.GUI(图形用户界面)自定义
使用 tkinter 库来创建GUI(图形用户界面),并设计了三种不同风格的时钟界面。

把行空板戴在手腕上?开源全部9个功能!图2

把行空板戴在手腕上?开源全部9个功能!图3

把行空板戴在手腕上?开源全部9个功能!图4

2.心率监测
把行空板戴在手腕上?开源全部9个功能!图16

要实现这个功能,我们还需要添加以下两个硬件:Beetle ESP32-C3和心率传感器。
首先将Beetle ESP32 C3编程,让它通过I2C通信从心率传感器读取数据,并将其写入串行端口。这样,行空板就可以读取并绘制血氧饱和度和心率数据了。
把行空板戴在手腕上?开源全部9个功能!图5

输出心率与血氧饱和度的代码:
  1. <blockquote><span style="white-space: normal;"><span style="white-space:pre">        </span>#include <Wire.h></span>
复制代码

以下是串行终端响应:
把行空板戴在手腕上?开源全部9个功能!图13

现在,用程序来解码和填充数据:

把行空板戴在手腕上?开源全部9个功能!图6

接下来,在Mind+中运行该脚本
把行空板戴在手腕上?开源全部9个功能!图14

3.计步功能
行空板M10的板载三轴陀螺仪和加速度传感器,因此可以实现WatchHIKER的计步功能。
  1. <blockquote><span style="white-space: normal;"><span style="white-space:pre">        </span>import tkinter as tk</span>
复制代码

4.天气情况和空气质量查询

把行空板戴在手腕上?开源全部9个功能!图7

此脚本允许添加若干城市,并从开放的天气API中读取并显示当地的天气、温度、湿度和风力级别等。

把行空板戴在手腕上?开源全部9个功能!图8

该脚本可以通过HTTP请求从远程API获取数据,并在界面上显示所在城市的PM2.5、PM10和其他空气污染物的浓度。使用者还可以通过点击“Refresh Data(刷新数据)”按钮来更新信息。

5.紧急联系人

把行空板戴在手腕上?开源全部9个功能!图9

一键SOS呼叫,快速将所在位置发送给紧急联系人。

6.路况查询
把行空板戴在手腕上?开源全部9个功能!图10

路况检测器提供实时路况更新,帮助使用者选择最快的路线。

7.录音功能
  1. #  -*- coding: UTF-8 -*-
  2. # MindPlus
  3. # Python
  4. from unihiker import Audio
  5. import tkinter as tk
  6. from tkinter import ttk
  7. import time
  8. import threading
  9. from datetime import datetime
  10. import os
  11. class AudioRecorder:
  12.     def __init__(self):
  13.         self.root = tk.Tk()
  14.         self.root.title("Audio Recorder")
  15.         self.root.geometry("240x320")
  16.         self.root.configure(bg="#1e1e1e")
  17.         
  18.         self.audio = Audio()
  19.         self.recording = False
  20.         self.elapsed_time = 0
  21.         self.recordings_dir = "recordings"
  22.         if not os.path.exists(self.recordings_dir):
  23.             os.makedirs(self.recordings_dir)
  24.         self.setup_ui()
  25.         
  26.     def setup_ui(self):
  27.         # Main container frame
  28.         main_frame = tk.Frame(self.root, bg="#1e1e1e")
  29.         main_frame.pack(expand=True, fill="both")
  30.         
  31.         # Toggle button for start/stop
  32.         self.toggle_btn = tk.Button(
  33.             main_frame,
  34.             text="START",
  35.             command=self.toggle_recording,
  36.             bg="#00ff00",
  37.             fg="#000000",
  38.             width=20,
  39.             height=2,
  40.             font=("Arial", 16, "bold")
  41.         )
  42.         self.toggle_btn.pack(pady=10)
  43.         
  44.         # Status text
  45.         self.status_label = tk.Label(
  46.             main_frame,
  47.             text="Ready to Record",
  48.             font=("Arial", 16),
  49.             bg="#1e1e1e",
  50.             fg="#00ff00"
  51.         )
  52.         self.status_label.pack(pady=10)
  53.         
  54.         # Recording indicator
  55.         self.canvas = tk.Canvas(
  56.             main_frame,
  57.             width=100,
  58.             height=100,
  59.             bg="#1e1e1e",
  60.             highlightthickness=0
  61.         )
  62.         self.canvas.pack(pady=10)
  63.         
  64.         self.indicator = self.canvas.create_oval(
  65.             25, 25, 75, 75,
  66.             fill="#1e1e1e",
  67.             outline="#ff0000",
  68.             width=2
  69.         )
  70.         
  71.         # Timer display
  72.         self.timer_label = tk.Label(
  73.             main_frame,
  74.             text="00:00",
  75.             font=("Arial", 24),
  76.             bg="#1e1e1e",
  77.             fg="#ffffff"
  78.         )
  79.         self.timer_label.pack(pady=10)
  80.    
  81.     def update_timer(self):
  82.         while self.recording:
  83.             self.elapsed_time += 1
  84.             minutes = self.elapsed_time // 60
  85.             seconds = self.elapsed_time % 60
  86.             self.timer_label.config(text=f"{minutes:02d}:{seconds:02d}")
  87.             time.sleep(1)
  88.    
  89.     def animate_indicator(self):
  90.         pulse_state = True
  91.         while self.recording:
  92.             self.canvas.itemconfig(
  93.                 self.indicator,
  94.                 fill="#ff0000" if pulse_state else "#1e1e1e"
  95.             )
  96.             pulse_state = not pulse_state
  97.             time.sleep(0.5)
  98.    
  99.     def toggle_recording(self):
  100.         if not self.recording:
  101.             # Start Recording
  102.             self.recording = True
  103.             self.elapsed_time = 0
  104.             
  105.             # Generate unique filename
  106.             timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  107.             self.current_file = os.path.join(self.recordings_dir, f"recording_{timestamp}.wav")
  108.             
  109.             self.status_label.config(text="Recording...")
  110.             self.toggle_btn.config(
  111.                 text="STOP",
  112.                 bg="#ff0000",
  113.                 fg="#ffffff"
  114.             )
  115.             
  116.             # Start recording in separate thread
  117.             threading.Thread(target=self.audio.start_record, args=(self.current_file,), daemon=True).start()
  118.             
  119.             # Start timer and animation
  120.             threading.Thread(target=self.update_timer, daemon=True).start()
  121.             threading.Thread(target=self.animate_indicator, daemon=True).start()
  122.         else:
  123.             # Stop Recording
  124.             self.recording = False
  125.             self.audio.stop_record()
  126.             self.status_label.config(text=f"Saved: {os.path.basename(self.current_file)}")
  127.             self.toggle_btn.config(
  128.                 text="START",
  129.                 bg="#00ff00",
  130.                 fg="#000000"
  131.             )
  132.             self.canvas.itemconfig(self.indicator, fill="#1e1e1e")
  133.    
  134.     def run(self):
  135.         self.root.mainloop()
  136. if __name__ == "__main__":
  137.     app = AudioRecorder()
  138.     app.run()
复制代码

使用者可以通过点击按钮开始或停止录音,录音文件会保存到指定的目录中。

8.噪音监测
  1. #  -*- coding: UTF-8 -*-
  2. # MindPlus
  3. # Python
  4. from pinpong.extension.unihiker import *
  5. from pinpong.board import Board,Pin
  6. from unihiker import Audio
  7. import tkinter as tk
  8. from tkinter import ttk
  9. import time
  10. import threading
  11. import math
  12. class NoiseMonitor:
  13.     def __init__(self):
  14.         self.root = tk.Tk()
  15.         self.root.title("Noise Monitor")
  16.         self.root.geometry("240x320")
  17.         self.root.configure(bg="#1e1e1e")
  18.         
  19.         self.audio = Audio()
  20.         self.setup_ui()
  21.         self.start_monitoring()
  22.    
  23.     def setup_ui(self):
  24.         # Title
  25.         self.title_label = tk.Label(
  26.             self.root,
  27.             text="NOISE MONITOR",
  28.             font=("Arial", 16, "bold"),
  29.             bg="#1e1e1e",
  30.             fg="#00ff00"
  31.         )
  32.         self.title_label.pack(pady=10)
  33.         
  34.         # Canvas for visualization
  35.         self.canvas = tk.Canvas(
  36.             self.root,
  37.             width=200,
  38.             height=150,
  39.             bg="#1e1e1e",
  40.             highlightthickness=0
  41.         )
  42.         self.canvas.pack(pady=10)
  43.         
  44.         # Create bars with peak indicators
  45.         self.bars = []
  46.         self.peaks = []
  47.         self.peak_speeds = []
  48.         num_bars = 16
  49.         
  50.         for i in range(num_bars):
  51.             # Create main bar
  52.             bar = self.canvas.create_rectangle(
  53.                 i*12 + 5, 150,
  54.                 i*12 + 13, 150,
  55.                 fill="#00ff00"
  56.             )
  57.             self.bars.append(bar)
  58.             
  59.             # Create peak indicator
  60.             peak = self.canvas.create_rectangle(
  61.                 i*12 + 5, 150,
  62.                 i*12 + 13, 148,
  63.                 fill="#ffffff"
  64.             )
  65.             self.peaks.append(peak)
  66.             self.peak_speeds.append(0)
  67.         
  68.         # Numerical display
  69.         self.level_label = tk.Label(
  70.             self.root,
  71.             text="0",
  72.             font=("Arial", 36, "bold"),
  73.             bg="#1e1e1e",
  74.             fg="#00ff00"
  75.         )
  76.         self.level_label.pack(pady=10)
  77.         
  78.         # Status bar
  79.         self.status_bar = tk.Label(
  80.             self.root,
  81.             text="Monitoring...",
  82.             bg="#1e1e1e",
  83.             fg="#ffffff",
  84.             bd=1,
  85.             relief=tk.SUNKEN
  86.         )
  87.         self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
  88.    
  89.     def update_visualization(self, level):
  90.         max_height = 150
  91.         peak_fall_speed = 0.5
  92.         
  93.         for i, (bar, peak) in enumerate(zip(self.bars, self.peaks)):
  94.             # Calculate bar height with some randomness
  95.             variation = math.sin(time.time() * 10 + i) * 5
  96.             adjusted_level = max(0, min(100, level + variation))
  97.             
  98.             # Calculate height and color
  99.             height = max_height - (adjusted_level * 1.2)
  100.             
  101.             # Rainbow color effect
  102.             hue = (i / len(self.bars)) * 360
  103.             rgb = self.hsv_to_rgb(hue, 1, 1 if adjusted_level > 0 else 0.2)
  104.             color = f'#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}'
  105.             
  106.             # Update bar
  107.             self.canvas.coords(bar, i*12 + 5, height, i*12 + 13, max_height)
  108.             self.canvas.itemconfig(bar, fill=color)
  109.             
  110.             # Update peak
  111.             peak_y = float(self.canvas.coords(peak)[1])
  112.             if height < peak_y:  # New peak
  113.                 self.canvas.coords(peak, i*12 + 5, height, i*12 + 13, height + 2)
  114.                 self.peak_speeds[i] = 0
  115.             else:  # Peak falling
  116.                 self.peak_speeds[i] += peak_fall_speed
  117.                 new_y = min(max_height, peak_y + self.peak_speeds[i])
  118.                 self.canvas.coords(peak, i*12 + 5, new_y, i*12 + 13, new_y + 2)
  119.         
  120.         # Update level display
  121.         self.level_label.config(text=str(int(level)))
  122.         
  123.         # Update status with smooth color transition
  124.         if level > 80:
  125.             status = "Very Loud!"
  126.             color = "#ff0000"
  127.         elif level > 60:
  128.             status = "Loud"
  129.             color = "#ffff00"
  130.         else:
  131.             status = "Normal"
  132.             color = "#00ff00"
  133.         
  134.         self.status_bar.config(text=status, fg=color)
  135.    
  136.     def hsv_to_rgb(self, h, s, v):
  137.         h = float(h)
  138.         s = float(s)
  139.         v = float(v)
  140.         h60 = h / 60.0
  141.         h60f = math.floor(h60)
  142.         hi = int(h60f) % 6
  143.         f = h60 - h60f
  144.         p = v * (1 - s)
  145.         q = v * (1 - f * s)
  146.         t = v * (1 - (1 - f) * s)
  147.         r, g, b = 0, 0, 0
  148.         if hi == 0: r, g, b = v, t, p
  149.         elif hi == 1: r, g, b = q, v, p
  150.         elif hi == 2: r, g, b = p, v, t
  151.         elif hi == 3: r, g, b = p, q, v
  152.         elif hi == 4: r, g, b = t, p, v
  153.         elif hi == 5: r, g, b = v, p, q
  154.         return (
  155.             int(r * 255),
  156.             int(g * 255),
  157.             int(b * 255)
  158.         )
  159.    
  160.     def monitor_audio(self):
  161.         while True:
  162.             try:
  163.                 level = self.audio.sound_level()
  164.                 self.root.after(0, self.update_visualization, level)
  165.                 time.sleep(0.1)
  166.             except Exception as e:
  167.                 print(f"Error: {e}")
  168.                 time.sleep(1)
  169.    
  170.     def start_monitoring(self):
  171.         threading.Thread(target=self.monitor_audio, daemon=True).start()
  172.    
  173.     def run(self):
  174.         self.root.mainloop()
  175. if __name__ == "__main__":
  176.     app = NoiseMonitor()
  177.     app.run()
复制代码

监测周围噪音水平,并在分贝超过正常范围时进行提醒。

9.查看实时货币行情

把行空板戴在手腕上?开源全部9个功能!图11

数据的实时更新,对于货币交易的操盘者来说十分重要,而这款手表也能为你做到信息的实时更新。

其它功能
除了以上功能,你还可以开动你的想象力,为WatchHIKER增加更多功能,比如,连接Beetle ESP32 C6,用于监控并在手表上探测到的家里是否有物体移动情况......

项目文件 下载附件watchhiker.rar

原文链接: https://community.dfrobot.com/makelog-315081.html
项目作者:pradeeplogu0
发表时间:2025.01.12

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail