OrangePi.ky040


NameOrangePi.ky040 JSON
Version 0.2.0 PyPI version JSON
download
home_pagehttps://github.com/sonocotta/orangepi-ky040
SummaryHigh-level interface for the KY040 rotary encoder and switch.
upload_time2023-05-27 21:08:43
maintainer
docs_urlNone
authorAndriy Malyshenko
requires_python
license
keywords keyes rotary encoder switch ky040
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # OrangePI-KY040

**High-level Python module for the KY040 rotary encoder and switch** on Raspberry Pi and similar boards that use `OPi.GPIO`

<img src="https://i.imgur.com/vgHjSoY.jpg" width="300" alt="KY-040 rotary encoder and switch">

## Features

- Increment callback
- Decrement callback
- Change callback (increment or decrement)
- Switch press callback

### Options

- Scale mode (internal counter is bound between X and Y, and is given as argument in the callback functions)
- Looped scale mode (from X to Y, then X again)
- Custom scale step
- GPIO polling (easier) or [as a device](#device-or-gpio-polling) (sturdier)

## Installation

```bash
pip install pyky040
```

<!--
## Usage

[![asciicast](https://asciinema.org/a/GVUyrqUUnZP4Sne8eEmKTWHCt.svg)](https://asciinema.org/a/GVUyrqUUnZP4Sne8eEmKTWHCt)
-->

### Basic

```python
# Import the module
from OrangePi_ky040 import pyky040

# Define your callback
def my_callback(scale_position):
    print('Hello world! The scale position is {}'.format(scale_position))

# Init the encoder pins
my_encoder = pyky040.Encoder(CLK=17, DT=18, SW=26)

# Or the encoder as a device (must be installed on the system beforehand!)
# my_encoder = pyky040.Encoder(device='/dev/input/event0')

# Setup the options and callbacks (see documentation)
my_encoder.setup(scale_min=0, scale_max=100, step=1, chg_callback=my_callback)

# Launch the listener
my_encoder.watch()

# Mess with the encoder...
# > Hello world! The scale position is 1
# > Hello world! The scale position is 2
# > Hello world! The scale position is 3
# > Hello world! The scale position is 2
# > Hello world! The scale position is 1
```

### In a thread

As the `watch()` method runs an infinite polling loop, you might want to run it in a thread if you don't want to block the rest of your script, or if you have **multiple encoders** to handle.

```python
# Import the module and threading
from OrangePi_ky040 import pyky040
import threading

# Define your callback
def my_callback(scale_position):
    print('Hello world! The scale position is {}'.format(scale_position))

# Init the encoder pins
my_encoder = pyky040.Encoder(CLK=17, DT=18, SW=26)

# Or the encoder as a device (must be installed on the system beforehand!)
# my_encoder = pyky040.Encoder(device='/dev/input/event0')

# Setup the options and callbacks (see documentation)
my_encoder.setup(scale_min=0, scale_max=100, step=1, chg_callback=my_callback)

# Create the thread
my_thread = threading.Thread(target=my_encoder.watch)

# Launch the thread
my_thread.start()

# Do other stuff
print('Other stuff...')
while True:
    print('Looped stuff...')
    sleep(1000)
# ... this is also where you can setup other encoders!

# Mess with the encoder...
# > Other stuff...
# > Looped stuff...
# > Hello world! The scale position is 1
# > Hello world! The scale position is 2
# > Hello world! The scale position is 3
# > Looped stuff...
# > Hello world! The scale position is 2

```

**Note:** The interruption of the module when running in threads is not yet handled, you might have to kill it by yourself 🔪

## Documentation

#### `Encoder(CLK=x, DT=y, SW=z)`

Initializes the module with the specified encoder pins.

- Options
  - `polling_interval` Specify the pins polling interval in ms (default 1ms)

#### `Encoder(device='...')`

⚠️ Linux only

Initializes the module with the specified encoder device. [Read more](#device-or-gpio-polling)

Requirement: `pip install pyky040[device]`

#### `Encoder.setup()`

Setup the behavior of the module. All of the following keyword arguments are optional.

- Callbacks
  - `inc_callback (function)` When the encoder is incremented (clockwise). Scale position as first argument.
  - `dec_callback (function)` When the encoder is decremented. Scale position as first argument.
  - `chg_callback (function)` When the encoder is either incremented or decremented. Scale position as first argument.
  - `sw_callback (function)` When the encoder switch is pressed

- Scale mode
  - `scale_min (int/float)` Scale minimum
  - `scale_max (int/float)` Scale maximum
  - `loop (boolean)` Loop mode (defaults to `False`)
  - `step (int/float)` Scale step when incrementing or decrementing

- Options
  - `sw_debounce_time (int/float)` Switch debounce time in ms (allow only one interrupt per X ms, dismiss others)

**Note:** better keep using ints and not floats for more precise results.

#### `Encoder.watch()`

Starts the listener. The pins polling interval is `1ms` by default and can be customized (see `Encoder()`).

## <a name="device-or-gpio-polling"></a>Should I use the GPIO polling or the device overlay?

The Raspberry Pi firmware allows the encoder to be set up as a device with the [`rotary-encoder` overlay](https://github.com/raspberrypi/firmware/blob/master/boot/overlays/README#L1892-L1921). It trades *the promise to catch every encoder tick* for *the ease of use* (because it needs to be installed on the host beforeheand, with root privileges).

|Approach|Plug & Play|Needs prior installation|Catches every tick|
|--------|-----------|------------------------|---------------------|
|GPIO polling|**Yes**|No|No|
|Device overlay|No|Yes|**Yes**|

### <a name="install-device"></a>How to install the encoder as a device?

Only tested on Raspbian Buster at this time.

```
# Copy this line in `/boot/config.txt` and reboot
# (replacing {CLK_PIN} and {DT_PIN} by their real values)
dtoverlay=rotary-encoder,pin_a={CLK_PIN},pin_b={DT_PIN},relative_axis=1,steps-per-period=2
```

## TROUBLESHOOTING

### Erratic behavior

It is known that some pins combinations introduce erratic behavior (interferences?). The library has been tested successfully using the following combinations (BCM numbering).

|CLK| DT| SW|       Pi|Raspbian|
|---|---|---|---------|--------|
| 26|  4| 21| 3B (1.2)|Buster  |

Feel free to edit the README to provide your working combinations!

If you are still experiencing issues, you might want to try to [set up the encoder as a device](#device-or-gpio-polling) instead.

## CHANGELOG

**0.1.4**

  - Added `device` mode
  - Added tests
 
**0.1.3**

  - Fixed `latest_switch_call` not defined before the loop

**0.1.2**

  - Changed `__init_` args to kwargs for better readability and ease of use `Encoder(CLK=x, DT=y, SW=z)`
  - Added customizable debounce time (in ms) for the switch `setup(..., sw_debounce_time=300)`
  - Added customizable polling interval (in ms) `Encoder(..., polling_interval=1)`

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/sonocotta/orangepi-ky040",
    "name": "OrangePi.ky040",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "keyes rotary encoder switch ky040",
    "author": "Andriy Malyshenko",
    "author_email": "anriy@sonocotta.com",
    "download_url": "https://files.pythonhosted.org/packages/75/50/b19e45ef9c6786285fc641d6ee1c384c95faa6e2a8aab7a3eb5758846a27/OrangePi.ky040-0.2.0.tar.gz",
    "platform": null,
    "description": "# OrangePI-KY040\n\n**High-level Python module for the KY040 rotary encoder and switch** on Raspberry Pi and similar boards that use `OPi.GPIO`\n\n<img src=\"https://i.imgur.com/vgHjSoY.jpg\" width=\"300\" alt=\"KY-040 rotary encoder and switch\">\n\n## Features\n\n- Increment callback\n- Decrement callback\n- Change callback (increment or decrement)\n- Switch press callback\n\n### Options\n\n- Scale mode (internal counter is bound between X and Y, and is given as argument in the callback functions)\n- Looped scale mode (from X to Y, then X again)\n- Custom scale step\n- GPIO polling (easier) or [as a device](#device-or-gpio-polling) (sturdier)\n\n## Installation\n\n```bash\npip install pyky040\n```\n\n<!--\n## Usage\n\n[![asciicast](https://asciinema.org/a/GVUyrqUUnZP4Sne8eEmKTWHCt.svg)](https://asciinema.org/a/GVUyrqUUnZP4Sne8eEmKTWHCt)\n-->\n\n### Basic\n\n```python\n# Import the module\nfrom OrangePi_ky040 import pyky040\n\n# Define your callback\ndef my_callback(scale_position):\n    print('Hello world! The scale position is {}'.format(scale_position))\n\n# Init the encoder pins\nmy_encoder = pyky040.Encoder(CLK=17, DT=18, SW=26)\n\n# Or the encoder as a device (must be installed on the system beforehand!)\n# my_encoder = pyky040.Encoder(device='/dev/input/event0')\n\n# Setup the options and callbacks (see documentation)\nmy_encoder.setup(scale_min=0, scale_max=100, step=1, chg_callback=my_callback)\n\n# Launch the listener\nmy_encoder.watch()\n\n# Mess with the encoder...\n# > Hello world! The scale position is 1\n# > Hello world! The scale position is 2\n# > Hello world! The scale position is 3\n# > Hello world! The scale position is 2\n# > Hello world! The scale position is 1\n```\n\n### In a thread\n\nAs the `watch()` method runs an infinite polling loop, you might want to run it in a thread if you don't want to block the rest of your script, or if you have **multiple encoders** to handle.\n\n```python\n# Import the module and threading\nfrom OrangePi_ky040 import pyky040\nimport threading\n\n# Define your callback\ndef my_callback(scale_position):\n    print('Hello world! The scale position is {}'.format(scale_position))\n\n# Init the encoder pins\nmy_encoder = pyky040.Encoder(CLK=17, DT=18, SW=26)\n\n# Or the encoder as a device (must be installed on the system beforehand!)\n# my_encoder = pyky040.Encoder(device='/dev/input/event0')\n\n# Setup the options and callbacks (see documentation)\nmy_encoder.setup(scale_min=0, scale_max=100, step=1, chg_callback=my_callback)\n\n# Create the thread\nmy_thread = threading.Thread(target=my_encoder.watch)\n\n# Launch the thread\nmy_thread.start()\n\n# Do other stuff\nprint('Other stuff...')\nwhile True:\n    print('Looped stuff...')\n    sleep(1000)\n# ... this is also where you can setup other encoders!\n\n# Mess with the encoder...\n# > Other stuff...\n# > Looped stuff...\n# > Hello world! The scale position is 1\n# > Hello world! The scale position is 2\n# > Hello world! The scale position is 3\n# > Looped stuff...\n# > Hello world! The scale position is 2\n\n```\n\n**Note:** The interruption of the module when running in threads is not yet handled, you might have to kill it by yourself \ud83d\udd2a\n\n## Documentation\n\n#### `Encoder(CLK=x, DT=y, SW=z)`\n\nInitializes the module with the specified encoder pins.\n\n- Options\n  - `polling_interval` Specify the pins polling interval in ms (default 1ms)\n\n#### `Encoder(device='...')`\n\n\u26a0\ufe0f Linux only\n\nInitializes the module with the specified encoder device. [Read more](#device-or-gpio-polling)\n\nRequirement: `pip install pyky040[device]`\n\n#### `Encoder.setup()`\n\nSetup the behavior of the module. All of the following keyword arguments are optional.\n\n- Callbacks\n  - `inc_callback (function)` When the encoder is incremented (clockwise). Scale position as first argument.\n  - `dec_callback (function)` When the encoder is decremented. Scale position as first argument.\n  - `chg_callback (function)` When the encoder is either incremented or decremented. Scale position as first argument.\n  - `sw_callback (function)` When the encoder switch is pressed\n\n- Scale mode\n  - `scale_min (int/float)` Scale minimum\n  - `scale_max (int/float)` Scale maximum\n  - `loop (boolean)` Loop mode (defaults to `False`)\n  - `step (int/float)` Scale step when incrementing or decrementing\n\n- Options\n  - `sw_debounce_time (int/float)` Switch debounce time in ms (allow only one interrupt per X ms, dismiss others)\n\n**Note:** better keep using ints and not floats for more precise results.\n\n#### `Encoder.watch()`\n\nStarts the listener. The pins polling interval is `1ms` by default and can be customized (see `Encoder()`).\n\n## <a name=\"device-or-gpio-polling\"></a>Should I use the GPIO polling or the device overlay?\n\nThe Raspberry Pi firmware allows the encoder to be set up as a device with the [`rotary-encoder` overlay](https://github.com/raspberrypi/firmware/blob/master/boot/overlays/README#L1892-L1921). It trades *the promise to catch every encoder tick* for *the ease of use* (because it needs to be installed on the host beforeheand, with root privileges).\n\n|Approach|Plug & Play|Needs prior installation|Catches every tick|\n|--------|-----------|------------------------|---------------------|\n|GPIO polling|**Yes**|No|No|\n|Device overlay|No|Yes|**Yes**|\n\n### <a name=\"install-device\"></a>How to install the encoder as a device?\n\nOnly tested on Raspbian Buster at this time.\n\n```\n# Copy this line in `/boot/config.txt` and reboot\n# (replacing {CLK_PIN} and {DT_PIN} by their real values)\ndtoverlay=rotary-encoder,pin_a={CLK_PIN},pin_b={DT_PIN},relative_axis=1,steps-per-period=2\n```\n\n## TROUBLESHOOTING\n\n### Erratic behavior\n\nIt is known that some pins combinations introduce erratic behavior (interferences?). The library has been tested successfully using the following combinations (BCM numbering).\n\n|CLK| DT| SW|       Pi|Raspbian|\n|---|---|---|---------|--------|\n| 26|  4| 21| 3B (1.2)|Buster  |\n\nFeel free to edit the README to provide your working combinations!\n\nIf you are still experiencing issues, you might want to try to [set up the encoder as a device](#device-or-gpio-polling) instead.\n\n## CHANGELOG\n\n**0.1.4**\n\n  - Added `device` mode\n  - Added tests\n \n**0.1.3**\n\n  - Fixed `latest_switch_call` not defined before the loop\n\n**0.1.2**\n\n  - Changed `__init_` args to kwargs for better readability and ease of use `Encoder(CLK=x, DT=y, SW=z)`\n  - Added customizable debounce time (in ms) for the switch `setup(..., sw_debounce_time=300)`\n  - Added customizable polling interval (in ms) `Encoder(..., polling_interval=1)`\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "High-level interface for the KY040 rotary encoder and switch.",
    "version": "0.2.0",
    "project_urls": {
        "Bug Reports": "https://github.com/sonocotta/orangepi-ky040/issues",
        "Homepage": "https://github.com/sonocotta/orangepi-ky040",
        "Source": "https://github.com/sonocotta/orangepi-ky040"
    },
    "split_keywords": [
        "keyes",
        "rotary",
        "encoder",
        "switch",
        "ky040"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7550b19e45ef9c6786285fc641d6ee1c384c95faa6e2a8aab7a3eb5758846a27",
                "md5": "83b7e1c06f76211f70c00c253b7721fd",
                "sha256": "54b1fe29f29a17d0e70b8d2afcb9cb06e0e5fc351650cf53db2e640a11053c5d"
            },
            "downloads": -1,
            "filename": "OrangePi.ky040-0.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "83b7e1c06f76211f70c00c253b7721fd",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 21264,
            "upload_time": "2023-05-27T21:08:43",
            "upload_time_iso_8601": "2023-05-27T21:08:43.818355Z",
            "url": "https://files.pythonhosted.org/packages/75/50/b19e45ef9c6786285fc641d6ee1c384c95faa6e2a8aab7a3eb5758846a27/OrangePi.ky040-0.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-05-27 21:08:43",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "sonocotta",
    "github_project": "orangepi-ky040",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "orangepi.ky040"
}
        
Elapsed time: 0.06756s