Initial Commit
This commit is contained in:
commit
f058b94f1f
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
out/
|
||||
test.exe
|
40
README.md
Normal file
40
README.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Go gPIo app
|
||||
|
||||
Binarys befinden sich unter https://nc.masilux.de/index.php/s/bEzP2msezHPzyee
|
||||
|
||||
Zum Kompilieren folgende Go-Abhängigkeiten installieren:
|
||||
- `go get github.com/gorilla/mux`
|
||||
- `go get github.com/stianeikeland/go-rpio`
|
||||
- `go get github.com/frickelblog/surgemq/service`
|
||||
- `go get github.com/frickelblog/message`
|
||||
|
||||
Kompilieren mit: `go build main.go -o gPIo.bin`
|
||||
Kompilieren unter Windows (Powesrhell) mit Zielsystem Linux/ARM: `$env:GOOS="linux"; $env:GOARCH="arm"; go build -o gPIo.arm`
|
||||
|
||||
|
||||
**Konfiguration**
|
||||
Die Konfiguration erfolgt in der Datei `config.json`, z.B:
|
||||
```
|
||||
{
|
||||
"debug": true,
|
||||
"enablefilesystem": true,
|
||||
"enablemqttserver": true,
|
||||
"enablemqttclient": true,
|
||||
"mqttserverhost": "127.0.0.1",
|
||||
"mqttservertcpport": "1883",
|
||||
"mqttserverwsport": "8080",
|
||||
"mqttclientaddress": "127.0.0.1",
|
||||
"mqttclientport": "1883",
|
||||
"mqttclientuser": "",
|
||||
"mqttclientpass": "",
|
||||
"pins": [
|
||||
{ "pin":17, "io":"in", "state":"0", "file":"", "mqtttopic":"/pin/17" },
|
||||
{ "pin":20, "io":"out", "state":"1", "file":"", "mqtttopic":"/pin/20" },
|
||||
{ "pin":21, "io":"in", "state":"0", "file":"", "mqtttopic":"/pin/21" },
|
||||
{ "pin":22, "io":"in", "state":"0", "file":"","mqtttopic":"/pin/22" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Anwendung**
|
||||
|
19
config.json
Normal file
19
config.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"debug": true,
|
||||
"enablefilesystem": true,
|
||||
"enablemqttserver": true,
|
||||
"enablemqttclient": true,
|
||||
"mqttserverhost": "127.0.0.1",
|
||||
"mqttservertcpport": "1883",
|
||||
"mqttserverwsport": "8080",
|
||||
"mqttclientaddress": "127.0.0.1",
|
||||
"mqttclientport": "1883",
|
||||
"mqttclientuser": "",
|
||||
"mqttclientpass": "",
|
||||
"pins": [
|
||||
{ "pin":17, "io":"in", "state":"0", "file":"", "mqtttopic":"/pin/17" },
|
||||
{ "pin":20, "io":"out", "state":"1", "file":"", "mqtttopic":"/pin/20" },
|
||||
{ "pin":21, "io":"in", "state":"0", "file":"", "mqtttopic":"/pin/21" },
|
||||
{ "pin":22, "io":"in", "state":"0", "file":"","mqtttopic":"/pin/22" }
|
||||
]
|
||||
}
|
BIN
gpio-numbers-pi2.png
Normal file
BIN
gpio-numbers-pi2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
409
main.go
Normal file
409
main.go
Normal file
@ -0,0 +1,409 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/frickelblog/message"
|
||||
"github.com/frickelblog/surgemq/service"
|
||||
"github.com/stianeikeland/go-rpio"
|
||||
)
|
||||
|
||||
var (
|
||||
config = readConfig("./config.json")
|
||||
MQTTClient service.Client
|
||||
srv *service.Server
|
||||
client *service.Client
|
||||
)
|
||||
|
||||
type Configuration struct {
|
||||
Debug bool `json:"debug"`
|
||||
EnableFileSystem bool `json:"enablefilesystem"`
|
||||
EnableMQTTServer bool `json:"enablemqttserver"`
|
||||
EnableMQTTClient bool `json:"enablemqttclient"`
|
||||
MQTTServerHost string `json:"mqttserverhost"`
|
||||
MQTTServerTCPPort string `json:"mqttservertcpport"`
|
||||
MQTTServerWSPort string `json:"mqttserverwsport"`
|
||||
MQTTClientAddress string `json:"mqttclientaddress"`
|
||||
MQTTClientPort string `json:"mqttclientport"`
|
||||
MQTTClientUser string `json:"mqttclientuser"`
|
||||
MQTTClientPass string `json:"mqttclientpass"`
|
||||
Pins []PinConfiguration `json:"pins"`
|
||||
}
|
||||
|
||||
type PinConfiguration struct {
|
||||
Pin int8 `json:"pin"`
|
||||
IO string `json:"io"`
|
||||
File string `json:"file"`
|
||||
MQTTTopic string `json:"mqtttopic"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
// our main function
|
||||
func main() {
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Create a new mqtt server
|
||||
if config.EnableMQTTServer {
|
||||
srv = &service.Server{
|
||||
KeepAlive: 300, // seconds
|
||||
ConnectTimeout: 2, // seconds
|
||||
SessionsProvider: "mem", // keeps sessions in memory
|
||||
Authenticator: "mockSuccess", // always succeed
|
||||
TopicsProvider: "mem", // keeps topic subscriptions in memory
|
||||
}
|
||||
|
||||
// Websocket // "tcp://127.0.0.1:1883" => TCP COnnection vom MQTT Server als WS-Backend
|
||||
addr := "tcp://" + config.MQTTServerHost + ":" + config.MQTTServerTCPPort
|
||||
AddWebsocketHandler("/mqtt", addr)
|
||||
go ListenAndServeWebsocket(":" + config.MQTTServerWSPort) // ":8081" => WS-Connection
|
||||
// MQTT-Server
|
||||
go srv.ListenAndServe(addr)
|
||||
}
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
for i, s := range config.Pins {
|
||||
fmt.Println(i, s)
|
||||
}
|
||||
|
||||
fmt.Println("--------")
|
||||
|
||||
if config.EnableMQTTClient {
|
||||
fmt.Println("Warte auf MQTT Client...")
|
||||
time.Sleep(1000 * time.Millisecond) // 2 Sek auf Server warten
|
||||
|
||||
MQTTClient = mqttConnect()
|
||||
go heartbeatProcess(&MQTTClient, 30)
|
||||
}
|
||||
/*
|
||||
icounter := 0
|
||||
for {
|
||||
icounter = icounter + 1
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
mqttPubMSG("/pin/17", string(icounter))
|
||||
}
|
||||
*/
|
||||
|
||||
err := rpio.Open()
|
||||
if err != nil {
|
||||
panic(fmt.Sprint("unable to open gpio", err.Error()))
|
||||
}
|
||||
|
||||
defer rpio.Close()
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Pin Configuration
|
||||
for _, pinConfig := range config.Pins {
|
||||
pin := rpio.Pin(pinConfig.Pin)
|
||||
|
||||
if pinConfig.IO == "in" {
|
||||
pin.Input()
|
||||
//pin.PullUp()
|
||||
//pin.PullDown()
|
||||
//pin.Detect(rpio.RiseEdge) // AnyEdge / FallEdge 1->0 / RiseEdge 0->1
|
||||
}
|
||||
|
||||
if pinConfig.IO == "out" {
|
||||
pin.Output()
|
||||
|
||||
// Default PinState aus config setzen
|
||||
if pinConfig.State == "1" {
|
||||
pin.High()
|
||||
|
||||
} else {
|
||||
pin.Low()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
mqttStreamOutput := ""
|
||||
|
||||
for {
|
||||
|
||||
for i, pinConfig := range config.Pins {
|
||||
//fmt.Println(i, pinConfig)
|
||||
|
||||
pin := rpio.Pin(pinConfig.Pin)
|
||||
|
||||
// Edge-Detection test
|
||||
//if pin.EdgeDetected() {
|
||||
// newState := stringState(pin.Read())
|
||||
// fmt.Println("Edge raised: " + newState)
|
||||
//}
|
||||
|
||||
if pinConfig.IO == "in" {
|
||||
|
||||
newState := stringState(pin.Read())
|
||||
|
||||
if newState != pinConfig.State {
|
||||
// Pinstate in Config speichern
|
||||
config.Pins[i].State = newState
|
||||
|
||||
// Pinstate in Datei schreiben
|
||||
if config.EnableFileSystem {
|
||||
writePinFileState(pinConfig.File, newState)
|
||||
}
|
||||
|
||||
// Pinstate in MQTT-Topic kippen
|
||||
if config.EnableMQTTClient {
|
||||
pubmsg := message.NewPublishMessage()
|
||||
pubmsg.SetTopic([]byte(pinConfig.MQTTTopic)) // /pin/17
|
||||
pubmsg.SetPayload([]byte(newState))
|
||||
MQTTClient.Publish(pubmsg, nil)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if pinConfig.IO == "out" {
|
||||
|
||||
// PinState aus Datei lesen
|
||||
if config.EnableFileSystem {
|
||||
if readPinFileState(pinConfig.File) {
|
||||
pin.High()
|
||||
|
||||
} else {
|
||||
pin.Low()
|
||||
}
|
||||
}
|
||||
|
||||
// Pinstate in MQTT-Topic kippen, falls er durch Datei geändert wurde
|
||||
newState := stringState(pin.Read())
|
||||
if newState != config.Pins[i].State {
|
||||
if config.EnableMQTTClient {
|
||||
pubmsg := message.NewPublishMessage()
|
||||
pubmsg.SetTopic([]byte(pinConfig.MQTTTopic)) // /pin/17
|
||||
pubmsg.SetPayload([]byte(newState))
|
||||
MQTTClient.Publish(pubmsg, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pinstate Ausgabe auf der Konsole
|
||||
res := pin.Read()
|
||||
fmt.Print(pinConfig.Pin)
|
||||
fmt.Print(": ")
|
||||
fmt.Println(res)
|
||||
|
||||
mqttStreamOutput = mqttStreamOutput + "" + strconv.Itoa(int(pinConfig.Pin)) + ": " + strconv.Itoa(int(res)) + "\r\n"
|
||||
}
|
||||
|
||||
if config.EnableMQTTClient {
|
||||
pubmsg := message.NewPublishMessage()
|
||||
pubmsg.SetTopic([]byte("/pins")) // /pin/17
|
||||
pubmsg.SetPayload([]byte(mqttStreamOutput))
|
||||
MQTTClient.Publish(pubmsg, nil)
|
||||
}
|
||||
mqttStreamOutput = ""
|
||||
|
||||
// 100ms warten
|
||||
time.Sleep(400 * time.Millisecond)
|
||||
cmd := exec.Command("clear") //Linux example, its tested
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Run()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Hilfsfunktionen
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
func fileExists(filename string) bool {
|
||||
info, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
func stringState(state rpio.State) string {
|
||||
if state == Low {
|
||||
return "0"
|
||||
}
|
||||
if state == High {
|
||||
return "1"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func readPinFileState(filename string) bool {
|
||||
if fileExists(filename) {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
text := strings.TrimSpace(string(content))
|
||||
if strings.EqualFold(text, "1") {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Standardwert: false
|
||||
return false
|
||||
}
|
||||
|
||||
func writePinFileState(filename string, content string) bool {
|
||||
if filename != "" {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
_, err = f.WriteString(content)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
f.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func readConfig(filename string) *Configuration {
|
||||
// initialize conf with default values.
|
||||
conf := &Configuration{}
|
||||
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return conf
|
||||
}
|
||||
if err = json.Unmarshal(b, conf); err != nil {
|
||||
return conf
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
// State of pin, High / Low
|
||||
const (
|
||||
Low rpio.State = iota
|
||||
High
|
||||
)
|
||||
|
||||
func mqttConnect() service.Client {
|
||||
// Instantiates a new Client
|
||||
client = &service.Client{}
|
||||
|
||||
// Creates a new MQTT CONNECT message and sets the proper parameters
|
||||
msg := message.NewConnectMessage()
|
||||
msg.SetWillQos(0)
|
||||
msg.SetVersion(4)
|
||||
msg.SetCleanSession(true)
|
||||
msg.SetClientId([]byte("gPIo"))
|
||||
msg.SetKeepAlive(20)
|
||||
//msg.SetWillTopic([]byte("will"))
|
||||
//msg.SetWillMessage([]byte("send me home"))
|
||||
//msg.SetUsername([]byte("gPIo"))
|
||||
//msg.SetPassword([]byte(""))
|
||||
|
||||
// Connects to the remote server at 127.0.0.1 port 1883
|
||||
//c.Connect("tcp://127.0.0.1:1883", msg)
|
||||
//c.Connect("tcp://test.mosquitto.org:1883", msg)
|
||||
if err := client.Connect("tcp://"+config.MQTTClientAddress+":"+config.MQTTClientPort, msg); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
//time.Sleep(5000 * time.Millisecond)
|
||||
|
||||
pubmsg := message.NewPublishMessage()
|
||||
pubmsg.SetTopic([]byte("/topic"))
|
||||
pubmsg.SetPayload([]byte("msg"))
|
||||
pubmsg.SetQoS(0)
|
||||
|
||||
// Publishes to the server by sending the message
|
||||
err := client.Publish(pubmsg, nil)
|
||||
if err != nil {
|
||||
fmt.Print("Fehler: ")
|
||||
}
|
||||
|
||||
// Subscribe MQTTTopics
|
||||
submsg := message.NewSubscribeMessage()
|
||||
|
||||
for _, pincfg := range config.Pins {
|
||||
if pincfg.MQTTTopic != "" {
|
||||
fmt.Println(" - Subscribe MQTT-Topic: " + pincfg.MQTTTopic)
|
||||
submsg.AddTopic([]byte(pincfg.MQTTTopic), 0)
|
||||
}
|
||||
}
|
||||
|
||||
client.Subscribe(submsg, nil, mqttOnPublishFunc)
|
||||
|
||||
return *client
|
||||
}
|
||||
|
||||
func mqttOnPublishFunc(msg *message.PublishMessage) error {
|
||||
topic := string(msg.Topic())
|
||||
payload := string(msg.Payload())
|
||||
|
||||
fmt.Println(topic)
|
||||
fmt.Println(payload)
|
||||
|
||||
for i, pinConfig := range config.Pins {
|
||||
if pinConfig.MQTTTopic == topic {
|
||||
pin := rpio.Pin(pinConfig.Pin)
|
||||
if payload == "1" {
|
||||
pin.High()
|
||||
} else if payload == "0" {
|
||||
pin.Low()
|
||||
}
|
||||
|
||||
// Pinstate in Config speichern
|
||||
newState := stringState(pin.Read())
|
||||
config.Pins[i].State = newState
|
||||
|
||||
// Pinstate in Datei schreiben
|
||||
if config.EnableFileSystem {
|
||||
writePinFileState(pinConfig.File, newState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mqttPubMSG(topic string, msg string) {
|
||||
// Creates a new PUBLISH message with the appropriate contents for publishing
|
||||
pubmsg := message.NewPublishMessage()
|
||||
pubmsg.SetTopic([]byte(topic))
|
||||
pubmsg.SetPayload([]byte(msg))
|
||||
pubmsg.SetQoS(0)
|
||||
|
||||
// Publishes to the server by sending the message
|
||||
err := MQTTClient.Publish(pubmsg, nil)
|
||||
if err != nil {
|
||||
fmt.Print("Fehler: ")
|
||||
fmt.Println(err)
|
||||
//MQTTClient.Unsubscribe()
|
||||
MQTTClient.Disconnect()
|
||||
MQTTClient = mqttConnect()
|
||||
MQTTClient.Publish(pubmsg, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func heartbeatProcess(c *service.Client, KeepAlive int) {
|
||||
for _ = range time.Tick(time.Duration(KeepAlive) * time.Second) {
|
||||
//fmt.Println("Ping OK")
|
||||
err := c.Ping(nil)
|
||||
if err != nil {
|
||||
fmt.Print("Fehler: ")
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
102
websocket.go
Normal file
102
websocket.go
Normal file
@ -0,0 +1,102 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/surge/glog"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
func DefaultListenAndServeWebsocket() error {
|
||||
if err := AddWebsocketHandler("/mqtt", "test.mosquitto.org:1883"); err != nil {
|
||||
return err
|
||||
}
|
||||
return ListenAndServeWebsocket(":1234")
|
||||
}
|
||||
|
||||
func AddWebsocketHandler(urlPattern string, uri string) error {
|
||||
glog.Debugf("AddWebsocketHandler urlPattern=%s, uri=%s", urlPattern, uri)
|
||||
u, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
glog.Errorf("surgemq/main: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
h := func(ws *websocket.Conn) {
|
||||
WebsocketTcpProxy(ws, u.Scheme, u.Host)
|
||||
}
|
||||
http.Handle(urlPattern, websocket.Handler(h))
|
||||
return nil
|
||||
}
|
||||
|
||||
/* start a listener that proxies websocket <-> tcp */
|
||||
func ListenAndServeWebsocket(addr string) error {
|
||||
return http.ListenAndServe(addr, nil)
|
||||
}
|
||||
|
||||
/* starts an HTTPS listener */
|
||||
func ListenAndServeWebsocketSecure(addr string, cert string, key string) error {
|
||||
return http.ListenAndServeTLS(addr, cert, key, nil)
|
||||
}
|
||||
|
||||
/* copy from websocket to writer, this copies the binary frames as is */
|
||||
func io_copy_ws(src *websocket.Conn, dst io.Writer) (int, error) {
|
||||
var buffer []byte
|
||||
count := 0
|
||||
for {
|
||||
err := websocket.Message.Receive(src, &buffer)
|
||||
if err != nil {
|
||||
return count, err
|
||||
}
|
||||
n := len(buffer)
|
||||
count += n
|
||||
i, err := dst.Write(buffer)
|
||||
if err != nil || i < 1 {
|
||||
return count, err
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
/* copy from reader to websocket, this copies the binary frames as is */
|
||||
func io_ws_copy(src io.Reader, dst *websocket.Conn) (int, error) {
|
||||
buffer := make([]byte, 2048)
|
||||
count := 0
|
||||
for {
|
||||
n, err := src.Read(buffer)
|
||||
if err != nil || n < 1 {
|
||||
return count, err
|
||||
}
|
||||
count += n
|
||||
err = websocket.Message.Send(dst, buffer[0:n])
|
||||
if err != nil {
|
||||
return count, err
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
/* handler that proxies websocket <-> unix domain socket */
|
||||
func WebsocketTcpProxy(ws *websocket.Conn, nettype string, host string) error {
|
||||
client, err := net.Dial(nettype, host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
defer ws.Close()
|
||||
chDone := make(chan bool)
|
||||
|
||||
go func() {
|
||||
io_ws_copy(client, ws)
|
||||
chDone <- true
|
||||
}()
|
||||
go func() {
|
||||
io_copy_ws(ws, client)
|
||||
chDone <- true
|
||||
}()
|
||||
<-chDone
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user