New weekend project time! 🎉
Here's a cloud-native app written in golang which makes a Raspberry Pi's GPIO pins accessible from a HTTP API and works great in Kubernetes or Docker.

What's the details?

Let's take a look under the hood, beginning with the HTTP handler:

func APIgetPin(w http.ResponseWriter, r *http.Request) {
...
	vars := mux.Vars(r)
	pinIdstr := vars["pin"]
	pinId, err := strconv.Atoi(pinIdstr)
	pinState, err1 := common.GetPin(pinId)
...
	JSONresp := types.JSONMessageResponse{
...
		Spec: pinState,
	}
	common.JSONResponse(r, w, responseCode, JSONresp)
}
#1

In the code block above (#1), a request is handled to read the state of a given pin. The request variable pin (which inherited from the elsewhere defined endpoint /api/pin/{pin:[0-9]+}) is converted to the correct type before handing off to common.GetPin. After the hand off, it is return in a standard JSON response.

func GetPin(num int) (pin types.Pin, err error) {
...
	pinSelect := rpio.Pin(num)
	state := pinSelect.Read()

	pin = types.Pin{
		Number: num,
		State:  int(state),
	}
	return pin, err
}
#2

In code block #2, the function GetPin is defined. The golang library go-rpio is used to fetch the state of the requested pin.

Common requests and examples

All pins

curl http://raspberrypi.local:8080/api/pin

Returns an array of objects with the pin number and state.

{
  "metadata": {
    "selfLink": "/api/pin",
    "version": "0.0.1",
    "timestamp": 1581673372,
    "response": "Fetched all pins"
  },
  "spec": [
    {
      "number": 1,
      "state": 1
    },
    {
      "number": 2,
      "state": 1
    },
...
    {
      "number": 39,
      "state": 1
    },
    {
      "number": 40,
      "state": 0
    }
  ]
}

A single pin

curl http://raspberrypi.local:8080/api/pin/39

Returns the requested pin number and state.

{
  "metadata": {
    "selfLink": "/api/pin/39",
    "version": "0.0.1",
    "timestamp": 1581673438,
    "response": "Fetched pin state"
  },
  "spec": {
    "number": 39,
    "state": 1
  }
}

Write to a pin

curl -X POST http://raspberrypi.local:8080/api/pin/39/0

Returns the requested pin number and state.

{
  "metadata": {
    "selfLink": "/api/pin/39/0",
    "version": "0.0.1",
    "timestamp": 1581761266,
    "response": "Updated pin state"
  },
  "spec": {
    "number": 39,
    "state": 0
  }
}

Configuration

Authorization

A supported feature of the app is auth via the Authorization header.
Enabling is as easy as setting the variable APP_AUTH_SECRET. Once set, all requests to any pin related endpoints will require the header, otherwise it will reject the request.

Example: curl -H "Authorization: bearer mypassword" http://192.168.1.31:8080/api/pin/39 | jq .

{
  "metadata": {
    "selfLink": "/api/pin/39",
    "version": "0.0.1",
    "timestamp": 1581758742,
    "response": "Fetched pin state"
  },
  "spec": {
    "number": 39,
    "state": 0
  }
}

Authorization failure results in a 401 and:

{
  "metadata": {
    "selfLink": "/api/pin/39/0",
    "version": "0.0.1",
    "timestamp": 1581761556,
    "response": "Unauthorized"
  },
  "spec": null
}

SSL/TLS

Another supported feature of the app is SSL/TLS.
To enable, set APP_USE_TLS to true.
Setting SSL certificates requires the use of the environment variables APP_TLS_PUBLIC_CERT and APP_TLS_PRIVATE_CERT, setting their values as file locations.

Note: enabling SSL/HTTPS (4433) will disable HTTP (8080)

More

For more configuration options, please check out the environment variables section of the readme.

Usage ideas

Since this app becomes Yet-Another-API™, you are intended to consume it in your app.
You may wish to:

  • build a frontend to control LEDs via a web interface or from a mobile app
  • use NodeRED to post to the app's API to automate or schedule a sound or lights
  • write an app to read the number of Pods and power on the respective number of lights or output the number to a LCD display

These are only a few ideas, but there are many out which utilize the GPIO pins of a RPi and limitless ideas to conceive.

Notes

  • Special thanks to gh:stianeikeland for stianeikeland/go-rpio, as a fully golang implementation would not be possible without it
  • The pin number is limited between or at 1 and 40
  • Some pins won't do anything, such as the ground ones
  • Currently, this app is stateless – meaning that it won't save any states of the pins, nor upon a reboot will it reinstate their values (your app that talks to this should do this)
  • Ensure that the user running the app is in the gpio group or the security definitions force the group (for Raspbian the GID is 997) or run the app as root (not recommended)

:end of article:

That's all for today; Happy hacking!