{"id":25747788,"url":"https://github.com/elixir-circuits/circuits_i2c","last_synced_at":"2025-05-16T15:07:58.820Z","repository":{"id":33278087,"uuid":"133993281","full_name":"elixir-circuits/circuits_i2c","owner":"elixir-circuits","description":"Communicate over I2C from Elixir","archived":false,"fork":false,"pushed_at":"2025-05-13T01:53:44.000Z","size":532,"stargazers_count":64,"open_issues_count":4,"forks_count":11,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-15T08:23:12.437Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Elixir","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,"zenodo":null}},"created_at":"2018-05-18T18:58:08.000Z","updated_at":"2025-04-28T04:25:20.000Z","dependencies_parsed_at":"2024-01-06T01:30:18.040Z","dependency_job_id":"2be0d122-aa70-4a76-a6ce-4f9beab25f81","html_url":"https://github.com/elixir-circuits/circuits_i2c","commit_stats":{"total_commits":335,"total_committers":29,"mean_commits":"11.551724137931034","dds":"0.25671641791044775","last_synced_commit":"182df9008fe4fe1f2e1445cd90ca9d2c6cb3f73c"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-circuits%2Fcircuits_i2c","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-circuits%2Fcircuits_i2c/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-circuits%2Fcircuits_i2c/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-circuits%2Fcircuits_i2c/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elixir-circuits","download_url":"https://codeload.github.com/elixir-circuits/circuits_i2c/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553958,"owners_count":22090417,"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.548Z","updated_at":"2025-05-16T15:07:53.811Z","avatar_url":"https://github.com/elixir-circuits.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Circuits.I2C\n\n[![Hex version](https://img.shields.io/hexpm/v/circuits_i2c.svg \"Hex version\")](https://hex.pm/packages/circuits_i2c)\n[![API docs](https://img.shields.io/hexpm/v/circuits_i2c.svg?label=hexdocs \"API docs\")](https://hexdocs.pm/circuits_i2c/Circuits.I2C.html)\n[![CircleCI](https://dl.circleci.com/status-badge/img/gh/elixir-circuits/circuits_i2c/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/elixir-circuits/circuits_i2c/tree/main)\n[![REUSE status](https://api.reuse.software/badge/github.com/elixir-circuits/circuits_i2c)](https://api.reuse.software/info/github.com/elixir-circuits/circuits_i2c)\n\n`Circuits.I2C` lets you communicate with hardware devices using the I2C protocol.\n\n*This is the v2.0 branch. Circuits.I2C v1.x is still maintained in the [maint-v1.x branch](https://github.com/elixir-circuits/circuits_i2c/tree/maint-v1.x).*\n\n`Circuits.I2C` v2.0  is an almost backwards compatible update to `Circuits.I2C`\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* Develop using simulated I2C devices with\n  [CircuitsSim](https://github.com/elixir-circuits/circuits_sim)\n* Use USB-\u003eI2C adapters for development on your laptop (Coming soon)\n\nIf you've used `Circuits.I2C` v1.x, nearly all of your code will be the same. If\nyou're a library author, we'd appreciate if you could try this out and update\nyour `:circuits_i2c` dependency to allow v2.0. Details can be found in our\n[porting guide](PORTING.md).\n\n## Getting started on Nerves and Linux\n\nBy default, `Circuits.I2C` supports the Linux-based I2C driver interface so the\nfollowing instructions assume a Linux-based system like Nerves, Raspberry Pi OS,\nembedded Linux or even desktop Linux if I2C lines are exposed. If you want to\nuse `Circuits.I2C` on a different platform and support is available, generally\nthe only difference is to change the \"open\" call. The rest is the same.\n\nFirst off, add `circuits_i2c` to your `mix.exs`'s dependency list like any other\nElixir library:\n\n```elixir\ndef deps do\n  [{:circuits_i2c, \"~\u003e 2.0\"}]\nend\n```\n\n`Circuits.I2C` doesn't load device drivers, so you may need to load them\nbeforehand. If you are using Nerves on a supported platform, this is enabled for\nyou already. If using Raspberry Pi OS, the [Adafruit Raspberry Pi I2C\ninstructions](https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c)\nmay be helpful.\n\nInternally, it uses the [Linux \"i2c-dev\"\ninterface](https://elixir.bootlin.com/linux/latest/source/Documentation/i2c/dev-interface)\nso that it does not require board-dependent code.\n\n## Getting started without hardware\n\nIf you don't have any real I2C devices, it's possible to work with simulated\ndevices. See the [CircuitsSim](https://github.com/elixir-circuits/circuits_sim)\nproject for details.\n\n## I2C background\n\nAn [Inter-Integrated Circuit](https://en.wikipedia.org/wiki/I%C2%B2C) (I2C) bus\nsupports addressing hardware components and bidirectional use of the data line.\n\nThe following shows a bus IO expander connected via I2C to the processor.\n\n![I2C schematic](assets/images/schematic-i2c.png)\n\nThe protocol for talking to the IO expander is described in the [MCP23008\nDatasheet](http://www.microchip.com/wwwproducts/Devices.aspx?product=MCP23008).\nHere's a simple example of using it.\n\n```elixir\n# On the Raspberry Pi, the IO expander is connected to I2C bus 1 (i2c-1).\n# Its 7-bit address is 0x20. (see datasheet)\niex\u003e alias Circuits.I2C\nCircuits.I2C\niex\u003e {:ok, ref} = I2C.open(\"i2c-1\")\n{:ok, #Reference\u003c...\u003e}\n\n# By default, all 8 GPIOs are set to inputs. Set the 4 high bits to outputs\n# so that we can toggle the LEDs. (Write 0x0f to register 0x00)\niex\u003e I2C.write(ref, 0x20, \u003c\u003c0x00, 0x0f\u003e\u003e)\n:ok\n\n# Turn on the LED attached to bit 4 on the expander. (Write 0x10 to register\n# 0x09)\niex\u003e I2C.write(ref, 0x20, \u003c\u003c0x09, 0x10\u003e\u003e)\n:ok\n\n# Read all 11 of the expander's registers to see that the bit 0 switch is\n# the only one on and that the bit 4 LED is on.\niex\u003e I2C.write(ref, 0x20, \u003c\u003c0\u003e\u003e)  # Set the next register to be read to 0\n:ok\n\niex\u003e I2C.read(ref, 0x20, 11)\n{:ok, \u003c\u003c15, 0, 0, 0, 0, 0, 0, 0, 0, 17, 16\u003e\u003e}\n\n# The operation of writing one or more bytes to select a register and\n# then reading is very common, so a shortcut is to just run the following:\niex\u003e I2C.write_read(ref, 0x20, \u003c\u003c0\u003e\u003e, 11)\n{:ok, \u003c\u003c15, 0, 0, 0, 0, 0, 0, 0, 0, 17, 16\u003e\u003e}\n\n# The 17 in register 9 says that bits 0 and bit 4 are high\n# We could have just read register 9.\n\niex\u003e I2C.write_read(ref, 0x20, \u003c\u003c9\u003e\u003e, 1)\n{:ok, \u003c\u003c17\u003e\u003e}\n```\n\n## Creating a new backend\n\n`Circuits.I2C` supports alternative backends to support non-Linux hardware,\ntesting, and simulation. A backend can support communication on more than one\nI2C bus.\n\nTo create a new backend, you need to implement the `Circuits.I2C.Backend`\nbehaviour. `Circuits.I2C` calls the `bus_names/1` callback to discover what I2C\nbuses are available and then it calls the `open/2` callback to use the I2C bus.\n\n## FAQ\n\n### How do I debug?\n\nThe most common issue is communicating with an I2C for the first time.  For I2C,\nfirst check that an I2C bus is available:\n\n```elixir\niex\u003e Circuits.I2C.bus_names\n[\"i2c-1\"]\n```\n\nIf the list is empty, then I2C is either not available, not enabled, or not\nconfigured in the kernel. If you're using Raspbian, run `raspi-config` and check\nthat I2C is enabled in the advanced options. If you're on a BeagleBone, try\nrunning `config-pin` and see the [Universal I/O\nproject](https://github.com/cdsteinkuehler/beaglebone-universal-io) to enable\nthe I2C pins. On other ARM boards, double check that I2C is enabled in the\nkernel and that the device tree configures it.\n\nOnce an I2C bus is available, try detecting devices on it:\n\n```elixir\niex\u003e Circuits.I2C.detect_devices()\nCircuits.I2C.detect_devices\nDevices on I2C bus \"i2c-1\":\n * 64  (0x40)\n * 112 (0x70)\n\n2 devices detected on 1 I2C buses\n```\n\nThe return value here is a list of device addresses that were detected. It is\nstill possible that the device will work even if it does not detect, but you\nprobably want to check wires at this point. If you have a logic analyzer, use it\nto verify that I2C transactions are being initiated on the bus.\n\n### I2C seems slow. What could be wrong?\n\nI2C buses are usually run at 100 kbit/s or 400 kbit/s. Many devices support\nhigher speeds. The tradeoff is that higher speeds are sometimes don't work as\nwell especially if you're using jumper cables to connect parts together. The\nRaspberry Pi runs the I2C bus at a low speed - probably for this reason.\n\nOther things to check:\n\n* Can you reduce the reads and writes? I2C devices let you read or write many\n  bytes at the same time. Each transaction has overhead so minimizing\n  transaction helps.\n* Can you reduce the total number of bytes in each transaction? For example, do\n  you need to read a particular register? Is there a mode that the device can be\n  put it so that it only returns useful data?\n* Can a write and read be combined? The `Circuits.I2C.write_read` function is\n  more efficient than a separate write followed by a read.\n* Does the device support a queue mode? Some devices have internal queues that\n  allow the host to copy out more than one sample each time.\n\n### Where can I get help?\n\nThe hardest part is communicating with a device for the first time. The issue is\nusually unrelated to `Circuits.I2C`. If you expand your searches to include\nPython and C forums, you'll frequently find the answer.\n\nIf that fails, try posting a question to the [Elixir\nForum](https://elixirforum.com/). Tag the question with `Nerves` and it will\nhave a good chance of getting to the right people. Feel free to do this even if\nyou're not using Nerves.\n\n### Can I develop code that uses Circuits.I2C on my laptop?\n\nYou have a few options:\n\n1. Connect your I2C devices to a USB-\u003eI2C adapter like a [Adafruit FT232H\n   Breakout](https://www.adafruit.com/product/2264)\n2. Use the CircuitsSim backend\n3. Create a custom backend and use it to mock interactions with the Circuits.I2C\n   API\n\n### Will it run on Arduino?\n\nNo. This only runs on Linux-based boards. If you're interested in controlling an\nArduino from a computer that can run Elixir, check out\n[circuits_uart](https://hex.pm/packages/circuits_uart) for communicating via the\nArduino's serial connection or\n[firmata](https://github.com/mobileoverlord/firmata) for communication using the\nArduino's Firmata protocol.\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* Linux header files included for convenience under GPL-2.0-or-later WITH\n  Linux-syscall-note\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_i2c","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felixir-circuits%2Fcircuits_i2c","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-circuits%2Fcircuits_i2c/lists"}