GPIO Library for Single Board Computers built in Swift.
About the Project
This library is heavily based on SwiftyGPIO, and does share some code with it. The reason for rewriting is to try to improve the readability and maintainability of the code by strengthening type safety when interacting with hardware registers, and streamling the developer interaction. Doing it as a re-write was proving to be faster and easier to test than attempting to refactor it in-place.
Things can go further, say you wanted to do this for 4 pins at the same time. SingleBoard supports the idea of pin sets to interact with a set of pins instead of just a single pin. The interface is different in subtle ways, but very similar:
let gpios = SingleBoard.raspberryPi.gpio
let pins: PinSet = [.p22, .p23, .p24, .p25]
gpios[pins].setMode(.output)
gpios[pins].setPullup(.down)
while true {
print("On!")
gpios[pins].value = true
usleep(1_000_000)
print("Off!")
gpios[pins].value = false
usleep(1_000_000)
}
If you just want a bit of type safety, you can also use pin sets of single pins, but it may be a bit slower in some cases, depending on which board you are targeting, and what operation you are doing. In the case of the Raspberry Pi, reading or writing many pins at once is perfectly fast. So is configuring pullups for many pins at once. However, using pin sets to set input/output modes is convenient, but slower than using pin indexes:
This is a brief example that writes to a device at address 0x40, to write a 1 at offset 0x06.
// Get a connection for a device with address 0x40 on the board's primary bus
print(SingleBoard.raspberryPi.i2cMainBus.busId)
let i2cDevice = SingleBoard.raspberryPi.i2cMainBus[0x40]
i2cDevice.writeByte(to: 0x06, value: 1)
A board’s main bus is considered to be the one that is primarily for use by those developing on the single board computer, and not for the system’s use.
On the Raspberry Pi, bus 0 is used by the system for a handful of things including identifying HATs. Bus 1 is exposed on pins 3 & 5 for use by tinkerers, and so bus 1 is considered the main bus. On the Rock 64, it’s the opposite. Bus 0 is exposed on pins 3 & 5, while bus 1 is used for HATs and system devices. The Pine A64 is like the Raspberry Pi. By exposing the main bus as a property, it makes it a bit easier to write code that can handle multiple similar boards.
The full set of basic read/write functionality is the following:
But in addition to this, there is also support for handling types that conform to RawRepresentable and OptionSet automatically. For example, any RawRepresentable that is backed by a UInt8 can be used to read or write bytes and words:
For more complex types, there’s protocols that you can implement to enable calling encode and decode on the endpoint with your own types. These leverage Data for storing the buffers, but make it possible to keep code a little cleaner.
The idea here is to make it a bit easier to use more restrictive types to represent the values to be read and written when interacting with the I2C device. These techniques are demonstrated in the PCA9685 and MCP4725 libraries.
PWM
A very simple example here, telling a Raspberry Pi to output on channel 0, using both of its output pins:
SingleBoard
GPIO Library for Single Board Computers built in Swift.
About the Project
This library is heavily based on SwiftyGPIO, and does share some code with it. The reason for rewriting is to try to improve the readability and maintainability of the code by strengthening type safety when interacting with hardware registers, and streamling the developer interaction. Doing it as a re-write was proving to be faster and easier to test than attempting to refactor it in-place.
Supported Boards
Experimental
Usage
GPIO
A simple example that toggles pin GPIO12/BCM12 on a Raspberry Pi on and off every second:
Things can go further, say you wanted to do this for 4 pins at the same time. SingleBoard supports the idea of pin sets to interact with a set of pins instead of just a single pin. The interface is different in subtle ways, but very similar:
If you just want a bit of type safety, you can also use pin sets of single pins, but it may be a bit slower in some cases, depending on which board you are targeting, and what operation you are doing. In the case of the Raspberry Pi, reading or writing many pins at once is perfectly fast. So is configuring pullups for many pins at once. However, using pin sets to set input/output modes is convenient, but slower than using pin indexes:
I2C
This is a brief example that writes to a device at address
0x40
, to write a1
at offset0x06
.A board’s main bus is considered to be the one that is primarily for use by those developing on the single board computer, and not for the system’s use.
On the Raspberry Pi, bus 0 is used by the system for a handful of things including identifying HATs. Bus 1 is exposed on pins 3 & 5 for use by tinkerers, and so bus 1 is considered the main bus. On the Rock 64, it’s the opposite. Bus 0 is exposed on pins 3 & 5, while bus 1 is used for HATs and system devices. The Pine A64 is like the Raspberry Pi. By exposing the main bus as a property, it makes it a bit easier to write code that can handle multiple similar boards.
The full set of basic read/write functionality is the following:
But in addition to this, there is also support for handling types that conform to
RawRepresentable
andOptionSet
automatically. For example, any RawRepresentable that is backed by a UInt8 can be used to read or write bytes and words:Taken a step further, you can also use
RawRepresentable
andOptionSet
as the bytes or words themselves if they are backed byUInt8
orUInt16
:Reading works the same way.
For more complex types, there’s protocols that you can implement to enable calling
encode
anddecode
on the endpoint with your own types. These leverageData
for storing the buffers, but make it possible to keep code a little cleaner.The idea here is to make it a bit easier to use more restrictive types to represent the values to be read and written when interacting with the I2C device. These techniques are demonstrated in the PCA9685 and MCP4725 libraries.
PWM
A very simple example here, telling a Raspberry Pi to output on channel 0, using both of its output pins:
Much like GPIOs, enabling the output on pins can use a pin index, or a pin set:
Built with SingleBoard
Included Libraries
Projects