From d2b65a906cb6c682d1e136fdb0574f3d083620cb Mon Sep 17 00:00:00 2001 From: Jaimyn Mayer Date: Fri, 17 May 2024 02:28:20 +1000 Subject: [PATCH] added v1.0.0 --- .github/workflows/pypi-release.yml | 24 +++++++++ .gitignore | 2 +- README.md | 40 +++++++++++++- pyproject.toml | 21 ++++++++ requirements.txt | 1 + src/example.py | 15 ++++++ src/pywiegandpi/__init__.py | 83 ++++++++++++++++++++++++++++++ 7 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pypi-release.yml create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 src/example.py create mode 100644 src/pywiegandpi/__init__.py diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml new file mode 100644 index 0000000..3a62031 --- /dev/null +++ b/.github/workflows/pypi-release.yml @@ -0,0 +1,24 @@ +name: PyPi Release + +on: + workflow_dispatch: + push: + branches: + - "main" + +jobs: + pypi-publish: + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/pywiegandpi + permissions: + id-token: write + steps: + - name: Install dependencies + run: pip install build + - name: Build package + run: python -m build + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 68bc17f..2dc53ca 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ diff --git a/README.md b/README.md index 93510e7..2c93538 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,40 @@ # pywiegandpi -Read Wiegand data from Raspberry Pi GPIO pins. +Read Wiegand data from Raspberry Pi GPIO pins with a simple callback structure and automatic decoding. + +## Getting Started +Install pigpio if it's not already installed: +```bash +sudo apt install pigpio +``` +Enable the pigpio daemon: +```bash +sudo systemctl enable pigpiod +``` + +Install the required python packages by running the following command: +```bash +pip3 install -r requirements.txt +``` + +Use it like so: +```python +from pywiegandpi import WiegandDecoder + +data_0_pin = 6 +data_1_pin = 5 + + +def callback(value): + print("Got Wiegand data: {}".format(value)) + + +wiegand_reader = WiegandDecoder(data_0_pin, data_1_pin, callback) + +while True: + # do something else + pass + +``` + +## Acknowledgements +This library is based on the original example from [joan2937](https://github.com/joan2937/pigpio/tree/master/EXAMPLES/Python/WIEGAND_CODE). diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5a62917 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +[project] +name = "pywiegandpi" +version = "1.0.0" +dependencies = [ + "pigpio >= 1.78", +] +authors = [ + { name="Jaimyn Mayer", email="hello@jaimyn.dev" }, +] +description = "Automatically decode Wiegand data from Raspberry Pi GPIO pins." +readme = "README.md" +requires-python = ">=3.9" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", +] + +[project.urls] +Homepage = "https://github.com/membermatters/pywiegandpi" +Issues = "https://github.com/membermatters/pywiegandpi/issues" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ea02812 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pigpio \ No newline at end of file diff --git a/src/example.py b/src/example.py new file mode 100644 index 0000000..9df6403 --- /dev/null +++ b/src/example.py @@ -0,0 +1,15 @@ +from pywiegandpi import WiegandDecoder + +data_0_pin = 6 +data_1_pin = 5 + + +def callback(value): + print("Got Wiegand data: {}".format(value)) + + +wiegand_reader = WiegandDecoder(data_0_pin, data_1_pin, callback) + +while True: + # do something else + pass diff --git a/src/pywiegandpi/__init__.py b/src/pywiegandpi/__init__.py new file mode 100644 index 0000000..dffec7e --- /dev/null +++ b/src/pywiegandpi/__init__.py @@ -0,0 +1,83 @@ +import pigpio + +__version__ = "1.0.0" + +class WiegandDecoder: + def __init__(self, gpio_0, gpio_1, callback, bit_timeout=5, wiegand_32bit_mode=False, raw_mode=False): + """ + The callback is passed the code length in bits and the card uid/value. + """ + + self.pi = pigpio.pi() + self.gpio_0 = gpio_0 + self.gpio_1 = gpio_1 + self.raw_mode = raw_mode + self.wiegand_32bit_mode = wiegand_32bit_mode + + self.callback = callback + self.bit_timeout = bit_timeout + self.receiving_bits = False + + self.pi.set_mode(gpio_0, pigpio.INPUT) + self.pi.set_mode(gpio_1, pigpio.INPUT) + self.pi.set_pull_up_down(gpio_0, pigpio.PUD_UP) + self.pi.set_pull_up_down(gpio_1, pigpio.PUD_UP) + + self.cb_0 = self.pi.callback(gpio_0, pigpio.FALLING_EDGE, self._pin_change_callback) + self.cb_1 = self.pi.callback(gpio_1, pigpio.FALLING_EDGE, self._pin_change_callback) + + def _pin_change_callback(self, gpio, trigger, tick): + """ + Accumulate bits until both GPIO 0 and 1 timeout. + """ + + # if it was a falling or rising edge then accumulate the bit (this cb is also called on timeout) + if trigger in [pigpio.FALLING_EDGE, pigpio.RISING_EDGE]: + if not self.receiving_bits: + self.bits = 1 + self.num = 0 + + self.receiving_bits = True + self.receiving_bits_timeout = 0 + self.pi.set_watchdog(self.gpio_0, self.bit_timeout) + self.pi.set_watchdog(self.gpio_1, self.bit_timeout) + else: + self.bits += 1 + self.num = self.num << 1 + + if gpio == self.gpio_0: + self.receiving_bits_timeout = self.receiving_bits_timeout & 2 # clear gpio 0 timeout + else: + self.receiving_bits_timeout = self.receiving_bits_timeout & 1 # clear gpio 1 timeout + self.num = self.num | 1 + + else: + # if we aren't receiving bits, just continue + if self.receiving_bits: + if gpio == self.gpio_0: + self.receiving_bits_timeout = self.receiving_bits_timeout | 1 # timeout gpio 0 + else: + self.receiving_bits_timeout = self.receiving_bits_timeout | 2 # timeout gpio 1 + + if self.receiving_bits_timeout == 3: # both GPIO timed out + self.pi.set_watchdog(self.gpio_0, 0) + self.pi.set_watchdog(self.gpio_1, 0) + self.receiving_bits = False + + if self.raw_mode: + self.callback(self.bits, self.num) + else: + # get only the bits we're interested in + card_mask_26bit = 0b0_00000000_11111111_11111111_11111111_0 + card_mask_34bit = 0b0_11111111_11111111_11111111_11111111_0 + card_mask = card_mask_34bit if self.wiegand_32bit_mode else card_mask_26bit + card_uid = self.num & card_mask >> 1 # strip off the remaining parity bit + self.callback(self.bits, card_uid) + + def cancel(self): + """ + Cancel the Wiegand decoder. + """ + + self.cb_0.cancel() + self.cb_1.cancel()