libgpiod support and better docs

This commit is contained in:
1e99 2025-03-22 14:09:00 +01:00
parent 5c8cecdc92
commit 42f1200ff2
5 changed files with 195 additions and 6 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.notes

10
README.md Normal file
View file

@ -0,0 +1,10 @@
# WoLBodge
## Configuration
Configuration is done using environment variables.
- `WOLBODGE_ADDRESS`: The address that WoLBodge should listen on, defaults to `:3000`.
- `WOLBODGE_STORAGE_TYPE`: Storage type to pick, defaults to `ram`.
- `ram`: Sessions are stored in RAM.
- `WOLBODGE_DEVICE_TYPE`: Device type to pick, defaults to `test`-
- `test`: A dummy device, used for testing.
- `libgpiod`: Uses the libgpiod commands under the hood. The gpio chip is specified using `WOLBODGE_DEVICE_GPIOCHIP`, the power button pin using `WOLBODGE_DEVICE_POWER_BUTTON_PIN` and the power LED pin using `WOLBODGE_DEVICE_POWER_LED_PIN`.

View file

@ -1,5 +1,99 @@
package devices package devices
func NewRaspiGPIO() Device { import (
"fmt"
"os/exec"
"sync"
"time"
)
func NewLibgpiod(gpiochip string, powerButtonPin int, powerLEDPin int) Device {
device := &libgpiod{
gpiochip: gpiochip,
powerButtonPin: powerButtonPin,
powerLEDPin: powerLEDPin,
lock: sync.Mutex{},
}
return device
}
type libgpiod struct {
gpiochip string
powerButtonPin int
powerLEDPin int
lock sync.Mutex
}
func (d *libgpiod) Status() (bool, error) {
d.lock.Lock()
defer d.lock.Unlock()
status, err := d.read(d.powerLEDPin)
if err != nil {
return false, err
}
return status, nil
}
func (d *libgpiod) PushPowerButton() error {
d.lock.Lock()
defer d.lock.Unlock()
err := d.write(d.powerButtonPin, 1)
if err != nil {
return err
}
time.Sleep(2 * time.Second)
err = d.write(d.powerButtonPin, 0)
if err != nil {
return err
}
return nil return nil
} }
func (d *libgpiod) write(pin int, value int) error {
cmd := exec.Command(
"env", "sh", "-c",
fmt.Sprintf("gpioset %s %d=%d", d.gpiochip, pin, value),
)
outRaw, err := cmd.Output()
if err != nil {
return err
}
out := string(outRaw)
if out == "" {
return nil
}
return fmt.Errorf("unexpected command output: %s", err)
}
func (d *libgpiod) read(pin int) (bool, error) {
cmd := exec.Command(
"env", "sh", "-c",
fmt.Sprintf("gpioget --bias pull-down %s %d", d.gpiochip, pin),
)
outRaw, err := cmd.Output()
if err != nil {
return false, err
}
out := string(outRaw)
switch out {
case "0\n":
return false, nil
case "1\n":
return true, nil
default:
return false, fmt.Errorf("unexpected command output: %s", out)
}
}

77
env.go Normal file
View file

@ -0,0 +1,77 @@
package main
import (
"fmt"
"log"
"os"
"strconv"
"git.1e99.eu/1e99/wolbodge/devices"
"git.1e99.eu/1e99/wolbodge/sessions"
)
func NewStorage() (sessions.Storage, error) {
var storageType string
Env("WOLBODGE_STORAGE_TYPE", &storageType, "mem")
switch storageType {
case "mem", "memory":
return sessions.NewMemStorage(), nil
default:
return nil, fmt.Errorf("unknown storage type: %s", storageType)
}
}
func NewDevice() (devices.Device, error) {
var deviceType string
Env("WOLBODGE_DEVICE_TYPE", &deviceType, "test")
switch deviceType {
case "test":
return devices.NewTest(false), nil
case "libgpiod":
var gpiochip string
var powerButtonPin int
var powerLEDPin int
Env("WOLBODGE_DEVICE_GPIOCHIP", &gpiochip, "gpiochip0")
Env("WOLBODGE_DEVICE_POWER_BUTTON_PIN", &powerButtonPin, "27")
Env("WOLBODGE_DEVICE_POWER_LED_PIN", &powerLEDPin, "17")
return devices.NewLibgpiod(gpiochip, powerButtonPin, powerLEDPin), nil
default:
return nil, fmt.Errorf("unknown device type: %s", deviceType)
}
}
func Env(name string, out any, def string) {
raw := os.Getenv(name)
if raw == "" {
raw = def
log.Printf("No \"%s\" provided, defaulting to \"%s\".", name, def)
}
switch value := out.(type) {
case *int:
i, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
log.Printf("\"%s\" is not a number (\"%s\").", name, raw)
return
}
*value = int(i)
case *bool:
switch raw {
case "true", "TRUE", "1":
*value = true
case "false", "FALSE", "0":
*value = false
}
case *string:
*value = raw
}
}

17
main.go
View file

@ -7,9 +7,7 @@ import (
"net/http" "net/http"
"time" "time"
"git.1e99.eu/1e99/wolbodge/devices"
"git.1e99.eu/1e99/wolbodge/routes" "git.1e99.eu/1e99/wolbodge/routes"
"git.1e99.eu/1e99/wolbodge/sessions"
_ "embed" _ "embed"
) )
@ -18,7 +16,8 @@ import (
var embed string var embed string
func Run() error { func Run() error {
address := ":3000" var address string
Env("WOLBODGE_ADDRESS", &address, ":3000")
tmpl := template.New("") tmpl := template.New("")
_, err := tmpl.Parse(embed) _, err := tmpl.Parse(embed)
@ -26,8 +25,16 @@ func Run() error {
return fmt.Errorf("failed to parse template: %w", err) return fmt.Errorf("failed to parse template: %w", err)
} }
device := devices.NewTest(false) device, err := NewDevice()
storage := sessions.NewMemStorage() if err != nil {
return fmt.Errorf("failed to create new device: %w", err)
}
storage, err := NewStorage()
if err != nil {
return fmt.Errorf("failed to create new storage: %w", err)
}
logger := log.Default() logger := log.Default()
mux := http.NewServeMux() mux := http.NewServeMux()