zoey不种土豆 发表于 2025-1-24 20:00:00

把行空板戴在手腕上?开源全部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]
查看完整版本: 把行空板戴在手腕上?开源全部9个功能!