{"id":25747789,"url":"https://github.com/elixir-circuits/circuits_gpio","last_synced_at":"2025-04-04T13:13:33.756Z","repository":{"id":32287234,"uuid":"131500489","full_name":"elixir-circuits/circuits_gpio","owner":"elixir-circuits","description":"Use GPIOs from Elixir","archived":false,"fork":false,"pushed_at":"2024-04-15T03:04:25.000Z","size":696,"stargazers_count":125,"open_issues_count":1,"forks_count":23,"subscribers_count":7,"default_branch":"main","last_synced_at":"2024-05-01T22:25:50.539Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elixir-circuits.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSES/Apache-2.0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-29T14:17:41.000Z","updated_at":"2024-05-17T15:35:48.836Z","dependencies_parsed_at":"2024-04-08T13:27:20.658Z","dependency_job_id":"58d1a514-36b4-4e0f-aa9d-a6e257fdda56","html_url":"https://github.com/elixir-circuits/circuits_gpio","commit_stats":{"total_commits":346,"total_committers":34,"mean_commits":"10.176470588235293","dds":"0.25144508670520227","last_synced_commit":"1802cdb581dc45e6ab50918eed680135dc8861ea"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-circuits%2Fcircuits_gpio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-circuits%2Fcircuits_gpio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-circuits%2Fcircuits_gpio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-circuits%2Fcircuits_gpio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elixir-circuits","download_url":"https://codeload.github.com/elixir-circuits/circuits_gpio/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247182420,"owners_count":20897381,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-26T12:18:04.574Z","updated_at":"2025-04-04T13:13:33.731Z","avatar_url":"https://github.com/elixir-circuits.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Circuits - GPIO\n\n[![Hex version](https://img.shields.io/hexpm/v/circuits_gpio.svg \"Hex version\")](https://hex.pm/packages/circuits_gpio)\n[![API docs](https://img.shields.io/hexpm/v/circuits_gpio.svg?label=hexdocs \"API docs\")](https://hexdocs.pm/circuits_gpio/Circuits.GPIO.html)\n[![CircleCI](https://dl.circleci.com/status-badge/img/gh/elixir-circuits/circuits_gpio/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/elixir-circuits/circuits_gpio/tree/main)\n[![REUSE status](https://api.reuse.software/badge/github.com/elixir-circuits/circuits_gpio)](https://api.reuse.software/info/github.com/elixir-circuits/circuits_gpio)\n\n`Circuits.GPIO` lets you use GPIOs in Elixir.\n\n## Getting started on Nerves and Linux\n\nIf you're natively compiling `circuits_gpio` using Nerves or using a Linux-based\nSBC like a Raspberry Pi, everything should work like any other Elixir library.\nNormally, you would include `circuits_gpio` as a dependency in your `mix.exs`\nlike this:\n\n```elixir\ndef deps do\n  [{:circuits_gpio, \"~\u003e 2.1\"}]\nend\n```\n\nOne common error on RaspberryPi OS is that the Erlang headers are missing\n(`erl_nif.h`), you may need to install erlang with `apt-get install erlang-dev`\nor build Erlang from source per instructions [here](http://elinux.org/Erlang).\n\n## Examples\n\nWhile `Circuits.GPIO` can support non-Nerves and non-Linux systems, the examples\nbelow were made using Nerves. Operation on other devices mostly differs on how\nto refer to GPIOs.\n\nThe following examples were tested on a Raspberry Pi that was connected to an\n[Erlang Embedded Demo Board](http://solderpad.com/omerk/erlhwdemo/). There's\nnothing special about either the demo board or the Raspberry Pi.\n\n## Nerves + Livebook\n\n[Nerves Livebook project](https://github.com/nerves-livebook/nerves_livebook)\nallows you to try out the Nerves project on real hardware without needing\nto build a project from scratch.\n\nWithin minutes, you'll have a Raspberry Pi or Beaglebone running Nerves. You'll\nbe able to run code in [Livebook](https://livebook.dev/) and work through\nNerves tutorials from the comfort of your browser.\n\n[![Run in Nerves Livebook](https://livebook.dev/badge/v1/blue.svg)](https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Felixir-circuits%2Fcircuits_gpio%2Fblob%2Fmain%2Fnotebooks%2Fbasics.livemd)\n\n## GPIO\n\nA [General Purpose\nInput/Output](https://en.wikipedia.org/wiki/General-purpose_input/output) (GPIO)\nis just a wire that you can use as an input or an output. It can only be one of\ntwo values, 0 or 1. A 1 corresponds to a logic high voltage like 3.3 V and a 0\ncorresponds to 0 V. The actual voltage depends on the hardware.\n\nHere's an example of turning an LED on or off:\n\n![GPIO LED schematic](assets/images/schematic-gpio-led.png)\n\nTo turn on the LED that's connected to the net (or wire) labeled `GPIO18`, you\nneed to open it first. The first parameter to `Circuits.GPIO.open/2` is called a\nGPIO spec and identifies the GPIO. The Raspberry Pis are nice and provide string\nnames for GPIOs. Other boards are not as nice so you always have to check. The\nstring name for this GPIO is `\"GPIO18\"` (use `\"PIN12\"` on a Raspberry Pi 5).\n\n```elixir\niex\u003e {:ok, gpio} = Circuits.GPIO.open(\"GPIO12\", :output)\n{:ok, %Circuits.GPIO.CDev{...}}\n\niex\u003e Circuits.GPIO.write(gpio, 1)\n:ok\n\niex\u003e Circuits.GPIO.close(gpio)\n:ok\n```\n\nThe call to `Circuits.GPIO.close/1` is not necessary, since the garbage\ncollector will free up unreferenced GPIOs. It's a good practice, though,\nsince backends can enforce exclusivity and prevent future opens from\nworking until the GC occurs.\n\nInput works similarly. Here's an example of a button with a pull down resistor\nconnected.\n\n![GPIO Button schematic](assets/images/schematic-gpio-button.png)\n\nIf you're not familiar with pull up or pull down resistors, they're resistors\nwhose purpose is to drive a wire high or low when the button isn't pressed. In\nthis case, it drives the wire low. Many processors have ways of configuring\ninternal resistors to accomplish the same effect without needing to add an\nexternal resistor. If you're using a Raspberry Pi, you can use [the built-in\npull-up/pull-down resistors](#internal-pull-uppull-down).\n\nThe code looks like this in `Circuits.GPIO`:\n\n```elixir\niex\u003e {:ok, gpio} = Circuits.GPIO.open(\"GPIO17\", :input)\n{:ok, %Circuits.GPIO.CDev{...}}\n\niex\u003e Circuits.GPIO.read(gpio)\n0\n\n# Push the button down\n\niex\u003e Circuits.GPIO.read(gpio)\n1\n```\n\nIf you'd like to get a message when the button is pressed or released, call the\n`set_interrupts` function. You can trigger on the `:rising` edge, `:falling`\nedge or `:both`.\n\n```elixir\niex\u003e Circuits.GPIO.set_interrupts(gpio, :both)\n:ok\n\niex\u003e flush\n{:circuits_gpio, \"GPIO17\", 1233456, 1}\n{:circuits_gpio, \"GPIO17\", 1234567, 0}\n:ok\n```\n\nNote that after calling `set_interrupts`, the calling process will receive an\ninitial message with the state of the pin. This prevents the race condition\nbetween getting the initial state of the pin and turning on interrupts. Without\nit, you could get the state of the pin, it could change states, and then you\ncould start waiting on it for interrupts. If that happened, you would be out of\nsync.\n\n### Internal pull-up/pull-down\n\nTo connect or disconnect an internal [pull-up or pull-down resistor](https://github.com/raspberrypilearning/physical-computing-guide/blob/master/pull_up_down.md) to a GPIO\npin, call the `set_pull_mode` function.\n\n```elixir\niex\u003e Circuits.GPIO.set_pull_mode(gpio, pull_mode)\n:ok\n```\n\nValid `pull_mode` values are `:none` `:pullup`, or `:pulldown`\n\nNote that `set_pull_mode` is platform dependent, and currently only works for\nRaspberry Pi hardware.  Calls to `set_pull_mode` on other platforms will have no\neffect.  The internal pull-up resistor value is between 50K and 65K, and the\npull-down is between 50K and 60K.  It is not possible to read back the current\nPull-up/down settings, and GPIO pull-up pull-down resistor connections are\nmaintained, even when the CPU is powered down.\n\n## GPIO Specs\n\n`Circuits.GPIO` v2.0 supports a new form of specifying how to open a GPIO called\na `t:gpio_spec/0`. These specs are very flexible and allow for GPIOs to be\nopened by number, a string label, or a tuple that includes both the GPIO\ncontroller hardware name and a line offset.\n\nThe contents of a `gpio_spec` depend on the backend. When running on Nerves or\na Linux machine, `Circuits.GPIO` uses the Linux gpio-cdev backend. This backend\nprefers the use of GPIO controller/line offset tuples and labels. For backwards\ncompatibility, it somewhat supports use of the *older* pin numbering scheme.\n\nThe GPIO controller part of the tuple is usually some variation on `\"gpiochip0\"`\nthat depends on what controllers are available under `/dev`. The line offset is\na the line offset of the GPIO on that controller.\n\n```elixir\niex\u003e {:ok, gpio} = Circuits.GPIO.open({\"gpiochip0\", 1}, :input)\n{:ok, %Circuits.GPIO.CDev{...}}\n```\n\nWhen the Linux device tree is configured with GPIO labels, you can use those instead:\n\n```elixir\niex\u003e {:ok, gpio} = Circuits.GPIO.open(\"special-name-for-pin-1\")\n{:ok, %Circuits.GPIO.CDev{...}}\n```\n\nIf you're deploying to multiple types of devices and you can set labels in the\ndevice tree, labels make it really easy for code using `Circuits.GPIO` to just\nuse the right GPIO.\n\nLabels are not guaranteed to be unique, so if your device defines one twice,\n`Circuits.GPIO` will use the first GPIO it finds that has the specified label.\n\nSee [Enumeration](#enumeration) for listing out all available `gpio_specs` for\nyour device.\n\n## Enumeration\n\n`Circuits.GPIO` v2.0 supports a new function, `enumerate/0`, which lists every\nknown GPIO pin.\n\nFor Nerves and Linux users, the `gpio-cdev` subsystem maintains the official\nlist. See the [Official DeviceTree documentation for\nGPIOs](https://elixir.bootlin.com/linux/v6.6.6/source/Documentation/devicetree/bindings/gpio/gpio.txt)\nfor more information on how to configure the fields of this struct for your own\nsystem.\n\nHere's an example:\n\n```elixir\niex\u003e Circuits.GPIO.enumerate()\n[\n  %{\n    location: {\"gpiochip0\", 0},\n    label: \"ID_SDA\",\n    controller: \"pinctrl-bcm2835\"\n  },\n  %{\n    location: {\"gpiochip0\", 1},\n    label: \"ID_SCL\",\n    controller: \"pinctrl-bcm2835\"\n  },\n  %{\n    location: {\"gpiochip0\", 2},\n    label: \"SDA1\",\n    controller: \"pinctrl-bcm2835\"\n  },\n  ...\n]\n```\n\nThe `:location` can always be passed as the first parameter to\n`Circuits.GPIO.open/3`. You may find the `:label` field more descriptive to use,\nthough.\n\n## Convenience functions\n\nHaving to `open` *then* `read` and `write` can be a cumbersome for one-off GPIO\naccess in code and when working at the IEx prompt. Circuits v2.0 has a pair of\nnew functions to help:\n\n```elixir\niex\u003e alias Circuits.GPIO\niex\u003e GPIO.write_one(\"special-name-for-pin-1\", 1)\n:ok\niex\u003e GPIO.read_one(\"special-name-for-pin-2\")\n1\n```\n\nThese functions get passed a `t:gpio_spec/0` just like `open/3` and internally\nopen the GPIO and read or write it. Importantly, they `close` the GPIO when done\nto avoid reserving the GPIO any longer than necessary.\n\nPlease note that this is not a performant way of reading or writing the same\nGPIO more than once. Opening a GPIO takes much longer than reading or writing an\nalready opened one, so if these are used in tight loops, the open overhead will\ndominate (\u003e99% of the time taken in a trivial benchmark.)\n\n## Testing\n\nThe `Circuits.GPIO.CDev` backend supports a test mode on platforms without CDev\nGPIO support and when `MIX_ENV=test`. In previous versions of `Circuits.GPIO`,\nthis was called the \"stub\".\n\nThere are a couple ways of using it, but you might have it by default especially\nif you're on MacOS. To see, run the following and look for `test: true`:\n\n```elixir\niex\u003e Circuits.GPIO.backend_info()\n%{name: {Circuits.GPIO.CDev, test: true}, pins_open: 0}\n```\n\nIf you're not running with `test: true`, you can force it by setting the default\nbackend:\n\n```elixir\nconfig :circuits_gpio, default_backend: {Circuits.GPIO.CDev, test: true}\n```\n\nTest mode has 64 GPIOs. Each pair of GPIOs is connected. For example, GPIO 0 is\nconnected to GPIO 1. If you open GPIO 0 as an output and GPIO 1 as an input, you\ncan write to GPIO 0 and see the result on GPIO 1. Here's an example:\n\n```elixir\niex\u003e {:ok, gpio0} = Circuits.GPIO.open({\"gpiochip0\", 0}, :output)\n{:ok, %Circuits.GPIO.CDev{...}}\niex\u003e {:ok, gpio1} = Circuits.GPIO.open({\"gpiochip0\", 1}, :input)\n{:ok, %Circuits.GPIO.CDev{...}}\niex\u003e Circuits.GPIO.read(gpio1)\n0\niex\u003e Circuits.GPIO.write(gpio0, 1)\n:ok\niex\u003e Circuits.GPIO.read(gpio1)\n1\n```\n\nTest mode is fairly limited, but it does support interrupts.\n\n## Migration from v1.x\n\nv2.x is the current version of `Circuits.GPIO`.\n\n`Circuits.GPIO` v2.0 is an almost backwards compatible update to `Circuits.GPIO`\nv1.x. Here's what's new:\n\n* Linux or Nerves are no longer required. In fact, the NIF supporting them won't\n  be compiled if you don't want it.\n* GPIOs can be enumerated to see what's available (See `Circuits.GPIO.enumerate/0`)\n* Linux and Nerves now use the Linux GPIO cdev subsystem rather than sysfs\n* GPIO pull mode setting for all platforms that support it rather than only Raspberry Pi\n* Develop using simulated GPIOs to work with LEDs and buttons with\n  [CircuitsSim](https://github.com/elixir-circuits/circuits_sim)\n\nIf you've used `Circuits.GPIO` v1.x, nearly all of your code will be the\nsame.`Circuits.GPIO` offers a substantial improvement by more descriptive GPIO\nspecs for identifying GPIOs. You can still refer to GPIOs by number. However,\nyou can also refer to GPIOs by labels and by which GPIO controller handles them.\nThe new `enumerate/0` can help with this.\n\nIf you are using a previous version and wish to update, review the [porting\nguide](PORTING.md). Also see [circuits_gpio v1.x maintenance\nbranch](https://github.com/elixir-circuits/circuits_gpio/tree/maint-v1.x).\n\n## FAQ\n\n### Where can I get help?\n\nMost issues people have are on how to communicate with hardware for the first\ntime. Since `Circuits.GPIO` is a thin wrapper on the Linux CDev GPIO interface,\nyou may find help by searching for similar issues when using Python or C.\n\nFor help specifically with `Circuits.GPIO`, you may also find help on the nerves\nchannel on the [elixir-lang Slack](https://elixir-lang.slack.com/).  Many\n[Nerves](http://nerves-project.org) users also use `Circuits.GPIO`.\n\n### I tried turning on and off a GPIO as fast as I could. Why was it slow?\n\nPlease don't do that - there are so many better ways of accomplishing whatever\nyou're trying to do:\n\n1. If you're trying to drive a servo or dim an LED, look into PWM. Many\n   platforms have PWM hardware and you won't tax your CPU at all. If your\n   platform is missing a PWM, several chips are available that take I2C commands\n   to drive a PWM output.\n2. If you need to implement a wire level protocol to talk to a device, look for\n   a Linux kernel driver. It may just be a matter of loading the right kernel\n   module.\n3. If you want a blinking LED to indicate status, `gpio` really should\n   be fast enough to do that, but check out Linux's LED class interface. Linux\n   can flash LEDs, trigger off events and more. See [nerves_leds](https://github.com/nerves-project/nerves_leds).\n\nIf you're still intent on optimizing GPIO access, you may be interested in\n[gpio_twiddler](https://github.com/fhunleth/gpio_twiddler). Note that the\ntwiddler doc is dated. Circuits.GPIO v2 should be faster than v1.\n\n### Can I develop code that uses GPIO on my laptop?\n\nThe intended way to support this is to have a custom `Circuits.GPIO.Backend`\nthat runs on your laptop. The\n[CircuitsSim](https://github.com/elixir-circuits/circuits_sim) is an example of\na project that provides simulated LEDs and buttons.\n\n## License\n\nAll original source code in this project is licensed under Apache-2.0.\n\nAdditionally, this project follows the [REUSE recommendations](https://reuse.software)\nand labels so that licensing and copyright are clear at the file level.\n\nExceptions to Apache-2.0 licensing are:\n\n* Configuration and data files are licensed under CC0-1.0\n* Documentation files are CC-BY-4.0\n* Erlang Embedded board images are Solderpad Hardware License v0.51.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-circuits%2Fcircuits_gpio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felixir-circuits%2Fcircuits_gpio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-circuits%2Fcircuits_gpio/lists"}