添加gpio控制

This commit is contained in:
gqc 2025-12-03 14:13:11 +08:00
parent 18205b02df
commit d91e6e41e1
7 changed files with 223 additions and 18 deletions

1
.idea/vcs.xml generated
View File

@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/rk1808-yolov5-inference" vcs="Git" />
</component> </component>
</project> </project>

View File

@ -18,7 +18,7 @@
"task_id": "6589", "task_id": "6589",
"address": "房间南侧", "address": "房间南侧",
"type": "1", "type": "1",
"facility": "02" "facility": "02"
}, },
{ {
"host_uuid": "qbxmjyzrkpntfgswaevodhluicqzxplkm", "host_uuid": "qbxmjyzrkpntfgswaevodhluicqzxplkm",

47
business/gpio_ctrl.go Normal file
View File

@ -0,0 +1,47 @@
// gpio_controller.go
package business
import (
"fmt"
"os"
"strconv"
"time"
)
const BuzzerGPIO = 109
// TriggerBuzzer 触发蜂鸣器响指定秒数
func TriggerBuzzer(seconds int) {
if seconds <= 0 {
seconds = 10
}
// 必须用同步!异步 goroutine 在某些系统里会被静默丢弃
fmt.Printf("蜂鸣器开始响铃 %d 秒...\n", seconds)
gpioPath := fmt.Sprintf("/sys/class/gpio/gpio%d", BuzzerGPIO)
// 1. 强制导出(即使已经导出也没事)
os.WriteFile("/sys/class/gpio/export", []byte(strconv.Itoa(BuzzerGPIO)), 0666)
time.Sleep(50 * time.Millisecond)
// 2. 强制设为输出
os.WriteFile(fmt.Sprintf("%s/direction", gpioPath), []byte("out"), 0666)
time.Sleep(10 * time.Millisecond)
// 3. 拉高 → 响
if err := os.WriteFile(fmt.Sprintf("%s/value", gpioPath), []byte("1"), 0666); err != nil {
fmt.Printf("GPIO拉高失败: %v\n", err)
} else {
fmt.Println("GPIO109 已拉高 → 蜂鸣器响!")
}
time.Sleep(time.Duration(seconds) * time.Second)
// 4. 拉低 → 停
if err := os.WriteFile(fmt.Sprintf("%s/value", gpioPath), []byte("0"), 0666); err != nil {
fmt.Printf("GPIO拉低失败: %v\n", err)
} else {
fmt.Println("GPIO109 已拉低 → 蜂鸣器停止")
}
}

View File

@ -17,6 +17,17 @@ var (
wsURL = "ws://172.17.0.1:18080/ws" wsURL = "ws://172.17.0.1:18080/ws"
) )
type AlertMessage struct {
UUID string `json:"uuid"`
Topic string `json:"topic"`
QoS int `json:"qos"`
Content string `json:"content"`
Timeout int `json:"timeout"`
}
type AlertContent struct {
Type int `json:"type"` // 1=蜂鸣器告警
BuzzerDuration int `json:"buzzer_duration"` // 响多久(秒)
}
type WSMessage struct { type WSMessage struct {
ID int64 `json:"id,omitempty"` ID int64 `json:"id,omitempty"`
Method string `json:"method"` Method string `json:"method"`
@ -46,8 +57,9 @@ type MetricDataParams struct {
} }
type PublishParams struct { type PublishParams struct {
Topic string `json:"topic"` Topic string `json:"topic"`
Data json.RawMessage `json:"data,omitempty"` Data json.RawMessage `json:"data,omitempty"`
Content json.RawMessage `json:"content,omitempty"`
} }
type WSClient struct { type WSClient struct {
@ -65,15 +77,6 @@ type WSClient struct {
reconnectFlag bool reconnectFlag bool
} }
// ==================== Public API ====================
//// GetWSClient returns the global WebSocket client instance
//func GetWSClient() *WSClient {
// wsMutex.RLock()
// defer wsMutex.RUnlock()
// return wsClient
//}
// InitWSChannel initializes WebSocket connection // InitWSChannel initializes WebSocket connection
func InitWSChannel() error { func InitWSChannel() error {
wsMutex.Lock() wsMutex.Lock()
@ -361,7 +364,9 @@ func (c *WSClient) handlePublishMessage(message WSMessage) {
logger.Logger.Printf("Failed to parse publish message parameters: %v", err) logger.Logger.Printf("Failed to parse publish message parameters: %v", err)
return return
} }
// 加这一行!万能抓包,永不漏!
logger.Logger.Printf("WebSocket 收到 publish 消息 → Topic: %s | 原始数据: %s",
publishParams.Topic, string(publishParams.Data))
c.mutex.Lock() c.mutex.Lock()
callback, exists := c.subscribers[publishParams.Topic] callback, exists := c.subscribers[publishParams.Topic]
c.mutex.Unlock() c.mutex.Unlock()
@ -458,11 +463,11 @@ func (c *WSClient) autoReRegister() {
} }
c.mutex.Unlock() c.mutex.Unlock()
for topic, callback := range subscribers { //for topic, callback := range subscribers {
if err := SubscribeTopic(topic, callback); err != nil { // if err := SubscribeTopic(topic, callback); err != nil {
logger.Logger.Printf("Failed to re-subscribe topic %s: %v", topic, err) // logger.Logger.Printf("Failed to re-subscribe topic %s: %v", topic, err)
} // }
} //}
} }
} }

BIN
dthjjc

Binary file not shown.

68
main.go
View File

@ -9,6 +9,7 @@ import (
"log" "log"
"os" "os"
"os/signal" "os/signal"
"strconv"
"syscall" "syscall"
"time" "time"
) )
@ -126,6 +127,13 @@ func runNormalMode() {
if err := connect.SubscribeTopic("device_updates", handleDeviceUpdates); err != nil { if err := connect.SubscribeTopic("device_updates", handleDeviceUpdates); err != nil {
log.Fatal("Topic subscription failed:", err) log.Fatal("Topic subscription failed:", err)
} }
logger.Logger.Printf("Subscribing to alert topic (/dthjjc/alert) for buzzer...")
if err := connect.SubscribeTopic("/dthjjc/alert", handleAlertMessage); err != nil {
logger.Logger.Printf("Failed to subscribe alert topic: %v (continue anyway)", err)
} else {
logger.Logger.Printf("Successfully subscribed to /dthjjc/alert → buzzer ready!")
}
// 8. 启动数据采集循环 // 8. 启动数据采集循环
logger.Logger.Printf("Starting data collection loop...") logger.Logger.Printf("Starting data collection loop...")
@ -171,3 +179,63 @@ func startDataCollectionLoop(businessManager *business.BusinessManager) {
} }
} }
func handleAlertMessage(params *connect.PublishParams) {
payload := params.Content
if len(payload) == 0 {
payload = params.Data
}
if len(payload) == 0 {
logger.Logger.Printf("平台告警空包 → 强制响 3 秒")
business.TriggerBuzzer(3)
return
}
raw := string(payload)
logger.Logger.Printf("收到告警原始内容 → %s", raw)
type Cmd struct {
Type int `json:"type"`
BuzzerDuration json.RawMessage `json:"buzzer_duration"` // 万能类型!
}
var cmd Cmd
var outer string
if json.Unmarshal(payload, &outer) == nil {
logger.Logger.Printf("检测到外层是字符串 → %s", outer)
raw = outer
}
if err := json.Unmarshal([]byte(raw), &cmd); err != nil {
logger.Logger.Printf("最终解析失败: %v → 暴力识别", err)
} else if cmd.Type == 1 {
dur := 3
var temp interface{}
if json.Unmarshal(cmd.BuzzerDuration, &temp) == nil {
switch v := temp.(type) {
case string:
if n, err := strconv.Atoi(v); err == nil {
dur = n
}
case float64:
dur = int(v)
case int:
dur = v
}
}
if dur <= 0 {
dur = 3
}
logger.Logger.Printf("平台告警解析成功 → 蜂鸣器响 %d 秒!!!", dur)
business.TriggerBuzzer(dur)
return
}
// 终极兜底:只要包含 type 和 1 就响
//if strings.Contains(raw, "type") && strings.Contains(raw, "1") {
// logger.Logger.Printf("暴力识别告警指令 → 强制响 3 秒")
// business.TriggerBuzzer(3)
//}
}

84
连接线路.drawio Normal file
View File

@ -0,0 +1,84 @@
<mxfile host="Electron" modified="2025-11-26T09:09:40.277Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.0.4 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="c7_EBDN9ol-w24zU6wdL" version="24.0.4" type="device">
<diagram name="第 1 页" id="tfFKj4gfuwo1-KzXBk_g">
<mxGraphModel dx="1309" dy="999" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="CCi69vAXkhbXQBJSxABA-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="CCi69vAXkhbXQBJSxABA-1" target="CCi69vAXkhbXQBJSxABA-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="CCi69vAXkhbXQBJSxABA-1" target="CCi69vAXkhbXQBJSxABA-9" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;startArrow=classic;startFill=1;" parent="1" source="CCi69vAXkhbXQBJSxABA-1" target="CCi69vAXkhbXQBJSxABA-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="CCi69vAXkhbXQBJSxABA-1" target="CCi69vAXkhbXQBJSxABA-7" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="CCi69vAXkhbXQBJSxABA-1" target="CCi69vAXkhbXQBJSxABA-14" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-1" value="N2000" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="700" y="360" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-24" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;" parent="1" source="CCi69vAXkhbXQBJSxABA-4" target="CCi69vAXkhbXQBJSxABA-14" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;" parent="1" source="CCi69vAXkhbXQBJSxABA-4" target="CCi69vAXkhbXQBJSxABA-7" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-4" value="220V供电" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="730" y="280" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;" parent="1" source="CCi69vAXkhbXQBJSxABA-5" target="CCi69vAXkhbXQBJSxABA-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;" parent="1" source="CCi69vAXkhbXQBJSxABA-5" target="CCi69vAXkhbXQBJSxABA-9" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;" parent="1" source="CCi69vAXkhbXQBJSxABA-5" target="CCi69vAXkhbXQBJSxABA-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=1;entryDx=0;entryDy=0;dashed=1;" parent="1" source="CCi69vAXkhbXQBJSxABA-5" target="CCi69vAXkhbXQBJSxABA-1" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="980" y="430" />
<mxPoint x="820" y="430" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-5" value="12v" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="950" y="465" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-7" value="安卓&lt;div&gt;显示屏&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="890" y="370" width="60" height="40" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-8" value="温湿度&lt;br&gt;传感器" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="660" y="470" width="60" height="40" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-9" value="水浸&lt;div&gt;传感器&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="730" y="470" width="60" height="40" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-10" value="&lt;div&gt;烟雾&lt;/div&gt;&lt;div&gt;传感器&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="800" y="470" width="60" height="40" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-14" value="摄像头" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="580" y="370" width="60" height="40" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-15" value="485" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="760" y="418" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-17" value="HTTP" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="825" y="368" width="50" height="30" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-19" value="RTSP" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="640" y="368" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="CCi69vAXkhbXQBJSxABA-27" value="电表" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="730" y="310" width="60" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>