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 }