diff --git a/cmd/iocheckout/main.go b/cmd/iocheckout/main.go index d1f4b5e8..1589ee2f 100644 --- a/cmd/iocheckout/main.go +++ b/cmd/iocheckout/main.go @@ -9,7 +9,7 @@ import ( "github.com/macformula/hil/iocontrol" "github.com/macformula/hil/iocontrol/raspi" - "github.com/macformula/hil/iocontrol/sil" + // "github.com/macformula/hil/iocontrol/sil" "github.com/macformula/hil/iocontrol/speedgoat" "github.com/macformula/hil/macformula" "github.com/macformula/hil/macformula/pinout" @@ -62,14 +62,14 @@ func main() { } if *useRaspi { - rp := raspi.NewController() + rp := raspi.NewController(logger) ioControlOpts = append(ioControlOpts, iocontrol.WithRaspi(rp)) } - if *useSil { - s := sil.NewController(_silPort, logger) - ioControlOpts = append(ioControlOpts, iocontrol.WithSil(s)) - } + // if *useSil { + // s := sil.NewController(_silPort, logger) + // ioControlOpts = append(ioControlOpts, iocontrol.WithSil(s)) + // } ioControl := iocontrol.NewIOControl(logger, ioControlOpts...) diff --git a/cmd/raspitest/.gitignore b/cmd/raspitest/.gitignore new file mode 100644 index 00000000..45f36a7e --- /dev/null +++ b/cmd/raspitest/.gitignore @@ -0,0 +1 @@ +raspitest diff --git a/cmd/raspitest/main.go b/cmd/raspitest/main.go new file mode 100644 index 00000000..3154ff25 --- /dev/null +++ b/cmd/raspitest/main.go @@ -0,0 +1,137 @@ +package main + +import ( + "bufio" + "context" + "flag" + "fmt" + "log" + "os" + "strings" + + "go.uber.org/zap" + + "github.com/macformula/hil/iocontrol/raspi" +) + +const helpText = "commands: high | low | read | help | exit" + +func main() { + boardPin := flag.Uint("pin", 0, "Raspberry Pi board pin number (1-40)") + // one-shot mode (optional) + oneSet := flag.String("set", "", "one-shot: set pin: high|low") + oneGet := flag.Bool("get", false, "one-shot: read pin") + flag.Parse() + + if *boardPin == 0 || *boardPin > 40 { + log.Fatalf("pin is required and must be between 1 and 40: --pin N (board numbering)") + } + + logger, err := zap.NewDevelopment() + if err != nil { + log.Fatalf("init logger: %v", err) + } + defer logger.Sync() + + ctrl := raspi.NewController(logger) + if err := ctrl.Open(context.Background()); err != nil { + log.Fatalf("open raspi controller: %v", err) + } + defer func() { + if err := ctrl.Close(); err != nil { + logger.Warn("failed to close raspi controller", zap.Error(err)) + } + }() + + pin := raspi.NewDigitalPin(uint8(*boardPin)) + + // One-shot path + if *oneSet != "" || *oneGet { + switch { + case *oneSet != "": + switch strings.ToLower(*oneSet) { + case "high", "1", "on": + if err := ctrl.SetDigital(pin, true); err != nil { + log.Fatalf("set high: %v", err) + } + fmt.Printf("P1-%d -> HIGH\n", *boardPin) + case "low", "0", "off": + if err := ctrl.SetDigital(pin, false); err != nil { + log.Fatalf("set low: %v", err) + } + fmt.Printf("P1-%d -> LOW\n", *boardPin) + default: + log.Fatalf("unknown --set value: %q (use high|low)", *oneSet) + } + case *oneGet: + level, err := ctrl.ReadDigital(pin) + if err != nil { + log.Fatalf("read: %v", err) + } + if level { + fmt.Printf("P1-%d <- HIGH\n", *boardPin) + } else { + fmt.Printf("P1-%d <- LOW\n", *boardPin) + } + } + return + } + + // Interactive path: read from the controlling TTY + tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) + if err != nil { + log.Fatalf("open /dev/tty: %v", err) + } + defer tty.Close() + + fmt.Printf("raspitest ready on pin P1-%d\n%s\n", *boardPin, helpText) + + reader := bufio.NewScanner(tty) + for { + fmt.Fprintf(tty, "P1-%d> ", *boardPin) + if !reader.Scan() { + break + } + cmd := strings.ToLower(strings.TrimSpace(reader.Text())) + if cmd == "" { + continue + } + + switch cmd { + case "high", "1", "on": + if err := ctrl.SetDigital(pin, true); err != nil { + fmt.Fprintf(tty, "error setting HIGH: %v\n", err) + continue + } + fmt.Fprintln(tty, "-> HIGH") + case "low", "0", "off": + if err := ctrl.SetDigital(pin, false); err != nil { + fmt.Fprintf(tty, "error setting LOW: %v\n", err) + continue + } + fmt.Fprintln(tty, "-> LOW") + case "read", "get": + level, err := ctrl.ReadDigital(pin) + if err != nil { + fmt.Fprintf(tty, "error reading pin: %v\n", err) + continue + } + if level { + fmt.Fprintln(tty, "<- HIGH") + } else { + fmt.Fprintln(tty, "<- LOW") + } + case "help", "?": + fmt.Fprintln(tty, helpText) + case "exit", "quit": + fmt.Fprintln(tty, "bye") + return + default: + fmt.Fprintf(tty, "unknown command %q (%s)\n", cmd, helpText) + } + } + + if err := reader.Err(); err != nil { + log.Fatalf("tty read error: %v", err) + } +} diff --git a/go.mod b/go.mod index 42bfaeb8..3e6af879 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/macformula/hil -go 1.22.0 +go 1.22.6 require ( github.com/cenkalti/backoff v2.2.1+incompatible @@ -21,11 +21,7 @@ require ( google.golang.org/grpc v1.60.1 google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 -) - -require ( - github.com/dmarkham/enumer v1.6.1 // indirect - github.com/pascaldekloe/name v1.0.0 // indirect + periph.io/x/conn/v3 v3.7.2 ) require ( @@ -58,4 +54,5 @@ require ( golang.org/x/text v0.22.0 // indirect golang.org/x/tools v0.30.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect + periph.io/x/host/v3 v3.8.5 ) diff --git a/go.sum b/go.sum index b36aaeb8..c4621a37 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,6 @@ github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2 github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dmarkham/enumer v1.6.1 h1:aSc9awYtZL07TUueWs40QcHtxTvHTAwG0EqrNsK45w4= -github.com/dmarkham/enumer v1.6.1/go.mod h1:yixql+kDDQRYqcuBM2n9Vlt7NoT9ixgXhaXry8vmRg8= github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= @@ -36,13 +34,14 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -67,8 +66,6 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/pascaldekloe/name v1.0.0 h1:n7LKFgHixETzxpRv2R77YgPUFo85QHGZKrdaYm7eY5U= -github.com/pascaldekloe/name v1.0.0/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -94,21 +91,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -120,26 +111,18 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -160,3 +143,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +periph.io/x/conn/v3 v3.7.2 h1:qt9dE6XGP5ljbFnCKRJ9OOCoiOyBGlw7JZgoi72zZ1s= +periph.io/x/conn/v3 v3.7.2/go.mod h1:Ao0b4sFRo4QOx6c1tROJU1fLJN1hUIYggjOrkIVnpGg= +periph.io/x/host/v3 v3.8.5 h1:g4g5xE1XZtDiGl1UAJaUur1aT7uNiFLMkyMEiZ7IHII= +periph.io/x/host/v3 v3.8.5/go.mod h1:hPq8dISZIc+UNfWoRj+bPH3XEBQqJPdFdx218W92mdc= diff --git a/iocontrol/raspi/analog.go b/iocontrol/raspi/analog.go index 4d9c3847..ceb488bd 100644 --- a/iocontrol/raspi/analog.go +++ b/iocontrol/raspi/analog.go @@ -1,10 +1,12 @@ package raspi // AnalogPin defines an analog pin for the Raspberry Pi -type AnalogPin struct{} +type AnalogPin struct{ + id uint8 +} -func NewAnalogPin() *AnalogPin { - return &AnalogPin{} +func NewAnalogPin(idx uint8) *AnalogPin { + return &AnalogPin{id: idx} } // String returns the pin type diff --git a/iocontrol/raspi/controller.go b/iocontrol/raspi/controller.go index c59602f5..d25893b5 100644 --- a/iocontrol/raspi/controller.go +++ b/iocontrol/raspi/controller.go @@ -1,51 +1,134 @@ package raspi -import "context" +// ### NOTE ### +// This driver currently requires permissions (sudo) to access the GPIO pins. Running HIL stack on root is not recommended. +// Need to add udev rule to allow gpio group to access /dev/gpiomem and related device nodes without sudo. +// Once the Ansible script is updated to install the udev rule, permissions will no longer be required. + +import ( + "context" + "fmt" + "sync" + + "github.com/pkg/errors" + "go.uber.org/zap" + "periph.io/x/conn/v3/gpio" + "periph.io/x/conn/v3/gpio/gpioreg" + "periph.io/x/host/v3" +) + +const ( + _loggerName = "raspi_controller" +) + +// Mapping from board pins (1-40) to their corresponding BCM GPIO numbers +// CAN HAT uses 8, 10, 3, 5, 15, 19, 21, 23, 24, 26, 27, 28, 33 (https://www.waveshare.com/wiki/2-CH_CAN_HAT) +// Remaining pins are listed below (see https://pinout.xyz/pinout/pin7_gpio4/) +var boardToBCM = map[uint8]int{ + 7: 4, 11: 17, 12: 18, 13: 27, 16: 23, 18: 24, 22: 25, + 29: 5, 31: 6, 32: 12, 35: 19, 36: 16, 37: 26, 38: 20, 40: 21, +} // Controller provides control for various Raspberry Pi pins type Controller struct { + l *zap.Logger + mu sync.Mutex + opened bool } // NewController returns a new Raspberry Pi controller -func NewController() *Controller { - return &Controller{} +func NewController(l *zap.Logger) *Controller { + return &Controller{ + l: l.Named(_loggerName), + } } // Open configures the controller func (c *Controller) Open(_ context.Context) error { + c.mu.Lock() + defer c.mu.Unlock() + if c.opened { + return errors.New("raspberry pi controller already opened") + } + + // initialize periph io + if _, err := host.Init(); err != nil { + return errors.Wrap(err, "periph host init") + } + + c.opened = true return nil } -// SetDigital sets an output digital pin for a Raspberry Pi digital pin -func (c *Controller) SetDigital(output *DigitalPin, b bool) error { +// Close tears down the controller instance +func (c *Controller) Close() error { + c.l.Info("closing raspberry pi controller") + c.mu.Lock() + defer c.mu.Unlock() + c.opened = false return nil } +// SetDigital sets an output digital pin for a Raspberry Pi digital pin +func (c *Controller) SetDigital(output *DigitalPin, level bool) error { + pin, err := resolvePin(output) + if err != nil { + return err + } + + // configure as output, then write + if err := pin.Out(gpio.Low); err != nil { + return errors.Wrap(err, "set output mode") + } + if level { + return errors.Wrap(pin.Out(gpio.High), "write high") + } + return errors.Wrap(pin.Out(gpio.Low), "write low") +} + // ReadDigital returns the level of a Raspberry Pi digital pin -func (c *Controller) ReadDigital(output *DigitalPin) (bool, error) { - return false, nil +func (c *Controller) ReadDigital(input *DigitalPin) (bool, error) { + pin, err := resolvePin(input) + if err != nil { + return false, err + } + + if err := pin.In(gpio.Float, gpio.NoEdge); err != nil { + return false, errors.Wrap(err, "set input mode") + } + return pin.Read() == gpio.High, nil } // WriteVoltage sets the voltage of a Raspberry Pi analog pin func (c *Controller) WriteVoltage(output *AnalogPin, voltage float64) error { - return nil + return errors.New("currently unsupported on raspi") } // ReadVoltage returns the voltage of a Raspberry Pi analog pin func (c *Controller) ReadVoltage(output *AnalogPin) (float64, error) { - return 0.00, nil + return 0.00, errors.New("currently unsupported on raspi") } // WriteCurrent sets the current of a Raspberry Pi analog pin func (c *Controller) WriteCurrent(output *AnalogPin, current float64) error { - return nil + return errors.New("currently unsupported on raspi") } // ReadCurrent returns the current of a Raspberry Pi analog pin func (c *Controller) ReadCurrent(output *AnalogPin) (float64, error) { - return 0.00, nil + return 0.00, errors.New("currently unsupported on raspi") } -func (c *Controller) Close() error { - return nil +// helpers +func resolvePin(pin *DigitalPin) (gpio.PinIO, error) { + bcm, ok := boardToBCM[pin.id] + if !ok { + return nil, errors.Errorf("no BCM mapping for board pin %d", pin.id) + } + + if p := gpioreg.ByName(fmt.Sprintf("GPIO%d", bcm)); p != nil { + return p, nil + } + + return nil, errors.Errorf("GPIO for board pin %d unavailable", pin.id) } diff --git a/iocontrol/raspi/digital.go b/iocontrol/raspi/digital.go index 60431455..93aaf727 100644 --- a/iocontrol/raspi/digital.go +++ b/iocontrol/raspi/digital.go @@ -1,11 +1,13 @@ package raspi // DigitalPin defines a digital pin for the Raspberry Pi +// Works with physical board pin numbering (1-40) type DigitalPin struct { + id uint8 } -func NewDigitalPin() *DigitalPin { - return &DigitalPin{} +func NewDigitalPin(idx uint8) *DigitalPin { + return &DigitalPin{id: idx} } // String returns the pin type diff --git a/macformula/pinout/pinout.go b/macformula/pinout/pinout.go index 85c869ba..1197cad4 100644 --- a/macformula/pinout/pinout.go +++ b/macformula/pinout/pinout.go @@ -2,7 +2,7 @@ package pinout import ( "github.com/macformula/hil/iocontrol" - "github.com/macformula/hil/iocontrol/raspi" + // "github.com/macformula/hil/iocontrol/raspi" "github.com/macformula/hil/iocontrol/sil" "github.com/macformula/hil/iocontrol/speedgoat" "github.com/pkg/errors" @@ -54,7 +54,7 @@ var _revisionDigitalOutputPinout = map[Revision]DigitalPinout{ HvilDisable: speedgoat.NewDigitalPin(10), }, MockTest: { - StartButtonN: raspi.NewDigitalPin(), + // StartButtonN: raspi.NewDigitalPin(), }, Sil: { IndicatorButton: sil.NewDigitalOutputPin("DemoProject", IndicatorButton.String()), @@ -76,7 +76,7 @@ var _revisionAnalogInputPinout = map[Revision]AnalogPinout{ FrontController3v3RefVoltage: speedgoat.NewAnalogPin(2), }, MockTest: { - LvController3v3RefVoltage: raspi.NewAnalogPin(), + // LvController3v3RefVoltage: raspi.NewAnalogPin(), }, Sil: { HvilFeedback: sil.NewAnalogInputPin("FrontController", HvilFeedback.String()), @@ -92,9 +92,9 @@ var _revisionAnalogOutputPinout = map[Revision]AnalogPinout{ AccelPedalPosition2: speedgoat.NewAnalogPin(11), }, MockTest: { - AccelPedalPosition1: raspi.NewAnalogPin(), - AccelPedalPosition2: raspi.NewAnalogPin(), - HvCurrentSense: raspi.NewAnalogPin(), + // AccelPedalPosition1: raspi.NewAnalogPin(), + // AccelPedalPosition2: raspi.NewAnalogPin(), + // HvCurrentSense: raspi.NewAnalogPin(), }, Sil: { AccelPedalPosition1: sil.NewAnalogOutputPin("FrontController", AccelPedalPosition1.String()), diff --git a/raspitest b/raspitest new file mode 100755 index 00000000..75718e3d Binary files /dev/null and b/raspitest differ diff --git a/results/tag.go b/results/tag.go index 640a065e..6599a7c9 100644 --- a/results/tag.go +++ b/results/tag.go @@ -12,7 +12,7 @@ type Tag struct { CompOp ComparisonOperator UpperLimit any `yaml:"upperLimit,omitempty"` LowerLimit any `yaml:"lowerLimit,omitempty"` - ExpectedValue any `yaml:"expectedVal,omitempty"` + ExpectedValue any `yaml:"expectedValue,omitempty"` Unit string `yaml:"unit"` }