FireLeave_tool/gpio_monitor/gpio_monitor.go
2026-07-03 20:55:15 +08:00

225 lines
7.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package gpio_monitor
import (
"context"
"fmt"
"os"
"strconv"
"time"
"FireLeave_tool/logger"
)
const (
// ButtonGPIO GPIO引脚编号
ButtonGPIO = 111
// DebounceTime 去抖动时间
DebounceTime = 200 * time.Millisecond
)
// GPIOButtonMonitor GPIO按钮监视器
type GPIOButtonMonitor struct {
gpioPin int
isRunning bool
lastState bool
lastChange time.Time
lastAlertTime time.Time
cooldownTime time.Duration
cooldownActive bool
onPress func()
}
// NewGPIOButtonMonitor 创建GPIO按钮监视器
func NewGPIOButtonMonitor(gpioPin int) *GPIOButtonMonitor {
return &GPIOButtonMonitor{
gpioPin: gpioPin,
isRunning: false,
lastState: false,
lastChange: time.Now(),
lastAlertTime: time.Now().Add(-6 * time.Second), // 初始化为6秒前确保第一次可以触发
cooldownTime: 5 * time.Second, // 5秒冷却时间
cooldownActive: false, // 冷却期状态
}
}
func (m *GPIOButtonMonitor) SetOnPress(handler func()) {
m.onPress = handler
}
// InitGPIO 初始化GPIO
func (m *GPIOButtonMonitor) InitGPIO() error {
gpioPath := fmt.Sprintf("/sys/class/gpio/gpio%d", m.gpioPin)
// 检查GPIO是否已导出
if _, err := os.Stat(gpioPath); os.IsNotExist(err) {
// 导出GPIO
if err := os.WriteFile("/sys/class/gpio/export", []byte(strconv.Itoa(m.gpioPin)), 0666); err != nil {
return fmt.Errorf("failed to export GPIO %d: %v", m.gpioPin, err)
}
// 等待导出完成
time.Sleep(50 * time.Millisecond)
}
// 检查并设置方向为输入
directionPath := fmt.Sprintf("%s/direction", gpioPath)
directionBytes, err := os.ReadFile(directionPath)
if err != nil {
return fmt.Errorf("failed to read GPIO direction: %v", err)
}
direction := string(directionBytes)
if direction != "in\n" && direction != "in" {
if err := os.WriteFile(directionPath, []byte("in"), 0666); err != nil {
return fmt.Errorf("failed to set GPIO direction to in: %v", err)
}
// 等待设置完成
time.Sleep(10 * time.Millisecond)
}
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d initialized successfully as input", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d initialized successfully as input", m.gpioPin)
}
return nil
}
// ReadGPIOValue 读取GPIO值
func (m *GPIOButtonMonitor) ReadGPIOValue() (bool, error) {
valuePath := fmt.Sprintf("/sys/class/gpio/gpio%d/value", m.gpioPin)
valueBytes, err := os.ReadFile(valuePath)
if err != nil {
return false, fmt.Errorf("failed to read GPIO value: %v", err)
}
value := string(valueBytes)
return value == "1\n" || value == "1", nil
}
// Start 启动GPIO监控
func (m *GPIOButtonMonitor) Start(ctx context.Context) error {
// 初始化GPIO
if err := m.InitGPIO(); err != nil {
return fmt.Errorf("failed to initialize GPIO: %v", err)
}
m.isRunning = true
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("Started monitoring GPIO %d for button alerts", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("Started monitoring GPIO %d for button alerts", m.gpioPin)
}
go func() {
defer func() {
m.isRunning = false
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("Stopped monitoring GPIO %d", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("Stopped monitoring GPIO %d", m.gpioPin)
}
}()
for {
select {
case <-ctx.Done():
return
default:
// 读取GPIO值
currentState, err := m.ReadGPIOValue()
if err != nil {
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("Error reading GPIO %d value: %v", m.gpioPin, err)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("Error reading GPIO %d value: %v", m.gpioPin, err)
}
time.Sleep(100 * time.Millisecond)
continue
}
// 检查状态是否变化
if currentState != m.lastState {
// 记录状态变化
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d state changed: %t → %t", m.gpioPin, m.lastState, currentState)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d state changed: %t → %t", m.gpioPin, m.lastState, currentState)
}
// 状态变化时更新lastChange
m.lastChange = time.Now()
// 立即更新lastState避免重复触发状态变化日志
m.lastState = currentState
// 只输出true状态
if currentState {
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d current state: %t", m.gpioPin, currentState)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d current state: %t", m.gpioPin, currentState)
}
}
}
// 检查冷却期状态
if time.Since(m.lastAlertTime) < m.cooldownTime {
if !m.cooldownActive {
m.cooldownActive = true
// 只记录一次冷却期开始
}
} else {
if m.cooldownActive {
// 只记录一次冷却期结束
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d cooldown period ended", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d cooldown period ended", m.gpioPin)
}
m.cooldownActive = false
}
}
// 检查是否稳定且当前状态为true高电平
if currentState && time.Since(m.lastChange) > DebounceTime {
// 检查冷却期
if time.Since(m.lastAlertTime) >= m.cooldownTime {
// 检测到稳定的高电平,切换事件周期
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d detected stable high level after debounce, triggering fire_leave pause...", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d detected stable high level after debounce, triggering fire_leave pause...", m.gpioPin)
}
// 切换事件周期
if m.onPress != nil {
m.onPress()
}
// 更新lastAlertTime开始冷却期
m.lastAlertTime = time.Now()
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d alert sent, cooldown period started (5s)", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d alert sent, cooldown period started (5s)", m.gpioPin)
}
// 重置lastChange避免重复触发
m.lastChange = time.Now()
}
}
// 增加休眠时间减少CPU占用和日志频率
time.Sleep(100 * time.Millisecond)
}
}
}()
return nil
}
// IsRunning 检查监视器是否正在运行
func (m *GPIOButtonMonitor) IsRunning() bool {
return m.isRunning
}