{"id":17486511,"url":"https://github.com/noah-huppert/py-i2c-register","last_synced_at":"2025-04-22T15:40:21.264Z","repository":{"id":62580972,"uuid":"86943051","full_name":"Noah-Huppert/py-i2c-register","owner":"Noah-Huppert","description":"Python wrapper library around the common I2C controller register pattern.","archived":false,"fork":false,"pushed_at":"2021-05-25T07:56:07.000Z","size":528,"stargazers_count":6,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-17T01:26:40.842Z","etag":null,"topics":["i2c","python","python-library"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Noah-Huppert.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-01T21:10:56.000Z","updated_at":"2024-01-16T03:40:35.000Z","dependencies_parsed_at":"2022-11-03T21:32:05.262Z","dependency_job_id":null,"html_url":"https://github.com/Noah-Huppert/py-i2c-register","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Noah-Huppert%2Fpy-i2c-register","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Noah-Huppert%2Fpy-i2c-register/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Noah-Huppert%2Fpy-i2c-register/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Noah-Huppert%2Fpy-i2c-register/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Noah-Huppert","download_url":"https://codeload.github.com/Noah-Huppert/py-i2c-register/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250269646,"owners_count":21402915,"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":["i2c","python","python-library"],"created_at":"2024-10-19T02:10:40.809Z","updated_at":"2025-04-22T15:40:21.185Z","avatar_url":"https://github.com/Noah-Huppert.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/Noah-Huppert/py-i2c-register.svg?branch=master)](https://travis-ci.org/Noah-Huppert/py-i2c-register) [![Test Coverage](https://codeclimate.com/github/Noah-Huppert/py-i2c-register/badges/coverage.svg)](https://codeclimate.com/github/Noah-Huppert/py-i2c-register/coverage) [![PyPI version](https://badge.fury.io/py/py-i2c-register.svg)](https://badge.fury.io/py/py-i2c-register)\n\n# Python I2C Register\nPython wrapper library around the common I2C controller register pattern.\n\nI2C Register is a python library which aims to make communicating with registers on I2C devices dead simple. It is meant to directly transfer the Register Definitions pages of a data sheet into your program.\n\n# Table Of Contents\n- [Installation](#installation)\n- [Quick Example](#quick-example)\n- [Systems Overview](#systems-overview)\n    - [Creating a RegisterList](#creating-a-registerlist)\n    - [Defining Registers](#defining-registers)\n    - [Adding RegisterSegments](#adding-registersegments)\n    - [Reading from RegisterSegments](#reading-from-registersegments)\n    - [Writing to RegisterSegments](#writing-to-registersegments)\n- [Writting Wrapper Classes](#writing-wrapper-classes)\n- [Development](#development)\n    - [Running Tests](#running-tests)\n- [Distribution](#distribution)\n    - [Setup](#setup)\n    - [Steps](#steps)\n\n# Installation\nI2C Register is available as a PIP package with the name `py-i2c-register`.\n\nSimply use PIP to install:\n\n```bash\npip install --user py-i2c-register\n```\n\nYou will then be able to include the `py_i2c_register` module and its various classes:\n\n```python\nfrom py_i2c_register.register_list import RegisterList\nfrom py_i2c_register.register import Register\nfrom py_i2c_register.register_segment import RegisterSegment\n```\n\n# Quick Example\nTake these control register definitions from a data sheet: [page 1](https://github.com/Noah-Huppert/py-i2c-register/blob/master/docs/img/example-register-defs-p1.png?raw=true), [page 2](https://github.com/Noah-Huppert/py-i2c-register/blob/master/docs/img/example-register-defs-p2.png?raw=true)\n\nWith the help of the I2C Register library they can easily be represented and manipulated.\n\n```python\nfrom py_i2c_register.register_list import RegisterList\nfrom py_i2c_register.register import Register\n\n# Create RegisterList instance to hold registers, device's i2c address is 0x62\ncontrols = RegisterList(0x62, i2c, {})\n\n# Add a definition for an ACQ_COMMAND (Acquisition Command) register, address 0x00 with WRITE permissions\ncontrols.add(\"ACQ_COMMAND\", 0x00, Register.WRITE, {})\\\n    .add(\"ACQ_COMMAND\", 0, 7, [0] * 8)  # Define the segment of bits to read with LSB index of 0 and MSB index of 7\n\n# Add a definition for a STATUS register, address 0x01 with READ permissions\ncontrols.add(\"STATUS\", 0x01, Register.READ, {}) \\\n    # Define various individual Register Segments which each signify different parts of the status\n    .add(\"PROC_ERROR_FLAG\", 6, 6, [0]) \\\n    .add(\"HEALTH_FLAG\", 5, 5, [0]) \\\n    .add(\"SECONDARY_RET_FLAG\", 4, 4, [0]) \\\n    .add(\"INVALID_SIGNAL_FLAG\", 3, 3, [0]) \\\n    .add(\"SIGNAL_OVERFLOW_FLAG\", 2, 2, [0]) \\\n    .add(\"REFERENCE_OVERFLOW_FLAG\", 1, 1, [0]) \\\n    .add(\"BUSY_FLAG\", 0, 0, [0])\n\n# Add a definition for a VELOCITY register, address 0x09 with READ permissions\ncontrols.add(\"VELOCITY\", 0x09, Register.READ, {})\\\n    .add(\"VELOCITY\", 0, 7, [0] * 8)  # Define the segment of bits to read for velocity value with LSB index of 0 and MSB index of 7\n\n\n# Super simple to read and write values\n# Set ACQ_COMMAND Register bits to value of 0x04, then write to register\ncontrols.set_bits_from_int(\"ACQ_COMMAND\", \"ACQ_COMMAND\", 0x04, write_after=True)\n\n# Read STATUS register for BUSY_FLAG value and convert to an integer\nbusy = controls.to_int(\"STATUS\", \"BUSY_FLAG\", read_first=True)\n\n# Read VELOCITY register and convert to two's compliment integer\nvelocity = controls.to_twos_comp_int(\"VELOCITY\", \"VELOCITY\", read_first=True)\n```\n\n# Systems Overview\nThe main class this library provides is the `RegisterList` class. This class manages a list of `Register` definitions. It also provides some useful helper methods to make performing certain common actions quick and easy.\n\n## Creating a RegisterList\nTo create a `RegisterList` import the `register_list.RegisterList` class. Then call the constructor giving it a I2C device address, an [I2C Object](/docs/i2c-object.md), and any `Register` objects you have already defined:\n\n```python\nfrom py_i2c_register.register_list import RegisterList\ncontrols = RegisterList(0x62, i2c, {})\n```\n\nThe provided I2C Device address will be used to contact the device which holds the registers over I2C. The [I2C Object](/docs/i2c-object.md) depends on your platform, see the [documentation](/docs/i2c-object.md) for more information. In most cases you can provide an empty `Register` map as well.\n\n## Defining Registers\nAfter you create a `RegisterList` you must define some registers to control. A `Register` is defined by a name (for easy programmatic access), an I2C address, and a string containing IO operation permissions (ex., register read only, or read and write, ect).\n\nThe `RegisterList` class provides a useful `add(reg_name, reg_addr, reg_permissions, reg_segments)` method for adding `Register` objects.\n\n```python\nfrom py_i2c_register.register import Register\ncontrols.add(\"REGISTER_NAME\", 0x00, Registers.READ + Register.WRITE, {})\n```\n\nThis would define a `Register` with the name `REGISTER_NAME`, the address `0x00` and the permission to read and write to/from it.\n\n## Adding RegisterSegments\nTo actually read or write to/from a `Register` you need to define at least one `RegisterSegment`. These describe how bits in registers map to sub values.\n\nOften devices pack different pieces of information in bits within bytes. For example a device could provide a health register where each bit represents a different system's health.\n\nYou define `RegisterSegment` objects by giving a name (for easy programmatic access) and the index of the segment's least and most significant bits.\n\nThe `RegisterList.add()` method returns the `Register` that it just created. You can then in turn use a similar helper method that `Register` provides called `add(seg_name, lsb_i, msb_i, default_bits)` to add a `RegisterSegment`:\n\n```python\ncontrols.add(\"HEALTH\", 0x00, Registers.READ, {})\\\n    .add(\"LEFT_MOTOR_FLAG\", 2, 2, [0])\\\n    .add(\"RIGHT_MOTOR_FLAG\", 1, 1, [0])\\\n    .add(\"NETWORK_FLAG\", 0, 0, [0])\n```\n\nThis would define a `Register` named `HEALTH` at address `0x00` with read permissions. This `Register` would have 3 `RegisterSegment` objects. These 3 register segments would look at bits 0, 1, and 2 for the status of the left and right motors as well as some made up network module.\n\n## Reading from RegisterSegments\nThe `RegisterList` provides some useful helper methods for reading `RegisterSegment` objects as integer values. They are called `to_int` and `to_twos_comp_int`. They both take the name of a `Register` and `RegisterSegment` to read. Optionally you can pass a `read_first` value. When `True` these methods will read the `Register` off the I2C device before returning the `RegisterSegment` value:\n\n```python\nnetwork_status = controls.to_int(\"HEALTH\", \"NETWORK_FLAG\", read_first=True)\nvelocity = controls.to_twos_comp_int(\"VELOCITY\", \"VELOCITY\", read_first=True)\n```\n\nThis would read the `NETWORK_FLAG` segment of the `HEALTH` register and the `VELOCITY` segment of the `VELOCITY` register.\n\nOntop of using `RegisterList` object helper methods one can access raw `RegisterSegment` values via the `RegisterSegment.bits` array. This array contains the raw `0` or `1` values of the register. Just be sure to call `Register.read` before accessing the `RegisterSegment.bits` array:\n\n```python\ncontrols.get(\"VELOCITY\").read()\nvelocity_bits = controls.get(\"VELOCITY\").get(\"VELOCITY\").bits\n```\n\n## Writing to RegisterSegments\nThe `RegisterList` class provides the `set_bits` and `set_bits_from_int` helper methods. Similar to the reading helper methods mentioned above `set_bits` and `set_bits_from_int` both also take a `Register` and `RegisterSegment` name as their first two parameters. The third value of both functions is the value to set. In the case of the `set_bits` method it is expected to be an array of bits to set. In the case of the `set_bits_from_int` method it is expected to be an integer value to set. The `set_bits` and `set_bits_from_int` methods also offer an optional `write_after` flag. If `True` they will write the value of the `Register` to the I2C device after the value has been set.\n\n```python\ncontrols.set_bits(\"ACQ_COMMAND\", \"ACQ_COMMAND\", [0, 0, 0, 0, 0, 1, 0, 0], write_after=True)\ncontrols.set_bits_from_int(\"ACQ_COMMAND\", \"ACQ_COMMAND\", 0x04, write_after=True)\n```\n\nThis would set the `ACQ_COMMAND` segment of the `ACQ_COMMAND` register to the value `0x04` using the `set_bits` and `set_bits_from_int` methods.\n\n# Writing Wrapper Classes\nI2C Register's simple architecture lends itself well to being used in hardware wrapper classes. All one must do is create a class with its own `RegisterList` instance. Then add `Register` and `RegisterSegment` definitions in the `__init__()` method:\n\n```python\nfrom py_i2c_register.register_list import RegisterList\nfrom py_i2c_register.register import Register\n\nclass LidarLiteV3():\n    # Register and Segment name constants\n    REG_ACQ_COMMAND = \"ACQ_COMMAND\"\n    SEG_ACQ_COMMAND = REG_ACQ_COMMAND\n\n    REG_STATUS = \"STATUS\"\n    SEG_PROC_ERROR_FLAG = \"PROC_ERROR_FLAG\"\n    SEG_HEALTH_FLAG = \"HEALTH_FLAG\"\n    SEG_SECONDARY_RET_FLAG = \"SECONDARY_RET_FLAG\"\n    SEG_INVALID_SIGNAL_FLAG = \"INVALID_SIGNAL_FLAG\"\n    SEG_SIGNAL_OVERFLOW_FLAG = \"SIGNAL_OVERFLOW_FLAG\"\n    SEG_REFERENCE_OVERFLOW_FLAG = \"REFERENCE_OVERFLOW_FLAG\"\n    SEG_BUSY_FLAG = \"BUSY_FLAG\"\n\n    REG_VELOCITY = \"VELOCITY\"\n    SEG_VELOCITY= REG_VELOCITY\n\n    REG_DISTANCE = \"DISTANCE\"\n    SEG_DISTANCE = REG_DISTANCE\n\n    def __init__(self):\n        # Create some device specific I2C Object\n        self.i2c = ...\n\n        # Configure control registers\n        self.controls = RegisterList(0x62, self.i2c, {})\n        self.controls.add(LightLiteV3.REG_ACQ_COMMAND, 0x00, Register.WRITE, {}) \\\n            .add(LightLiteV3.SEG_ACQ_COMMAND, 0, 7, [0] * 8)\n\n        self.controls.add(LightLiteV3.REG_STATUS, 0x01, Register.READ, {}) \\\n            .add(LightLiteV3.SEG_PROC_ERROR_FLAG, 6, 6, [0]) \\\n            .add(LightLiteV3.SEG_HEALTH_FLAG, 5, 5, [0]) \\\n            .add(LightLiteV3.SEG_SECONDARY_RET_FLAG, 4, 4, [0]) \\\n            .add(LightLiteV3.SEG_INVALID_SIGNAL_FLAG, 3, 3, [0]) \\\n            .add(LightLiteV3.SEG_SIGNAL_OVERFLOW_FLAG, 2, 2, [0]) \\\n            .add(LightLiteV3.SEG_REFERENCE_OVERFLOW_FLAG, 1, 1, [0]) \\\n            .add(LightLiteV3.SEG_BUSY_FLAG, 0, 0, [0])\n\n        self.controls.add(LightLiteV3.REG_VELOCITY, 0x09, Register.READ, {})\\\n            .add(LightLiteV3.SEG_VELOCITY, 0, 7, [0] * 8)\n\n        self.controls.add(LightLiteV3.REG_DISTANCE, 0x8f, Register.READ, {})\\\n            .add(LightLiteV3.SEG_DISTANCE, 0, 15, [0] * 16)\n\n    # Provide useful helper methods\n    def measure(self):\n        self.controls.set_bits_from_int(LidarLiteV3.REG_ACQ_COMMAND, LidarLiteV3.SEG_ACQ_COMMAND, 0x04, write_after=True)\n\n    def distance(self):\n        return self.controls.to_int(LidarLiteV3.REG_DISTANCE, LidarLiteV3.SEG_DISTANCE, read_first=True)\n\n    def velocity(self):\n        return self.controls.to_int(LidarLiteV3.REG_VELOCITY, LidarLiteV3.SEG_VELOCITY, read_first=True)\n\n# Now using your hardware has never been easier\nlidar = LidarLiteV3()\n\nwhile True:\n    lidar.measure()\n    print(\"Car is going {} m/s when it was {} m away\".format(lidar.velocity(), lidar.distance()))\n```\n\n# Development\nThe code for I2C Register is located in the `py_i2c_register` directory. Feel free to contribute by opening a pull request. I try to test and document as much as I can.\n\nThe code was written when Python 3.6 was recent (and Python 2.7 was supported and not deprecated).\n\n## Running Tests\nTo run tests a couple python packages are required. To install them you can run the `test-install` Make target:\n\n```bash\nmake test-install\n```\n\nYou can then run test by executing the `test` Make target:\n\n```bash\nmake test\n```\n\nTo see a more detailed HTML report you can run the `test-html` Make target. The results will then be saved to `htmlcov/index.html`.\n\n# Distribution\nThis repository provides a PIP package called `py-i2c-register`. To publish this distribution a variety of helpers are provided in the Makefile.\n\n## Setup\nThe [Pandoc](http://pandoc.org) tool is required for the release process along with some miscellaneous Python packages. Please refer to the [Pandoc Website](http://pandoc.org/installing.html) for installation instructions. You can install the misc. Python packages with the `dist-install` Make target:\n\n```bash\nmake dist-install\n```\n\nYou can verify that all distribution dependencies are install and accessible by running the `dist-check` Make target. If it exits successfully all dependencies were found.\n\nFinally you must create a `.pypirc` file in your home directory with the contents:\n\n```\n[distutils]\nindex-servers=pypi\n\n[pypi]\nrepository = https://upload.pypi.org/legacy/\nusername = Your Username\npassword = Your Password\n```\n\nThis gives the PyPi release tool some basic configuration options and your credentials.\n\n## Steps\nThis section details the steps required to release this package.\n\n1. Test\n    - Ensure that all tests pass by running the `test` Make target:\n\n    ```bash\n    make test\n    ```\n2. Clean and build\n    - Clean up previous distribution materials by running the `dist-clean` Make target:\n\n    ```bash\n    make dist-clean\n    ```\n    - Build the distribution by running the `dist-build` Make target:\n\n    ```bash\n    make dist-build\n    ```\n3. Upload\n    - Upload the distribution to PyPi by running the `dist-upload` Make target:\n\n    ```bash\n    make dist-upload\n    ```\n    - This requires that you have a `.pypirc` file setup with your username and password\n\nThe Makefile provides a useful target which runs steps 1 and 2 under one command named `dist`. However the upload step must still be completed separately.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoah-huppert%2Fpy-i2c-register","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoah-huppert%2Fpy-i2c-register","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoah-huppert%2Fpy-i2c-register/lists"}