From 611d955a74175e596a63e5f478f83d6b184cefa5 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 29 Jul 2024 13:44:03 -0400 Subject: [PATCH 1/9] initial commit with cookiecutter setup --- .../adafruit_circuitpython_pr.md | 13 + .github/workflows/build.yml | 14 + .github/workflows/failure-help-text.yml | 19 + .github/workflows/release_gh.yml | 19 + .github/workflows/release_pypi.yml | 19 + .pre-commit-config.yaml | 21 + .readthedocs.yaml | 19 + CODE_OF_CONDUCT.md | 158 +++++ LICENSE | 4 +- LICENSES/CC-BY-4.0.txt | 324 +++++++++ LICENSES/MIT.txt | 19 + LICENSES/Unlicense.txt | 20 + README.rst | 118 ++++ README.rst.license | 3 + adafruit_rfm/__init__.py | 0 adafruit_rfm/rfm69.py | 657 ++++++++++++++++++ adafruit_rfm/rfm9x.py | 539 ++++++++++++++ adafruit_rfm/rfm9xfsk.py | 578 +++++++++++++++ adafruit_rfm/rfm_common.py | 539 ++++++++++++++ docs/_static/favicon.ico | Bin 0 -> 4414 bytes docs/_static/favicon.ico.license | 3 + docs/api.rst | 8 + docs/api.rst.license | 4 + docs/conf.py | 186 +++++ docs/examples.rst | 8 + docs/examples.rst.license | 4 + docs/index.rst | 53 ++ docs/index.rst.license | 4 + docs/requirements.txt | 7 + examples/rfm69_node1.py | 64 ++ examples/rfm69_raw.py | 46 ++ examples/rfm69_rh_asyncio_node2.py | 109 +++ examples/rfm69_rh_header.py | 47 ++ examples/rfm69_rh_node1.py | 63 ++ examples/rfm69_rh_node1_ack.py | 71 ++ examples/rfm69_rh_node2.py | 67 ++ examples/rfm69_rh_node2_ack.py | 59 ++ examples/rfm69_rh_simpletest.py | 69 ++ examples/rfm69_rh_transmit.py | 62 ++ examples/rfm69_transmit.py | 76 ++ examples/rfm69ook_raw.py | 51 ++ examples/rfm69ook_transmit.py | 79 +++ examples/rfm9x_rh_asyncio_gps_node2.py | 161 +++++ examples/rfm9x_rh_asyncio_listen.py | 69 ++ examples/rfm9x_rh_asyncio_neopix_node2.py | 162 +++++ examples/rfm9x_rh_asyncio_node1.py | 103 +++ examples/rfm9x_rh_asyncio_node2.py | 108 +++ examples/rfm9x_rh_asyncio_raw.py | 87 +++ examples/rfm9x_rh_base.py | 45 ++ examples/rfm9x_rh_header.py | 40 ++ examples/rfm9x_rh_interrupt_node1.py | 87 +++ examples/rfm9x_rh_interrupt_node2.py | 87 +++ examples/rfm9x_rh_node1.py | 62 ++ examples/rfm9x_rh_node1_ack.py | 66 ++ examples/rfm9x_rh_node2.py | 63 ++ examples/rfm9x_rh_node2_ack.py | 54 ++ examples/rfm9x_rh_simpletest.py | 70 ++ examples/rfm9x_rh_transmit.py | 66 ++ examples/rfm9xfsk_node1.py | 66 ++ examples/rfm9xfsk_node2.py | 66 ++ examples/rfm9xfsk_raw.py | 39 ++ examples/rfm9xfsk_rh_asyncio_node1.py | 104 +++ examples/rfm9xfsk_rh_base.py | 46 ++ examples/rfm9xfsk_rh_header.py | 40 ++ examples/rfm9xfsk_rh_interrupt_node1.py | 89 +++ examples/rfm9xfsk_rh_interrupt_node2.py | 89 +++ examples/rfm9xfsk_rh_node1.py | 66 ++ examples/rfm9xfsk_rh_node1_ack.py | 67 ++ examples/rfm9xfsk_rh_node2.py | 66 ++ examples/rfm9xfsk_rh_node2_ack.py | 59 ++ examples/rfm9xfsk_rh_simpletest.py | 63 ++ examples/rfm9xfsk_rh_transmit.py | 65 ++ examples/rfm9xfsk_transmit.py | 80 +++ examples/rfm9xook_raw.py | 41 ++ examples/rfm9xook_transmit.py | 83 +++ optional_requirements.txt | 3 + pyproject.toml | 51 ++ requirements.txt | 8 + ruff.toml | 99 +++ 79 files changed, 6841 insertions(+), 2 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/failure-help-text.yml create mode 100644 .github/workflows/release_gh.yml create mode 100644 .github/workflows/release_pypi.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yaml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSES/CC-BY-4.0.txt create mode 100644 LICENSES/MIT.txt create mode 100644 LICENSES/Unlicense.txt create mode 100644 README.rst create mode 100644 README.rst.license create mode 100644 adafruit_rfm/__init__.py create mode 100644 adafruit_rfm/rfm69.py create mode 100644 adafruit_rfm/rfm9x.py create mode 100644 adafruit_rfm/rfm9xfsk.py create mode 100644 adafruit_rfm/rfm_common.py create mode 100644 docs/_static/favicon.ico create mode 100644 docs/_static/favicon.ico.license create mode 100644 docs/api.rst create mode 100644 docs/api.rst.license create mode 100644 docs/conf.py create mode 100644 docs/examples.rst create mode 100644 docs/examples.rst.license create mode 100644 docs/index.rst create mode 100644 docs/index.rst.license create mode 100644 docs/requirements.txt create mode 100644 examples/rfm69_node1.py create mode 100644 examples/rfm69_raw.py create mode 100644 examples/rfm69_rh_asyncio_node2.py create mode 100644 examples/rfm69_rh_header.py create mode 100644 examples/rfm69_rh_node1.py create mode 100644 examples/rfm69_rh_node1_ack.py create mode 100644 examples/rfm69_rh_node2.py create mode 100644 examples/rfm69_rh_node2_ack.py create mode 100644 examples/rfm69_rh_simpletest.py create mode 100644 examples/rfm69_rh_transmit.py create mode 100644 examples/rfm69_transmit.py create mode 100644 examples/rfm69ook_raw.py create mode 100644 examples/rfm69ook_transmit.py create mode 100644 examples/rfm9x_rh_asyncio_gps_node2.py create mode 100644 examples/rfm9x_rh_asyncio_listen.py create mode 100644 examples/rfm9x_rh_asyncio_neopix_node2.py create mode 100644 examples/rfm9x_rh_asyncio_node1.py create mode 100644 examples/rfm9x_rh_asyncio_node2.py create mode 100644 examples/rfm9x_rh_asyncio_raw.py create mode 100644 examples/rfm9x_rh_base.py create mode 100644 examples/rfm9x_rh_header.py create mode 100644 examples/rfm9x_rh_interrupt_node1.py create mode 100644 examples/rfm9x_rh_interrupt_node2.py create mode 100644 examples/rfm9x_rh_node1.py create mode 100644 examples/rfm9x_rh_node1_ack.py create mode 100644 examples/rfm9x_rh_node2.py create mode 100644 examples/rfm9x_rh_node2_ack.py create mode 100644 examples/rfm9x_rh_simpletest.py create mode 100644 examples/rfm9x_rh_transmit.py create mode 100644 examples/rfm9xfsk_node1.py create mode 100644 examples/rfm9xfsk_node2.py create mode 100644 examples/rfm9xfsk_raw.py create mode 100644 examples/rfm9xfsk_rh_asyncio_node1.py create mode 100644 examples/rfm9xfsk_rh_base.py create mode 100644 examples/rfm9xfsk_rh_header.py create mode 100644 examples/rfm9xfsk_rh_interrupt_node1.py create mode 100644 examples/rfm9xfsk_rh_interrupt_node2.py create mode 100644 examples/rfm9xfsk_rh_node1.py create mode 100644 examples/rfm9xfsk_rh_node1_ack.py create mode 100644 examples/rfm9xfsk_rh_node2.py create mode 100644 examples/rfm9xfsk_rh_node2_ack.py create mode 100644 examples/rfm9xfsk_rh_simpletest.py create mode 100644 examples/rfm9xfsk_rh_transmit.py create mode 100644 examples/rfm9xfsk_transmit.py create mode 100644 examples/rfm9xook_raw.py create mode 100644 examples/rfm9xook_transmit.py create mode 100644 optional_requirements.txt create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 ruff.toml diff --git a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md new file mode 100644 index 0000000..8de294e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2021 Adafruit Industries +# +# SPDX-License-Identifier: MIT + +Thank you for contributing! Before you submit a pull request, please read the following. + +Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://docs.circuitpython.org/en/latest/docs/design_guide.html + +If your changes are to documentation, please verify that the documentation builds locally by following the steps found here: https://adafru.it/build-docs + +Before submitting the pull request, make sure you've run Pylint and Black locally on your code. You can do this manually or using pre-commit. Instructions are available here: https://adafru.it/check-your-code + +Please remove all of this text before submitting. Include an explanation or list of changes included in your PR, as well as, if applicable, a link to any related issues. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..041a337 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Build CI + +on: [pull_request, push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main diff --git a/.github/workflows/failure-help-text.yml b/.github/workflows/failure-help-text.yml new file mode 100644 index 0000000..0b1194f --- /dev/null +++ b/.github/workflows/failure-help-text.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2021 Scott Shawcroft for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Failure help text + +on: + workflow_run: + workflows: ["Build CI"] + types: + - completed + +jobs: + post-help: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.event == 'pull_request' }} + steps: + - name: Post comment to help + uses: adafruit/circuitpython-action-library-ci-failed@v1 diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml new file mode 100644 index 0000000..9acec60 --- /dev/null +++ b/.github/workflows/release_gh.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: GitHub Release Actions + +on: + release: + types: [published] + +jobs: + upload-release-assets: + runs-on: ubuntu-latest + steps: + - name: Run GitHub Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-gh@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + upload-url: ${{ github.event.release.upload_url }} diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml new file mode 100644 index 0000000..c16b495 --- /dev/null +++ b/.github/workflows/release_pypi.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: PyPI Release Actions + +on: + release: + types: [published] + +jobs: + upload-release-assets: + runs-on: ubuntu-latest + steps: + - name: Run PyPI Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-pypi@main + with: + pypi-username: ${{ secrets.pypi_username }} + pypi-password: ${{ secrets.pypi_password }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ff19dde --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.4 + hooks: + - id: ruff-format + - id: ruff + args: ["--fix"] + - repo: https://github.com/fsfe/reuse-tool + rev: v3.0.1 + hooks: + - id: reuse diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..fcb7778 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +build: + os: ubuntu-latest + tools: + python: "3" + +python: + install: + - requirements: docs/requirements.txt + - requirements: requirements.txt diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a773adf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,158 @@ + +# Adafruit Community Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and leaders pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level or type of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +We are committed to providing a friendly, safe and welcoming environment for +all. + +Examples of behavior that contributes to creating and maintaining a positive environment +include: + +* Be kind and courteous to others +* Using welcoming and inclusive language +* Respecting the identity of every community member, including asking for their + pronouns if uncertain +* Being respectful of differing viewpoints and experiences +* Collaborating with other community members +* Providing desired assistance and knowledge to other community members +* Being open to new information and ideas +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by community members include: + +* The use of sexualized language or imagery and sexual attention or advances +* The use of inappropriate images, including in a community member's avatar +* The use of inappropriate language or profanity, including in a community member's nickname +* Any spamming, flaming, baiting or other attention-stealing behavior +* Excessive or unwelcome helping; answering outside the scope of the question + asked +* Discussion or promotion of activities or projects that intend or pose a risk of + significant harm +* Trolling, insulting/derogatory comments, and attacks of any nature (including, + but not limited to, personal or political attacks) +* Promoting or spreading disinformation, lies, or conspiracy theories against + a person, group, organisation, project, or community +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Engaging in behavior that creates an unwelcoming or uninclusive environment +* Other conduct which could reasonably be considered inappropriate + +The Adafruit Community welcomes everyone and strives to create a safe space for all. It is built +around sharing and contributing to technology. We encourage discussing your thoughts, experiences, +and feelings within the scope of the community. However, there are topics that can sometimes stray +from that scope, and can lead to hurting others and create an unwelcoming, uninclusive environment. + +Examples of discussion topics that have been known to stray outside the scope of the Adafruit +Community include, but are not limited to: + +* Discussions regarding religion and related topics +* Discussions regarding politics and related topics + +The goal of the standards and moderation guidelines outlined here is to build +and maintain a respectful community. We ask that you don’t just aim to be +"technically unimpeachable", but rather try to be your best self. + +We value many things beyond technical expertise, including collaboration and +supporting others within our community. Providing a positive experience for +other community members can have a much more significant impact than simply +providing the correct answer. + +## Our Responsibilities + +Project leaders are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project leaders have the right and responsibility to remove, edit, or +reject messages, comments, commits, code, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any community member for other behaviors that they deem +inappropriate, threatening, offensive, or harmful. + +## Moderation + +Instances of behaviors that violate the Adafruit Community Code of Conduct +may be reported by any member of the community. Community members are +encouraged to report these situations, including situations they witness +involving other community members. + +You may report in the following ways: + +In any situation, you may email . + +On the Adafruit Discord, you may send an open message from any channel +to all Community Moderators by tagging @community moderators. You may +also send an open message from the #help-with-community channel, or a +direct message to any Community Moderator. + +The source of email and direct message reports will be kept confidential. + +In situations on Discord where the issue is particularly offensive, possibly +illegal, requires immediate action, or violates the Discord terms of service, +you should also report the message directly to [Discord](https://discord.com/safety). + +These are the steps for upholding our community’s standards of conduct. + +1. Any member of the community may report any situation that violates the + Adafruit Community Code of Conduct. All reports will be reviewed and + investigated. +2. If the behavior is a severe violation, the community member who + committed the violation may be banned immediately, without warning. +3. Otherwise, moderators will first respond to such behavior with a warning. +4. Moderators follow a soft "three strikes" policy - the community member may + be given another chance, if they are receptive to the warning and change their + behavior. +5. If the community member is unreceptive or unreasonable when warned by a + moderator, or the warning goes unheeded, they may be banned for a first or + second offense. Repeated offenses will result in the community member being + banned. +6. Disciplinary actions (warnings, bans, etc) for Code of Conduct violations apply + to the platform where the violation occurred. However, depending on the severity + of the violation, the disciplinary action may be applied across Adafruit's other + community platforms. For example, a severe violation on the Adafruit Discord + server may result in a ban on not only the Adafruit Discord server, but also on + the Adafruit GitHub organisation, Adafruit Forums, Adafruit Twitter, etc. + +## Scope + +This Code of Conduct and the enforcement policies listed above apply to all +Adafruit Community venues. This includes but is not limited to any community +spaces (both public and private), the entire Adafruit Discord server, and +Adafruit GitHub repositories. Examples of Adafruit Community spaces include +but are not limited to meet-ups, audio chats on the Adafruit Discord, or +interaction at a conference. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. As a community +member, you are representing our community, and are expected to behave +accordingly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), +version 1.4, available on [contributor-covenant.org](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html), +and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). + +For other projects adopting the Adafruit Community Code of +Conduct, please contact the maintainers of those projects for enforcement. +If you wish to use this code of conduct for your own project, consider +explicitly mentioning your moderation policy or making a copy with your +own moderation policy so as to avoid confusion. diff --git a/LICENSE b/LICENSE index 20fa55e..d70326e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -MIT License +The MIT License (MIT) -Copyright (c) 2024 Adafruit Industries +Copyright (c) 2024 Jerry Needell for Adafruit Industries Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 0000000..3f92dfc --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,324 @@ +Creative Commons Attribution 4.0 International Creative Commons Corporation +("Creative Commons") is not a law firm and does not provide legal services +or legal advice. Distribution of Creative Commons public licenses does not +create a lawyer-client or other relationship. Creative Commons makes its licenses +and related information available on an "as-is" basis. Creative Commons gives +no warranties regarding its licenses, any material licensed under their terms +and conditions, or any related information. Creative Commons disclaims all +liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions +that creators and other rights holders may use to share original works of +authorship and other material subject to copyright and certain other rights +specified in the public license below. The following considerations are for +informational purposes only, are not exhaustive, and do not form part of our +licenses. + +Considerations for licensors: Our public licenses are intended for use by +those authorized to give the public permission to use material in ways otherwise +restricted by copyright and certain other rights. Our licenses are irrevocable. +Licensors should read and understand the terms and conditions of the license +they choose before applying it. Licensors should also secure all rights necessary +before applying our licenses so that the public can reuse the material as +expected. Licensors should clearly mark any material not subject to the license. +This includes other CC-licensed material, or material used under an exception +or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors + +Considerations for the public: By using one of our public licenses, a licensor +grants the public permission to use the licensed material under specified +terms and conditions. If the licensor's permission is not necessary for any +reason–for example, because of any applicable exception or limitation to copyright–then +that use is not regulated by the license. Our licenses grant only permissions +under copyright and certain other rights that a licensor has authority to +grant. Use of the licensed material may still be restricted for other reasons, +including because others have copyright or other rights in the material. A +licensor may make special requests, such as asking that all changes be marked +or described. Although not required by our licenses, you are encouraged to +respect those requests where reasonable. More considerations for the public +: wiki.creativecommons.org/Considerations_for_licensees Creative Commons Attribution +4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to +be bound by the terms and conditions of this Creative Commons Attribution +4.0 International Public License ("Public License"). To the extent this Public +License may be interpreted as a contract, You are granted the Licensed Rights +in consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the Licensor +receives from making the Licensed Material available under these terms and +conditions. + +Section 1 – Definitions. + +a. Adapted Material means material subject to Copyright and Similar Rights +that is derived from or based upon the Licensed Material and in which the +Licensed Material is translated, altered, arranged, transformed, or otherwise +modified in a manner requiring permission under the Copyright and Similar +Rights held by the Licensor. For purposes of this Public License, where the +Licensed Material is a musical work, performance, or sound recording, Adapted +Material is always produced where the Licensed Material is synched in timed +relation with a moving image. + +b. Adapter's License means the license You apply to Your Copyright and Similar +Rights in Your contributions to Adapted Material in accordance with the terms +and conditions of this Public License. + +c. Copyright and Similar Rights means copyright and/or similar rights closely +related to copyright including, without limitation, performance, broadcast, +sound recording, and Sui Generis Database Rights, without regard to how the +rights are labeled or categorized. For purposes of this Public License, the +rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + +d. Effective Technological Measures means those measures that, in the absence +of proper authority, may not be circumvented under laws fulfilling obligations +under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, +and/or similar international agreements. + +e. Exceptions and Limitations means fair use, fair dealing, and/or any other +exception or limitation to Copyright and Similar Rights that applies to Your +use of the Licensed Material. + +f. Licensed Material means the artistic or literary work, database, or other +material to which the Licensor applied this Public License. + +g. Licensed Rights means the rights granted to You subject to the terms and +conditions of this Public License, which are limited to all Copyright and +Similar Rights that apply to Your use of the Licensed Material and that the +Licensor has authority to license. + +h. Licensor means the individual(s) or entity(ies) granting rights under this +Public License. + +i. Share means to provide material to the public by any means or process that +requires permission under the Licensed Rights, such as reproduction, public +display, public performance, distribution, dissemination, communication, or +importation, and to make material available to the public including in ways +that members of the public may access the material from a place and at a time +individually chosen by them. + +j. Sui Generis Database Rights means rights other than copyright resulting +from Directive 96/9/EC of the European Parliament and of the Council of 11 +March 1996 on the legal protection of databases, as amended and/or succeeded, +as well as other essentially equivalent rights anywhere in the world. + +k. You means the individual or entity exercising the Licensed Rights under +this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + +1. Subject to the terms and conditions of this Public License, the Licensor +hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, +irrevocable license to exercise the Licensed Rights in the Licensed Material +to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + +2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions +and Limitations apply to Your use, this Public License does not apply, and +You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + +4. Media and formats; technical modifications allowed. The Licensor authorizes +You to exercise the Licensed Rights in all media and formats whether now known +or hereafter created, and to make technical modifications necessary to do +so. The Licensor waives and/or agrees not to assert any right or authority +to forbid You from making technical modifications necessary to exercise the +Licensed Rights, including technical modifications necessary to circumvent +Effective Technological Measures. For purposes of this Public License, simply +making modifications authorized by this Section 2(a)(4) never produces Adapted +Material. + + 5. Downstream recipients. + +A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed +Material automatically receives an offer from the Licensor to exercise the +Licensed Rights under the terms and conditions of this Public License. + +B. No downstream restrictions. You may not offer or impose any additional +or different terms or conditions on, or apply any Effective Technological +Measures to, the Licensed Material if doing so restricts exercise of the Licensed +Rights by any recipient of the Licensed Material. + +6. No endorsement. Nothing in this Public License constitutes or may be construed +as permission to assert or imply that You are, or that Your use of the Licensed +Material is, connected with, or sponsored, endorsed, or granted official status +by, the Licensor or others designated to receive attribution as provided in +Section 3(a)(1)(A)(i). + + b. Other rights. + +1. Moral rights, such as the right of integrity, are not licensed under this +Public License, nor are publicity, privacy, and/or other similar personality +rights; however, to the extent possible, the Licensor waives and/or agrees +not to assert any such rights held by the Licensor to the limited extent necessary +to allow You to exercise the Licensed Rights, but not otherwise. + +2. Patent and trademark rights are not licensed under this Public License. + +3. To the extent possible, the Licensor waives any right to collect royalties +from You for the exercise of the Licensed Rights, whether directly or through +a collecting society under any voluntary or waivable statutory or compulsory +licensing scheme. In all other cases the Licensor expressly reserves any right +to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following +conditions. + + a. Attribution. + +1. If You Share the Licensed Material (including in modified form), You must: + +A. retain the following if it is supplied by the Licensor with the Licensed +Material: + +i. identification of the creator(s) of the Licensed Material and any others +designated to receive attribution, in any reasonable manner requested by the +Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + +v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + +B. indicate if You modified the Licensed Material and retain an indication +of any previous modifications; and + +C. indicate the Licensed Material is licensed under this Public License, and +include the text of, or the URI or hyperlink to, this Public License. + +2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner +based on the medium, means, and context in which You Share the Licensed Material. +For example, it may be reasonable to satisfy the conditions by providing a +URI or hyperlink to a resource that includes the required information. + +3. If requested by the Licensor, You must remove any of the information required +by Section 3(a)(1)(A) to the extent reasonably practicable. + +4. If You Share Adapted Material You produce, the Adapter's License You apply +must not prevent recipients of the Adapted Material from complying with this +Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to +Your use of the Licensed Material: + +a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, +reuse, reproduce, and Share all or a substantial portion of the contents of +the database; + +b. if You include all or a substantial portion of the database contents in +a database in which You have Sui Generis Database Rights, then the database +in which You have Sui Generis Database Rights (but not its individual contents) +is Adapted Material; and + +c. You must comply with the conditions in Section 3(a) if You Share all or +a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not replace +Your obligations under this Public License where the Licensed Rights include +other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + +a. Unless otherwise separately undertaken by the Licensor, to the extent possible, +the Licensor offers the Licensed Material as-is and as-available, and makes +no representations or warranties of any kind concerning the Licensed Material, +whether express, implied, statutory, or other. This includes, without limitation, +warranties of title, merchantability, fitness for a particular purpose, non-infringement, +absence of latent or other defects, accuracy, or the presence or absence of +errors, whether or not known or discoverable. Where disclaimers of warranties +are not allowed in full or in part, this disclaimer may not apply to You. + +b. To the extent possible, in no event will the Licensor be liable to You +on any legal theory (including, without limitation, negligence) or otherwise +for any direct, special, indirect, incidental, consequential, punitive, exemplary, +or other losses, costs, expenses, or damages arising out of this Public License +or use of the Licensed Material, even if the Licensor has been advised of +the possibility of such losses, costs, expenses, or damages. Where a limitation +of liability is not allowed in full or in part, this limitation may not apply +to You. + +c. The disclaimer of warranties and limitation of liability provided above +shall be interpreted in a manner that, to the extent possible, most closely +approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + +a. This Public License applies for the term of the Copyright and Similar Rights +licensed here. However, if You fail to comply with this Public License, then +Your rights under this Public License terminate automatically. + +b. Where Your right to use the Licensed Material has terminated under Section +6(a), it reinstates: + +1. automatically as of the date the violation is cured, provided it is cured +within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + +c. For the avoidance of doubt, this Section 6(b) does not affect any right +the Licensor may have to seek remedies for Your violations of this Public +License. + +d. For the avoidance of doubt, the Licensor may also offer the Licensed Material +under separate terms or conditions or stop distributing the Licensed Material +at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + +a. The Licensor shall not be bound by any additional or different terms or +conditions communicated by You unless expressly agreed. + +b. Any arrangements, understandings, or agreements regarding the Licensed +Material not stated herein are separate from and independent of the terms +and conditions of this Public License. + +Section 8 – Interpretation. + +a. For the avoidance of doubt, this Public License does not, and shall not +be interpreted to, reduce, limit, restrict, or impose conditions on any use +of the Licensed Material that could lawfully be made without permission under +this Public License. + +b. To the extent possible, if any provision of this Public License is deemed +unenforceable, it shall be automatically reformed to the minimum extent necessary +to make it enforceable. If the provision cannot be reformed, it shall be severed +from this Public License without affecting the enforceability of the remaining +terms and conditions. + +c. No term or condition of this Public License will be waived and no failure +to comply consented to unless expressly agreed to by the Licensor. + +d. Nothing in this Public License constitutes or may be interpreted as a limitation +upon, or waiver of, any privileges and immunities that apply to the Licensor +or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative +Commons may elect to apply one of its public licenses to material it publishes +and in those instances will be considered the "Licensor." The text of the +Creative Commons public licenses is dedicated to the public domain under the +CC0 Public Domain Dedication. Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as otherwise +permitted by the Creative Commons policies published at creativecommons.org/policies, +Creative Commons does not authorize the use of the trademark "Creative Commons" +or any other trademark or logo of Creative Commons without its prior written +consent including, without limitation, in connection with any unauthorized +modifications to any of its public licenses or any other arrangements, understandings, +or agreements concerning use of licensed material. For the avoidance of doubt, +this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 0000000..204b93d --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSES/Unlicense.txt b/LICENSES/Unlicense.txt new file mode 100644 index 0000000..24a8f90 --- /dev/null +++ b/LICENSES/Unlicense.txt @@ -0,0 +1,20 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute +this software, either in source code form or as a compiled binary, for any +purpose, commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and +to the detriment of our heirs and successors. We intend this dedication to +be an overt act of relinquishment in perpetuity of all present and future +rights to this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, +please refer to diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..1468746 --- /dev/null +++ b/README.rst @@ -0,0 +1,118 @@ +Introduction +============ + + +.. image:: https://readthedocs.org/projects/adafruit-circuitpython-rfm/badge/?version=latest + :target: https://docs.circuitpython.org/projects/rfm/en/latest/ + :alt: Documentation Status + + +.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg + :target: https://adafru.it/discord + :alt: Discord + + +.. image:: https://github.com/jerryneedell/Adafruit_CircuitPython_RFM/workflows/Build%20CI/badge.svg + :target: https://github.com/jerryneedell/Adafruit_CircuitPython_RFM/actions + :alt: Build Status + + +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Code Style: Ruff + +Support for RFM69 and RFM9x modules + + +Dependencies +============= +This driver depends on: + +* `Adafruit CircuitPython `_ +* `Bus Device `_ + +Please ensure all dependencies are available on the CircuitPython filesystem. +This is easily achieved by downloading +`the Adafruit library and driver bundle `_ +or individual libraries can be installed using +`circup `_. + + + +.. todo:: Describe the Adafruit product this library works with. For PCBs, you can also add the +image from the assets folder in the PCB's GitHub repo. + +`Purchase one from the Adafruit shop `_ + +Installing from PyPI +===================== +.. note:: This library is not available on PyPI yet. Install documentation is included + as a standard element. Stay tuned for PyPI availability! + +.. todo:: Remove the above note if PyPI version is/will be available at time of release. + +On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from +PyPI `_. +To install for current user: + +.. code-block:: shell + + pip3 install adafruit-circuitpython-rfm + +To install system-wide (this may be required in some cases): + +.. code-block:: shell + + sudo pip3 install adafruit-circuitpython-rfm + +To install in a virtual environment in your current project: + +.. code-block:: shell + + mkdir project-name && cd project-name + python3 -m venv .venv + source .env/bin/activate + pip3 install adafruit-circuitpython-rfm + +Installing to a Connected CircuitPython Device with Circup +========================================================== + +Make sure that you have ``circup`` installed in your Python environment. +Install it with the following command if necessary: + +.. code-block:: shell + + pip3 install circup + +With ``circup`` installed and your CircuitPython device connected use the +following command to install: + +.. code-block:: shell + + circup install adafruit_rfm + +Or the following command to update an existing version: + +.. code-block:: shell + + circup update + +Usage Example +============= + +.. todo:: Add a quick, simple example. It and other examples should live in the +examples folder and be included in docs/examples.rst. + +Documentation +============= +API documentation for this library can be found on `Read the Docs `_. + +For information on building library documentation, please check out +`this guide `_. + +Contributing +============ + +Contributions are welcome! Please read our `Code of Conduct +`_ +before contributing to help this project stay welcoming. diff --git a/README.rst.license b/README.rst.license new file mode 100644 index 0000000..532888d --- /dev/null +++ b/README.rst.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +SPDX-FileCopyrightText: Copyright (c) 2024 Jerry Needell for Adafruit Industries +SPDX-License-Identifier: MIT diff --git a/adafruit_rfm/__init__.py b/adafruit_rfm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/adafruit_rfm/rfm69.py b/adafruit_rfm/rfm69.py new file mode 100644 index 0000000..ab110ae --- /dev/null +++ b/adafruit_rfm/rfm69.py @@ -0,0 +1,657 @@ +# SPDX-FileCopyrightText: 2024 Jerry Needell for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_rfm69` +==================================================== + +CircuitPython RFM69 packet radio module. This supports basic RadioHead-compatible sending and +receiving of packets with RFM69 series radios (433/915Mhz). + +.. warning:: This is NOT for LoRa radios! + +.. note:: This is a 'best effort' at receiving data using pure Python code--there is not interrupt + support so you might lose packets if they're sent too quickly for the board to process them. + You will have the most luck using this in simple low bandwidth scenarios like sending and + receiving a 60 byte packet at a time--don't try to receive many kilobytes of data at a time! + +* Author(s): Tony DiCola, Jerry Needell +""" + +import time + +from micropython import const + +from adafruit_rfm.rfm_common import RFMSPI, ticks_diff + +HAS_SUPERVISOR = False + +try: + import supervisor + + if hasattr(supervisor, "ticks_ms"): + HAS_SUPERVISOR = True +except ImportError: + pass + + +try: + from typing import Optional + + import busio + import digitalio + +except ImportError: + pass + + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/jerryneedell/CircuitPython_RFM.git" + + +# Internal constants: +_RF69_REG_00_FIFO = const(0x00) +_RF69_REG_01_OP_MODE = const(0x01) +_RF69_REG_02_DATA_MOD = const(0x02) +_RF69_REG_03_BITRATE_MSB = const(0x03) +_RF69_REG_04_BITRATE_LSB = const(0x04) +_RF69_REG_05_FDEV_MSB = const(0x05) +_RF69_REG_06_FDEV_LSB = const(0x06) +_RF69_REG_07_FRF_MSB = const(0x07) +_RF69_REG_08_FRF_MID = const(0x08) +_RF69_REG_09_FRF_LSB = const(0x09) +_RF69_REG_10_VERSION = const(0x10) +_RF69_REG_11_PA_LEVEL = const(0x11) +_RF69_REG_13_OCP = const(0x13) +_RF69_REG_19_RX_BW = const(0x19) +_RF69_REG_1A_AFC_BW = const(0x1A) +_RF69_REG_1B_OOK_PEAK = const(0x1B) +_RF69_REG_1C_OOK_AVG = const(0x1C) +_RF69_REG_1D_OOK_FIX = const(0x1D) +_RF69_REG_24_RSSI_VALUE = const(0x24) +_RF69_REG_25_DIO_MAPPING1 = const(0x25) +_RF69_REG_27_IRQ_FLAGS1 = const(0x27) +_RF69_REG_28_IRQ_FLAGS2 = const(0x28) +_RF69_REG_2C_PREAMBLE_MSB = const(0x2C) +_RF69_REG_2D_PREAMBLE_LSB = const(0x2D) +_RF69_REG_2E_SYNC_CONFIG = const(0x2E) +_RF69_REG_2F_SYNC_VALUE1 = const(0x2F) +_RF69_REG_39_NODE_ADDR = const(0x39) +_RF69_REG_3A_BROADCAST_ADDR = const(0x3A) +_RF69_REG_37_PACKET_CONFIG1 = const(0x37) +_RF69_REG_3C_FIFO_THRESH = const(0x3C) +_RF69_REG_3D_PACKET_CONFIG2 = const(0x3D) +_RF69_REG_3E_AES_KEY1 = const(0x3E) +_RF69_REG_4E_TEMP1 = const(0x4E) +_RF69_REG_4F_TEMP2 = const(0x4F) +_RF69_REG_5A_TEST_PA1 = const(0x5A) +_RF69_REG_5C_TEST_PA2 = const(0x5C) +_RF69_REG_6F_TEST_DAGC = const(0x6F) + +_TEST_PA1_NORMAL = const(0x55) +_TEST_PA1_BOOST = const(0x5D) +_TEST_PA2_NORMAL = const(0x70) +_TEST_PA2_BOOST = const(0x7C) +_OCP_NORMAL = const(0x1A) +_OCP_HIGH_POWER = const(0x0F) + +# The crystal oscillator frequency and frequency synthesizer step size. +# See the datasheet for details of this calculation. +_FXOSC = 32000000.0 +_FSTEP = _FXOSC / 524288 + +# RadioHead specific compatibility constants. +_RH_BROADCAST_ADDRESS = const(0xFF) +# The acknowledgement bit in the FLAGS +# The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved +# for application layer use. +_RH_FLAGS_ACK = const(0x80) +_RH_FLAGS_RETRY = const(0x40) + +# User facing constants: +SLEEP_MODE = 0b000 +STANDBY_MODE = 0b001 +FS_MODE = 0b010 +TX_MODE = 0b011 +RX_MODE = 0b100 + + +# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-public-methods +class RFM69(RFMSPI): + """Interface to a RFM69 series packet radio. Allows simple sending and + receiving of wireless data at supported frequencies of the radio + (433/915mhz). + + :param busio.SPI spi: The SPI bus connected to the chip. Ensure SCK, MOSI, and MISO are + connected. + :param ~digitalio.DigitalInOut cs: A DigitalInOut object connected to the chip's CS/chip select + line. + :param ~digitalio.DigitalInOut reset: A DigitalInOut object connected to the chip's RST/reset + line. + :param int frequency: The center frequency to configure for radio transmission and reception. + Must be a frequency supported by your hardware (i.e. either 433 or 915mhz). + :param bytes sync_word: A byte string up to 8 bytes long which represents the syncronization + word used by received and transmitted packets. Read the datasheet for a full understanding + of this value! However by default the library will set a value that matches the RadioHead + Arduino library. + :param int preamble_length: The number of bytes to pre-pend to a data packet as a preamble. + This is by default 4 to match the RadioHead library. + :param bytes encryption_key: A 16 byte long string that represents the AES encryption key to use + when encrypting and decrypting packets. Both the transmitter and receiver MUST have the + same key value! By default no encryption key is set or used. + :param bool high_power: Indicate if the chip is a high power variant that supports boosted + transmission power. The default is True as it supports the common RFM69HCW modules sold by + Adafruit. + + .. note:: The D0/interrupt line is currently unused by this module and can remain unconnected. + + Remember this library makes a best effort at receiving packets with pure Python code. Trying + to receive packets too quickly will result in lost data so limit yourself to simple scenarios + of sending and receiving single packets at a time. + + Also note this library tries to be compatible with raw RadioHead Arduino library communication. + This means the library sets up the radio modulation to match RadioHead's default of GFSK + encoding, 250kbit/s bitrate, and 250khz frequency deviation. To change this requires explicitly + setting the radio's bitrate and encoding register bits. Read the datasheet and study the init + function to see an example of this--advanced users only! Advanced RadioHead features like + address/node specific packets or "reliable datagram" delivery are supported however due to the + limitations noted, "reliable datagram" is still subject to missed packets but with it, the + sender is notified if a packe has potentially been missed. + """ + + # Control bits from the registers of the chip: + data_mode = RFMSPI.RegisterBits(_RF69_REG_02_DATA_MOD, offset=5, bits=2) + modulation_type = RFMSPI.RegisterBits(_RF69_REG_02_DATA_MOD, offset=3, bits=2) + modulation_shaping = RFMSPI.RegisterBits(_RF69_REG_02_DATA_MOD, offset=0, bits=2) + temp_start = RFMSPI.RegisterBits(_RF69_REG_4E_TEMP1, offset=3) + temp_running = RFMSPI.RegisterBits(_RF69_REG_4E_TEMP1, offset=2) + sync_on = RFMSPI.RegisterBits(_RF69_REG_2E_SYNC_CONFIG, offset=7) + sync_size = RFMSPI.RegisterBits(_RF69_REG_2E_SYNC_CONFIG, offset=3, bits=3) + aes_on = RFMSPI.RegisterBits(_RF69_REG_3D_PACKET_CONFIG2, offset=0) + pa_0_on = RFMSPI.RegisterBits(_RF69_REG_11_PA_LEVEL, offset=7) + pa_1_on = RFMSPI.RegisterBits(_RF69_REG_11_PA_LEVEL, offset=6) + pa_2_on = RFMSPI.RegisterBits(_RF69_REG_11_PA_LEVEL, offset=5) + output_power = RFMSPI.RegisterBits(_RF69_REG_11_PA_LEVEL, offset=0, bits=5) + rx_bw_dcc_freq = RFMSPI.RegisterBits(_RF69_REG_19_RX_BW, offset=5, bits=3) + rx_bw_mantissa = RFMSPI.RegisterBits(_RF69_REG_19_RX_BW, offset=3, bits=2) + rx_bw_exponent = RFMSPI.RegisterBits(_RF69_REG_19_RX_BW, offset=0, bits=3) + afc_bw_dcc_freq = RFMSPI.RegisterBits(_RF69_REG_1A_AFC_BW, offset=5, bits=3) + afc_bw_mantissa = RFMSPI.RegisterBits(_RF69_REG_1A_AFC_BW, offset=3, bits=2) + afc_bw_exponent = RFMSPI.RegisterBits(_RF69_REG_1A_AFC_BW, offset=0, bits=3) + packet_format = RFMSPI.RegisterBits(_RF69_REG_37_PACKET_CONFIG1, offset=7, bits=1) + dc_free = RFMSPI.RegisterBits(_RF69_REG_37_PACKET_CONFIG1, offset=5, bits=2) + crc_on = RFMSPI.RegisterBits(_RF69_REG_37_PACKET_CONFIG1, offset=4, bits=1) + crc_auto_clear_off = RFMSPI.RegisterBits(_RF69_REG_37_PACKET_CONFIG1, offset=3, bits=1) + address_filter = RFMSPI.RegisterBits(_RF69_REG_37_PACKET_CONFIG1, offset=1, bits=2) + mode_ready = RFMSPI.RegisterBits(_RF69_REG_27_IRQ_FLAGS1, offset=7) + dio_0_mapping = RFMSPI.RegisterBits(_RF69_REG_25_DIO_MAPPING1, offset=6, bits=2) + ook_thresh_type = RFMSPI.RegisterBits(_RF69_REG_1B_OOK_PEAK, offset=6, bits=2) + ook_thresh_step = RFMSPI.RegisterBits(_RF69_REG_1B_OOK_PEAK, offset=5, bits=3) + ook_peak_thresh_dec = RFMSPI.RegisterBits(_RF69_REG_1B_OOK_PEAK, offset=0, bits=3) + ook_average_thresh_filt = RFMSPI.RegisterBits(_RF69_REG_1C_OOK_AVG, offset=6, bits=2) + + # pylint: disable=too-many-statements + # pylint: disable=too-many-arguments + def __init__( # noqa: PLR0913 + self, + spi: busio.SPI, + cs: digitalio.DigitalInOut, + rst: digitalio.DigitalInOut, + frequency: int, + *, + sync_word: bytes = b"\x2d\xd4", + preamble_length: int = 4, + encryption_key: Optional[bytes] = None, + high_power: bool = True, + baudrate: int = 2000000, + crc: bool = True, + ) -> None: + super().__init__(spi, cs, baudrate=baudrate) + + self.module = "RFM69" + self.max_packet_length = 60 + self.high_power = high_power + # Device support SPI mode 0 (polarity & phase = 0) up to a max of 10mhz. + # self._device = spidev.SPIDevice(spi, cs, baudrate=baudrate, polarity=0, phase=0) + # Setup reset as a digital output that's low. + self._rst = rst + self._rst.switch_to_output(value=False) + self.reset() # Reset the chip. + # Check the version of the chip. + version = self.read_u8(_RF69_REG_10_VERSION) + if version not in (0x23, 0x24): + raise RuntimeError("Invalid RFM69 version, check wiring! ID found:", hex(version)) + self.idle() # Enter idle state. + # Setup the chip in a similar way to the RadioHead RFM69 library. + # Set FIFO TX condition to not empty and the default FIFO threshold to 15. + self.write_u8(_RF69_REG_3C_FIFO_THRESH, 0b10001111) + # Configure low beta off. + self.write_u8(_RF69_REG_6F_TEST_DAGC, 0x30) + # Set the syncronization word. + self.sync_word = sync_word + self.preamble_length = preamble_length # Set the preamble length. + self.frequency_mhz = frequency # Set frequency. + self.encryption_key = encryption_key # Set encryption key. + # Configure modulation for RadioHead library GFSK_Rb250Fd250 mode + # by default. Users with advanced knowledge can manually reconfigure + # for any other mode (consulting the datasheet is absolutely + # necessary!). + self.modulation_shaping = 0b01 # Gaussian filter, BT=1.0 + self.bitrate = 250000 # 250kbs + self.frequency_deviation = 250000 # 250khz + self.rx_bw_dcc_freq = 0b111 # RxBw register = 0xE0 + self.rx_bw_mantissa = 0b00 + self.rx_bw_exponent = 0b000 + self.afc_bw_dcc_freq = 0b111 # AfcBw register = 0xE0 + self.afc_bw_mantissa = 0b00 + self.afc_bw_exponent = 0b000 + self.packet_format = 1 # Variable length. + self.dc_free = 0b10 # Whitening + # Set transmit power to 13 dBm, a safe value any module supports. + self._tx_power = None + self.tx_power = 13 + # Default to enable CRC checking on incoming packets. + self.enable_crc = crc + self.snr = None + + def reset(self) -> None: + """Perform a reset of the chip.""" + # See section 7.2.2 of the datasheet for reset description. + self._rst.value = True + time.sleep(0.0001) # 100 us + self._rst.value = False + time.sleep(0.005) # 5 ms + + def disable_boost(self) -> None: + """Disable preamp boost.""" + if self.high_power: + self.write_u8(_RF69_REG_5A_TEST_PA1, _TEST_PA1_NORMAL) + self.write_u8(_RF69_REG_5C_TEST_PA2, _TEST_PA2_NORMAL) + self.write_u8(_RF69_REG_13_OCP, _OCP_NORMAL) + + def idle(self) -> None: + """Enter idle standby mode (switching off high power amplifiers if necessary).""" + # Like RadioHead library, turn off high power boost if enabled. + self.disable_boost() + self.operation_mode = STANDBY_MODE + + def sleep(self) -> None: + """Enter sleep mode.""" + self.operation_mode = SLEEP_MODE + + def listen(self) -> None: + """Listen for packets to be received by the chip. Use :py:func:`receive` to listen, wait + and retrieve packets as they're available. + """ + # Like RadioHead library, turn off high power boost if enabled. + self.disable_boost() + # Enable payload ready interrupt for D0 line. + self.dio_0_mapping = 0b01 + # Enter RX mode (will clear FIFO!). + self.operation_mode = RX_MODE + + def transmit(self) -> None: + """Transmit a packet which is queued in the FIFO. This is a low level function for + entering transmit mode and more. For generating and transmitting a packet of data use + :py:func:`send` instead. + """ + # Like RadioHead library, turn on high power boost if needed. + if self.high_power and (self._tx_power >= 18): + self.write_u8(_RF69_REG_5A_TEST_PA1, _TEST_PA1_BOOST) + self.write_u8(_RF69_REG_5C_TEST_PA2, _TEST_PA2_BOOST) + self.write_u8(_RF69_REG_13_OCP, _OCP_HIGH_POWER) + # Enable packet sent interrupt for D0 line. + self.dio_0_mapping = 0b00 + # Enter TX mode (will clear FIFO!). + self.operation_mode = TX_MODE + + @property + def temperature(self) -> float: + """The internal temperature of the chip in degrees Celsius. Be warned this is not + calibrated or very accurate. + + .. warning:: Reading this will STOP any receiving/sending that might be happening! + """ + # Start a measurement then poll the measurement finished bit. + self.temp_start = 1 + while self.temp_running > 0: + pass + # Grab the temperature value and convert it to Celsius. + # This uses the same observed value formula from the Radiohead library. + temp = self.read_u8(_RF69_REG_4F_TEMP2) + return 166.0 - temp + + @property + def operation_mode(self) -> int: + """The operation mode value. Unless you're manually controlling the chip you shouldn't + change the operation_mode with this property as other side-effects are required for + changing logical modes--use :py:func:`idle`, :py:func:`sleep`, :py:func:`transmit`, + :py:func:`listen` instead to signal intent for explicit logical modes. + """ + op_mode = self.read_u8(_RF69_REG_01_OP_MODE) + return (op_mode >> 2) & 0b111 + + @operation_mode.setter + def operation_mode(self, val: int) -> None: + assert 0 <= val <= 4 + # Set the mode bits inside the operation mode register. + op_mode = self.read_u8(_RF69_REG_01_OP_MODE) + op_mode &= 0b11100011 + op_mode |= val << 2 + self.write_u8(_RF69_REG_01_OP_MODE, op_mode) + # Wait for mode to change by polling interrupt bit. + if HAS_SUPERVISOR: + start = supervisor.ticks_ms() + while not self.mode_ready: + if ticks_diff(supervisor.ticks_ms(), start) >= 1000: + raise TimeoutError("Operation Mode failed to set.") + else: + start = time.monotonic() + while not self.mode_ready: + if time.monotonic() - start >= 1: + raise TimeoutError("Operation Mode failed to set.") + + @property + def sync_word(self) -> bytearray: + """The synchronization word value. This is a byte string up to 8 bytes long (64 bits) + which indicates the synchronization word for transmitted and received packets. Any + received packet which does not include this sync word will be ignored. The default value + is 0x2D, 0xD4 which matches the RadioHead RFM69 library. Setting a value of None will + disable synchronization word matching entirely. + """ + # Handle when sync word is disabled.. + if not self.sync_on: + return None + # Sync word is not disabled so read the current value. + sync_word_length = self.sync_size + 1 # Sync word size is offset by 1 + # according to datasheet. + sync_word = bytearray(sync_word_length) + self.read_into(_RF69_REG_2F_SYNC_VALUE1, sync_word) + return sync_word + + @sync_word.setter + def sync_word(self, val: Optional[bytearray]) -> None: + # Handle disabling sync word when None value is set. + if val is None: + self.sync_on = 0 + else: + # Check sync word is at most 8 bytes. + assert 1 <= len(val) <= 8 + # Update the value, size and turn on the sync word. + self.write_from(_RF69_REG_2F_SYNC_VALUE1, val) + self.sync_size = len(val) - 1 # Again sync word size is offset by + # 1 according to datasheet. + self.sync_on = 1 + + @property + def preamble_length(self) -> int: + """The length of the preamble for sent and received packets, an unsigned 16-bit value. + Received packets must match this length or they are ignored! Set to 4 to match the + RadioHead RFM69 library. + """ + msb = self.read_u8(_RF69_REG_2C_PREAMBLE_MSB) + lsb = self.read_u8(_RF69_REG_2D_PREAMBLE_LSB) + return ((msb << 8) | lsb) & 0xFFFF + + @preamble_length.setter + def preamble_length(self, val: int) -> None: + assert 0 <= val <= 65535 + self.write_u8(_RF69_REG_2C_PREAMBLE_MSB, (val >> 8) & 0xFF) + self.write_u8(_RF69_REG_2D_PREAMBLE_LSB, val & 0xFF) + + @property + def frequency_mhz(self) -> float: + """The frequency of the radio in Megahertz. Only the allowed values for your radio must be + specified (i.e. 433 vs. 915 mhz)! + """ + # FRF register is computed from the frequency following the datasheet. + # See section 6.2 and FRF register description. + # Read bytes of FRF register and assemble into a 24-bit unsigned value. + msb = self.read_u8(_RF69_REG_07_FRF_MSB) + mid = self.read_u8(_RF69_REG_08_FRF_MID) + lsb = self.read_u8(_RF69_REG_09_FRF_LSB) + frf = ((msb << 16) | (mid << 8) | lsb) & 0xFFFFFF + frequency = (frf * _FSTEP) / 1000000.0 + return frequency + + @frequency_mhz.setter + def frequency_mhz(self, val: float) -> None: + assert 290 <= val <= 1020 + # Calculate FRF register 24-bit value using section 6.2 of the datasheet. + frf = int((val * 1000000.0) / _FSTEP) & 0xFFFFFF + # Extract byte values and update registers. + msb = frf >> 16 + mid = (frf >> 8) & 0xFF + lsb = frf & 0xFF + self.write_u8(_RF69_REG_07_FRF_MSB, msb) + self.write_u8(_RF69_REG_08_FRF_MID, mid) + self.write_u8(_RF69_REG_09_FRF_LSB, lsb) + + @property + def encryption_key(self) -> bytearray: + """The AES encryption key used to encrypt and decrypt packets by the chip. This can be set + to None to disable encryption (the default), otherwise it must be a 16 byte long byte + string which defines the key (both the transmitter and receiver must use the same key + value). + """ + # Handle if encryption is disabled. + if self.aes_on == 0: + return None + # Encryption is enabled so read the key and return it. + key = bytearray(16) + self.read_into(_RF69_REG_3E_AES_KEY1, key) + return key + + @encryption_key.setter + def encryption_key(self, val: bytearray) -> None: + # Handle if unsetting the encryption key (None value). + if val is None: + self.aes_on = 0 + else: + # Set the encryption key and enable encryption. + assert len(val) == 16 + self.write_from(_RF69_REG_3E_AES_KEY1, val) + self.aes_on = 1 + + @property + def tx_power(self) -> int: + """The transmit power in dBm. Can be set to a value from -2 to 20 for high power devices + (RFM69HCW, high_power=True) or -18 to 13 for low power devices. Only integer power + levels are actually set (i.e. 12.5 will result in a value of 12 dBm). + """ + # Follow table 10 truth table from the datasheet for determining power + # level from the individual PA level bits and output power register. + pa0 = self.pa_0_on + pa1 = self.pa_1_on + pa2 = self.pa_2_on + current_output_power = self.output_power + if pa0 and not pa1 and not pa2: + # -18 to 13 dBm range + return -18 + current_output_power + if not pa0 and pa1 and not pa2: + # -2 to 13 dBm range + return -18 + current_output_power + if not pa0 and pa1 and pa2 and self.high_power and self._tx_power < 18: + # 2 to 17 dBm range + return -14 + current_output_power + if not pa0 and pa1 and pa2 and self.high_power and self._tx_power >= 18: + # 5 to 20 dBm range + return -11 + current_output_power + raise RuntimeError("Power amps state unknown!") + + @tx_power.setter + def tx_power(self, val: float): + val = int(val) + # Determine power amplifier and output power values depending on + # high power state and requested power. + pa_0_on = pa_1_on = pa_2_on = 0 + output_power = 0 + if self.high_power: + # Handle high power mode. + assert -2 <= val <= 20 + pa_1_on = 1 + if val <= 13: + output_power = val + 18 + elif 13 < val <= 17: + pa_2_on = 1 + output_power = val + 14 + else: # power >= 18 dBm + # Note this also needs PA boost enabled separately! + pa_2_on = 1 + output_power = val + 11 + else: + # Handle non-high power mode. + assert -18 <= val <= 13 + # Enable only power amplifier 0 and set output power. + pa_0_on = 1 + output_power = val + 18 + # Set power amplifiers and output power as computed above. + self.pa_0_on = pa_0_on + self.pa_1_on = pa_1_on + self.pa_2_on = pa_2_on + self.output_power = output_power + self._tx_power = val + + @property + def rssi(self) -> float: + """The received strength indicator (in dBm). + May be inaccuate if not read immediatey. last_rssi contains the value read immediately + receipt of the last packet. + """ + # Read RSSI register and convert to value using formula in datasheet. + return -self.read_u8(_RF69_REG_24_RSSI_VALUE) / 2.0 + + @property + def bitrate(self) -> float: + """The modulation bitrate in bits/second (or chip rate if Manchester encoding is enabled). + Can be a value from ~489 to 32mbit/s, but see the datasheet for the exact supported + values. + """ + msb = self.read_u8(_RF69_REG_03_BITRATE_MSB) + lsb = self.read_u8(_RF69_REG_04_BITRATE_LSB) + return _FXOSC / ((msb << 8) | lsb) + + @bitrate.setter + def bitrate(self, val: float) -> None: + assert (_FXOSC / 65535) <= val <= 32000000.0 + # Round up to the next closest bit-rate value with addition of 0.5. + bitrate = int((_FXOSC / val) + 0.5) & 0xFFFF + self.write_u8(_RF69_REG_03_BITRATE_MSB, bitrate >> 8) + self.write_u8(_RF69_REG_04_BITRATE_LSB, bitrate & 0xFF) + + @property + def frequency_deviation(self) -> float: + """The frequency deviation in Hertz.""" + msb = self.read_u8(_RF69_REG_05_FDEV_MSB) + lsb = self.read_u8(_RF69_REG_06_FDEV_LSB) + return _FSTEP * ((msb << 8) | lsb) + + @frequency_deviation.setter + def frequency_deviation(self, val: float) -> None: + assert 0 <= val <= (_FSTEP * 16383) # fdev is a 14-bit unsigned value + # Round up to the next closest integer value with addition of 0.5. + fdev = int((val / _FSTEP) + 0.5) & 0x3FFF + self.write_u8(_RF69_REG_05_FDEV_MSB, fdev >> 8) + self.write_u8(_RF69_REG_06_FDEV_LSB, fdev & 0xFF) + + @property + def enable_crc(self) -> bool: + """Set to True to enable hardware CRC checking of incoming packets. + Incoming packets that fail the CRC check are not processed. Set to + False to disable CRC checking and process all incoming packets.""" + return self.crc_on + + @enable_crc.setter + def enable_crc(self, val: bool) -> None: + # Optionally enable CRC checking on incoming packets. + if val: + self.crc_on = 1 + else: + self.crc_on = 0 + + def crc_error(self) -> bool: + """crc status""" + return (self.read_u8(_RF69_REG_28_IRQ_FLAGS2) & 0x2) >> 1 + + @property + def enable_address_filter(self) -> bool: + """Set to True to enable address filtering. + Incoming packets that do no match the node address or broadcast address + will be ignored.""" + return self.address_filter + + @enable_address_filter.setter + def enable_address_filter(self, val: bool) -> None: + # Enable address filtering on incoming packets. + if val: + self.address_filter = 2 # accept node address or broadcast address + else: + self.address_filter = 0 + + @property + def fsk_node_address(self) -> int: + """Node Address for Address Filtering""" + return self.read_u8(_RF69_REG_39_NODE_ADDR) + + @fsk_node_address.setter + def fsk_node_address(self, val: int) -> None: + assert 0 <= val <= 255 + self.write_u8(_RF69_REG_39_NODE_ADDR, val) + + @property + def fsk_broadcast_address(self) -> int: + """Node Address for Address Filtering""" + return self.read_u8(_RF69_REG_3A_BROADCAST_ADDR) + + @fsk_broadcast_address.setter + def fsk_broadcast_address(self, val: int) -> None: + assert 0 <= val <= 255 + self.write_u8(_RF69_REG_3A_BROADCAST_ADDR, val) + + @property + def ook_fixed_threshold(self) -> int: + """Fixed threshold for data slicer in OOK mode""" + return self.read_u8(_RF69_REG_1D_OOK_FIX) + + @ook_fixed_threshold.setter + def ook_fixed_threshold(self, val: int) -> None: + assert 0 <= val <= 255 + self.write_u8(_RF69_REG_1D_OOK_FIX, val) + + def packet_sent(self) -> bool: + """Transmit status""" + return (self.read_u8(_RF69_REG_28_IRQ_FLAGS2) & 0x8) >> 3 + + def payload_ready(self) -> bool: + """Receive status""" + return (self.read_u8(_RF69_REG_28_IRQ_FLAGS2) & 0x4) >> 2 + + def clear_interrupt(self) -> None: + """Clear interrupt flags""" + self.write_u8(_RF69_REG_27_IRQ_FLAGS1, 0xFF) + self.write_u8(_RF69_REG_28_IRQ_FLAGS2, 0xFF) + + def fill_fifo(self, payload: bytearray) -> None: + """Write the payload to the FIFO.""" + complete_payload = bytearray(1) # prepend packet length to payload + complete_payload[0] = len(payload) + # put the payload lengthe in the beginning of the packet for RFM69 + complete_payload = complete_payload + payload + # Write payload to transmit fifo + self.write_from(_RF69_REG_00_FIFO, complete_payload) + + def read_fifo(self) -> bytearray: + """Read the packet from the FIFO.""" + # Read the length of the FIFO. + fifo_length = self.read_u8(_RF69_REG_00_FIFO) + # Handle if the received packet is too small to include the 4 byte + # RadioHead header and at least one byte of data --reject this packet and ignore it. + if fifo_length > 0: # read and clear the FIFO if anything in it + packet = bytearray(fifo_length) + # read the packet + self.read_into(_RF69_REG_00_FIFO, packet, fifo_length) + if fifo_length < 5: + packet = None + return packet diff --git a/adafruit_rfm/rfm9x.py b/adafruit_rfm/rfm9x.py new file mode 100644 index 0000000..ac62d99 --- /dev/null +++ b/adafruit_rfm/rfm9x.py @@ -0,0 +1,539 @@ +# SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_rfm9x` +==================================================== + +CircuitPython module for the RFM95/6/7/8 LoRa 433/915mhz radio modules. This is +adapted from the Radiohead library RF95 code from: +http: www.airspayce.com/mikem/arduino/RadioHead/ + +* Author(s): Tony DiCola, Jerry Needell +""" + +import time + +from micropython import const + +from adafruit_rfm.rfm_common import RFMSPI + +try: + import busio + import digitalio + + try: + from typing import Literal + except ImportError: + from typing_extensions import Literal + +except ImportError: + pass + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/jerryneedell/CircuitPython_RFM.git" + +# pylint: disable=duplicate-code + +# Internal constants: +# Register names (FSK Mode even though we use LoRa instead, from table 85) +_RF95_REG_00_FIFO = const(0x00) +_RF95_REG_01_OP_MODE = const(0x01) +_RF95_REG_06_FRF_MSB = const(0x06) +_RF95_REG_07_FRF_MID = const(0x07) +_RF95_REG_08_FRF_LSB = const(0x08) +_RF95_REG_09_PA_CONFIG = const(0x09) +_RF95_REG_0A_PA_RAMP = const(0x0A) +_RF95_REG_0B_OCP = const(0x0B) +_RF95_REG_0C_LNA = const(0x0C) +_RF95_REG_0D_FIFO_ADDR_PTR = const(0x0D) +_RF95_REG_0E_FIFO_TX_BASE_ADDR = const(0x0E) +_RF95_REG_0F_FIFO_RX_BASE_ADDR = const(0x0F) +_RF95_REG_10_FIFO_RX_CURRENT_ADDR = const(0x10) +_RF95_REG_11_IRQ_FLAGS_MASK = const(0x11) +_RF95_REG_12_IRQ_FLAGS = const(0x12) +_RF95_REG_13_RX_NB_BYTES = const(0x13) +_RF95_REG_14_RX_HEADER_CNT_VALUE_MSB = const(0x14) +_RF95_REG_15_RX_HEADER_CNT_VALUE_LSB = const(0x15) +_RF95_REG_16_RX_PACKET_CNT_VALUE_MSB = const(0x16) +_RF95_REG_17_RX_PACKET_CNT_VALUE_LSB = const(0x17) +_RF95_REG_18_MODEM_STAT = const(0x18) +_RF95_REG_19_PKT_SNR_VALUE = const(0x19) +_RF95_REG_1A_PKT_RSSI_VALUE = const(0x1A) +_RF95_REG_1B_RSSI_VALUE = const(0x1B) +_RF95_REG_1C_HOP_CHANNEL = const(0x1C) +_RF95_REG_1D_MODEM_CONFIG1 = const(0x1D) +_RF95_REG_1E_MODEM_CONFIG2 = const(0x1E) +_RF95_REG_1F_SYMB_TIMEOUT_LSB = const(0x1F) +_RF95_REG_20_PREAMBLE_MSB = const(0x20) +_RF95_REG_21_PREAMBLE_LSB = const(0x21) +_RF95_REG_22_PAYLOAD_LENGTH = const(0x22) +_RF95_REG_23_MAX_PAYLOAD_LENGTH = const(0x23) +_RF95_REG_24_HOP_PERIOD = const(0x24) +_RF95_REG_25_FIFO_RX_BYTE_ADDR = const(0x25) +_RF95_REG_26_MODEM_CONFIG3 = const(0x26) + +_RF95_REG_40_DIO_MAPPING1 = const(0x40) +_RF95_REG_41_DIO_MAPPING2 = const(0x41) +_RF95_REG_42_VERSION = const(0x42) + +_RF95_REG_4B_TCXO = const(0x4B) +_RF95_REG_4D_PA_DAC = const(0x4D) +_RF95_REG_5B_FORMER_TEMP = const(0x5B) +_RF95_REG_61_AGC_REF = const(0x61) +_RF95_REG_62_AGC_THRESH1 = const(0x62) +_RF95_REG_63_AGC_THRESH2 = const(0x63) +_RF95_REG_64_AGC_THRESH3 = const(0x64) + +_RF95_DETECTION_OPTIMIZE = const(0x31) +_RF95_DETECTION_THRESHOLD = const(0x37) + +_RF95_PA_DAC_DISABLE = const(0x04) +_RF95_PA_DAC_ENABLE = const(0x07) + +# The crystal oscillator frequency of the module +_RF95_FXOSC = 32000000.0 + +# The Frequency Synthesizer step = RH_RF95_FXOSC / 2^^19 +_RF95_FSTEP = _RF95_FXOSC / 524288 + +# RadioHead specific compatibility constants. +_RH_BROADCAST_ADDRESS = const(0xFF) + +# The acknowledgement bit in the FLAGS +# The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved +# for application layer use. +_RH_FLAGS_ACK = const(0x80) +_RH_FLAGS_RETRY = const(0x40) + +# User facing constants: +SLEEP_MODE = 0b000 +STANDBY_MODE = 0b001 +FS_TX_MODE = 0b010 +TX_MODE = 0b011 +FS_RX_MODE = 0b100 +RX_MODE = 0b101 + + +# pylint: disable=too-many-instance-attributes +class RFM9x(RFMSPI): + """Interface to a RFM95/6/7/8 LoRa radio module. Allows sending and + receiving bytes of data in long range LoRa mode at a support board frequency + (433/915mhz). + + You must specify the following parameters: + - spi: The SPI bus connected to the radio. + - cs: The CS pin DigitalInOut connected to the radio. + - reset: The reset/RST pin DigialInOut connected to the radio. + - frequency: The frequency (in mhz) of the radio module (433/915mhz typically). + + You can optionally specify: + - preamble_length: The length in bytes of the packet preamble (default 8). + - high_power: Boolean to indicate a high power board (RFM95, etc.). Default + is True for high power. + - baudrate: Baud rate of the SPI connection, default is 10mhz but you might + choose to lower to 1mhz if using long wires or a breadboard. + - agc: Boolean to Enable/Disable Automatic Gain Control - Default=False (AGC off) + - crc: Boolean to Enable/Disable Cyclic Redundancy Check - Default=True (CRC Enabled) + Remember this library makes a best effort at receiving packets with pure + Python code. Trying to receive packets too quickly will result in lost data + so limit yourself to simple scenarios of sending and receiving single + packets at a time. + + Also note this library tries to be compatible with raw RadioHead Arduino + library communication. This means the library sets up the radio modulation + to match RadioHead's defaults and assumes that each packet contains a + 4 byte header compatible with RadioHead's implementation. + Advanced RadioHead features like address/node specific packets + or "reliable datagram" delivery are supported however due to the + limitations noted, "reliable datagram" is still subject to missed packets but with it, + sender is notified if a packet has potentially been missed. + """ + + operation_mode = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, bits=3) + + low_frequency_mode = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, offset=3, bits=1) + + modulation_type = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, offset=5, bits=2) + + # Long range/LoRa mode can only be set in sleep mode! + long_range_mode = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, offset=7, bits=1) + + output_power = RFMSPI.RegisterBits(_RF95_REG_09_PA_CONFIG, bits=4) + + max_power = RFMSPI.RegisterBits(_RF95_REG_09_PA_CONFIG, offset=4, bits=3) + + pa_select = RFMSPI.RegisterBits(_RF95_REG_09_PA_CONFIG, offset=7, bits=1) + + pa_dac = RFMSPI.RegisterBits(_RF95_REG_4D_PA_DAC, bits=3) + + dio0_mapping = RFMSPI.RegisterBits(_RF95_REG_40_DIO_MAPPING1, offset=6, bits=2) + + auto_agc = RFMSPI.RegisterBits(_RF95_REG_26_MODEM_CONFIG3, offset=2, bits=1) + + low_datarate_optimize = RFMSPI.RegisterBits(_RF95_REG_26_MODEM_CONFIG3, offset=3, bits=1) + + lna_boost_hf = RFMSPI.RegisterBits(_RF95_REG_0C_LNA, offset=0, bits=2) + + auto_ifon = RFMSPI.RegisterBits(_RF95_DETECTION_OPTIMIZE, offset=7, bits=1) + + detection_optimize = RFMSPI.RegisterBits(_RF95_DETECTION_OPTIMIZE, offset=0, bits=3) + + bw_bins = (7800, 10400, 15600, 20800, 31250, 41700, 62500, 125000, 250000) + + def __init__( # noqa: PLR0913 + self, + spi: busio.SPI, + cs: digitalio.DigitalInOut, # pylint: disable=invalid-name + rst: digitalio.DigitalInOut, + frequency: int, + *, + preamble_length: int = 8, + high_power: bool = True, + baudrate: int = 5000000, + agc: bool = False, + crc: bool = True, + ) -> None: + super().__init__(spi, cs, baudrate=baudrate) + self.module = "RFM9X" + self.max_packet_length = 252 + self.high_power = high_power + # Device support SPI mode 0 (polarity & phase = 0) up to a max of 10mhz. + # Set Default Baudrate to 5MHz to avoid problems + # self._device = spidev.SPIDevice(spi, cs, baudrate=baudrate, polarity=0, phase=0) + # Setup reset as a digital output - initially High + # This line is pulled low as an output quickly to trigger a reset. + self._rst = rst + # initialize Reset High + self._rst.switch_to_output(value=True) + self.reset() + # No device type check! Catch an error from the very first request and + # throw a nicer message to indicate possible wiring problems. + version = self.read_u8(address=_RF95_REG_42_VERSION) + if version != 18: + raise RuntimeError( + "Failed to find rfm9x with expected version -- check wiring. Version found:", + hex(version), + ) + + # Set sleep mode, wait 10s and confirm in sleep mode (basic device check). + # Also set long range mode (LoRa mode) as it can only be done in sleep. + self.sleep() + time.sleep(0.01) + self.long_range_mode = True + if self.operation_mode != SLEEP_MODE or not self.long_range_mode: + raise RuntimeError("Failed to configure radio for LoRa mode, check wiring!") + # clear default setting for access to LF registers if frequency > 525MHz + if frequency > 525: + self.low_frequency_mode = 0 + # Setup entire 256 byte FIFO + self.write_u8(_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0x00) + self.write_u8(_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0x00) + # Set mode idle + self.idle() + # Set frequency + self.frequency_mhz = frequency + # Set preamble length (default 8 bytes to match radiohead). + self.preamble_length = preamble_length + # Defaults set modem config to RadioHead compatible Bw125Cr45Sf128 mode. + self.signal_bandwidth = 125000 + self.coding_rate = 5 + self.spreading_factor = 7 + # Default to enable CRC checking on incoming packets. + self.enable_crc = crc + """CRC Enable state""" + # set AGC - Default = False + self.auto_agc = agc + """Automatic Gain Control state""" + # Set transmit power to 13 dBm, a safe value any module supports. + self.tx_power = 13 + + def reset(self) -> None: + """Perform a reset of the chip.""" + # See section 7.2.2 of the datasheet for reset description. + self._rst.value = False # Set Reset Low + time.sleep(0.0001) # 100 us + self._rst.value = True # set Reset High + time.sleep(0.005) # 5 ms + + def idle(self) -> None: + """Enter idle standby mode.""" + self.operation_mode = STANDBY_MODE + + def sleep(self) -> None: + """Enter sleep mode.""" + self.operation_mode = SLEEP_MODE + + def listen(self) -> None: + """Listen for packets to be received by the chip. Use :py:func:`receive` + to listen, wait and retrieve packets as they're available. + """ + self.operation_mode = RX_MODE + self.dio0_mapping = 0b00 # Interrupt on rx done. + + def transmit(self) -> None: + """Transmit a packet which is queued in the FIFO. This is a low level + function for entering transmit mode and more. For generating and + transmitting a packet of data use :py:func:`send` instead. + """ + self.operation_mode = TX_MODE + self.dio0_mapping = 0b01 # Interrupt on tx done. + + @property + def preamble_length(self) -> int: + """The length of the preamble for sent and received packets, an unsigned + 16-bit value. Received packets must match this length or they are + ignored! Set to 8 to match the RadioHead RFM95 library. + """ + msb = self.read_u8(_RF95_REG_20_PREAMBLE_MSB) + lsb = self.read_u8(_RF95_REG_21_PREAMBLE_LSB) + return ((msb << 8) | lsb) & 0xFFFF + + @preamble_length.setter + def preamble_length(self, val: int) -> None: + assert 0 <= val <= 65535 + self.write_u8(_RF95_REG_20_PREAMBLE_MSB, (val >> 8) & 0xFF) + self.write_u8(_RF95_REG_21_PREAMBLE_LSB, val & 0xFF) + + @property + def frequency_mhz(self) -> Literal[433.0, 915.0]: + """The frequency of the radio in Megahertz. Only the allowed values for + your radio must be specified (i.e. 433 vs. 915 mhz)! + """ + msb = self.read_u8(_RF95_REG_06_FRF_MSB) + mid = self.read_u8(_RF95_REG_07_FRF_MID) + lsb = self.read_u8(_RF95_REG_08_FRF_LSB) + frf = ((msb << 16) | (mid << 8) | lsb) & 0xFFFFFF + frequency = (frf * _RF95_FSTEP) / 1000000.0 + return frequency + + @frequency_mhz.setter + def frequency_mhz(self, val: Literal[433.0, 915.0]) -> None: + if val < 240 or val > 960: + raise RuntimeError("frequency_mhz must be between 240 and 960") + # Calculate FRF register 24-bit value. + frf = int((val * 1000000.0) / _RF95_FSTEP) & 0xFFFFFF + # Extract byte values and update registers. + msb = frf >> 16 + mid = (frf >> 8) & 0xFF + lsb = frf & 0xFF + self.write_u8(_RF95_REG_06_FRF_MSB, msb) + self.write_u8(_RF95_REG_07_FRF_MID, mid) + self.write_u8(_RF95_REG_08_FRF_LSB, lsb) + + @property + def tx_power(self) -> int: + """The transmit power in dBm. Can be set to a value from 5 to 23 for + high power devices (RFM95/96/97/98, high_power=True) or -1 to 14 for low + power devices. Only integer power levels are actually set (i.e. 12.5 + will result in a value of 12 dBm). + The actual maximum setting for high_power=True is 20dBm but for values > 20 + the PA_BOOST will be enabled resulting in an additional gain of 3dBm. + The actual setting is reduced by 3dBm. + The reported value will reflect the reduced setting. + """ + if self.high_power: + return self.output_power + 5 + return self.output_power - 1 + + @tx_power.setter + def tx_power(self, val: int) -> None: + val = int(val) + if self.high_power: + if val < 5 or val > 23: + raise RuntimeError("tx_power must be between 5 and 23") + # Enable power amp DAC if power is above 20 dB. + # Lower setting by 3db when PA_BOOST enabled - see Data Sheet Section 6.4 + if val > 20: + self.pa_dac = _RF95_PA_DAC_ENABLE + val -= 3 + else: + self.pa_dac = _RF95_PA_DAC_DISABLE + self.pa_select = True + self.output_power = (val - 5) & 0x0F + else: + assert -1 <= val <= 14 + self.pa_select = False + self.max_power = 0b111 # Allow max power output. + self.output_power = (val + 1) & 0x0F + + @property + def rssi(self) -> float: + """The received strength indicator (in dBm) of the last received message.""" + # Read RSSI register and convert to value using formula in datasheet. + # Remember in LoRa mode the payload register changes function to RSSI! + raw_rssi = self.read_u8(_RF95_REG_1A_PKT_RSSI_VALUE) + if self.low_frequency_mode: + raw_rssi -= 157 + else: + raw_rssi -= 164 + return float(raw_rssi) + + @property + def snr(self) -> float: + """The SNR (in dB) of the last received message.""" + # Read SNR 0x19 register and convert to value using formula in datasheet. + # SNR(dB) = PacketSnr [twos complement] / 4 + snr_byte = self.read_u8(_RF95_REG_19_PKT_SNR_VALUE) + if snr_byte > 127: + snr_byte = (256 - snr_byte) * -1 + return snr_byte / 4 + + @property + def signal_bandwidth(self) -> int: + """The signal bandwidth used by the radio (try setting to a higher + value to increase throughput or to a lower value to increase the + likelihood of successfully received payloads). Valid values are + listed in RFM9x.bw_bins.""" + bw_id = (self.read_u8(_RF95_REG_1D_MODEM_CONFIG1) & 0xF0) >> 4 + if bw_id >= len(self.bw_bins): + current_bandwidth = 500000 + else: + current_bandwidth = self.bw_bins[bw_id] + return current_bandwidth + + @signal_bandwidth.setter + def signal_bandwidth(self, val: int) -> None: + # Set signal bandwidth (set to 125000 to match RadioHead Bw125). + for bw_id, cutoff in enumerate(self.bw_bins): + if val <= cutoff: + break + else: + bw_id = 9 + self.write_u8( + _RF95_REG_1D_MODEM_CONFIG1, + (self.read_u8(_RF95_REG_1D_MODEM_CONFIG1) & 0x0F) | (bw_id << 4), + ) + if val >= 500000: + # see Semtech SX1276 errata note 2.3 + self.auto_ifon = True + # see Semtech SX1276 errata note 2.1 + if self.low_frequency_mode: + self.write_u8(0x36, 0x02) + self.write_u8(0x3A, 0x7F) + else: + self.write_u8(0x36, 0x02) + self.write_u8(0x3A, 0x64) + else: + # see Semtech SX1276 errata note 2.3 + self.auto_ifon = False + self.write_u8(0x36, 0x03) + if val == 7800: + self.write_u8(0x2F, 0x48) + elif val >= 62500: + # see Semtech SX1276 errata note 2.3 + self.write_u8(0x2F, 0x40) + else: + self.write_u8(0x2F, 0x44) + self.write_u8(0x30, 0) + + @property + def coding_rate(self) -> Literal[5, 6, 7, 8]: + """The coding rate used by the radio to control forward error + correction (try setting to a higher value to increase tolerance of + short bursts of interference or to a lower value to increase bit + rate). Valid values are limited to 5, 6, 7, or 8.""" + cr_id = (self.read_u8(_RF95_REG_1D_MODEM_CONFIG1) & 0x0E) >> 1 + denominator = cr_id + 4 + return denominator + + @coding_rate.setter + def coding_rate(self, val: Literal[5, 6, 7, 8]) -> None: + # Set coding rate (set to 5 to match RadioHead Cr45). + denominator = min(max(val, 5), 8) + cr_id = denominator - 4 + self.write_u8( + _RF95_REG_1D_MODEM_CONFIG1, + (self.read_u8(_RF95_REG_1D_MODEM_CONFIG1) & 0xF1) | (cr_id << 1), + ) + + @property + def spreading_factor(self) -> Literal[6, 7, 8, 9, 10, 11, 12]: + """The spreading factor used by the radio (try setting to a higher + value to increase the receiver's ability to distinguish signal from + noise or to a lower value to increase the data transmission rate). + Valid values are limited to 6, 7, 8, 9, 10, 11, or 12.""" + sf_id = (self.read_u8(_RF95_REG_1E_MODEM_CONFIG2) & 0xF0) >> 4 + return sf_id + + @spreading_factor.setter + def spreading_factor(self, val: Literal[6, 7, 8, 9, 10, 11, 12]) -> None: + # Set spreading factor (set to 7 to match RadioHead Sf128). + val = min(max(val, 6), 12) + + if val == 6: + self.detection_optimize = 0x5 + else: + self.detection_optimize = 0x3 + + self.write_u8(_RF95_DETECTION_THRESHOLD, 0x0C if val == 6 else 0x0A) + self.write_u8( + _RF95_REG_1E_MODEM_CONFIG2, + ((self.read_u8(_RF95_REG_1E_MODEM_CONFIG2) & 0x0F) | ((val << 4) & 0xF0)), + ) + + @property + def enable_crc(self) -> bool: + """Set to True to enable hardware CRC checking of incoming packets. + Incoming packets that fail the CRC check are not processed. Set to + False to disable CRC checking and process all incoming packets.""" + return (self.read_u8(_RF95_REG_1E_MODEM_CONFIG2) & 0x04) == 0x04 + + @enable_crc.setter + def enable_crc(self, val: bool) -> None: + # Optionally enable CRC checking on incoming packets. + if val: + self.write_u8( + _RF95_REG_1E_MODEM_CONFIG2, + self.read_u8(_RF95_REG_1E_MODEM_CONFIG2) | 0x04, + ) + else: + self.write_u8( + _RF95_REG_1E_MODEM_CONFIG2, + self.read_u8(_RF95_REG_1E_MODEM_CONFIG2) & 0xFB, + ) + + def crc_error(self) -> bool: + """crc status""" + return (self.read_u8(_RF95_REG_12_IRQ_FLAGS) & 0x20) >> 5 + + def packet_sent(self) -> bool: + """Transmit status""" + return (self.read_u8(_RF95_REG_12_IRQ_FLAGS) & 0x8) >> 3 + + def payload_ready(self) -> bool: + """Receive status""" + return (self.read_u8(_RF95_REG_12_IRQ_FLAGS) & 0x40) >> 6 + + def clear_interrupt(self) -> None: + """Clear Interrupt flags""" + self.write_u8(_RF95_REG_12_IRQ_FLAGS, 0xFF) + + def fill_fifo(self, payload: bytearray) -> None: + """len_data is not used but is here for compatibility with rfm69 + Fill the FIFO with a packet to send""" + self.write_u8(_RF95_REG_0D_FIFO_ADDR_PTR, 0x00) # FIFO starts at 0. + # Write payload. + self.write_from(_RF95_REG_00_FIFO, payload) + # Write payload and header length. + self.write_u8(_RF95_REG_22_PAYLOAD_LENGTH, len(payload)) + + def read_fifo(self) -> bytearray: + """Read the data from the FIFO.""" + # Read the length of the FIFO. + fifo_length = self.read_u8(_RF95_REG_13_RX_NB_BYTES) + # Handle if the received packet is too small to include the 4 byte + # RadioHead header and at least one byte of data --reject this packet and ignore it. + if fifo_length > 0: # read and clear the FIFO if anything in it + packet = bytearray(fifo_length) + current_addr = self.read_u8(_RF95_REG_10_FIFO_RX_CURRENT_ADDR) + self.write_u8(_RF95_REG_0D_FIFO_ADDR_PTR, current_addr) + # read the packet + self.read_into(_RF95_REG_00_FIFO, packet) + + # clear interrupt + self.write_u8(_RF95_REG_12_IRQ_FLAGS, 0xFF) + if fifo_length < 5: + packet = None + return packet diff --git a/adafruit_rfm/rfm9xfsk.py b/adafruit_rfm/rfm9xfsk.py new file mode 100644 index 0000000..589c0ef --- /dev/null +++ b/adafruit_rfm/rfm9xfsk.py @@ -0,0 +1,578 @@ +# SPDX-FileCopyrightText: 2024 Jerry Needell for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_rfm9xFSK` +==================================================== + +CircuitPython module for the RFM95/6/7/8 FSK 433/915mhz radio modules. + +* Author(s): Jerry Needell +""" + +import time + +from micropython import const + +from adafruit_rfm.rfm_common import RFMSPI + +try: + from typing import Optional + + import busio + import digitalio + + try: + from typing import Literal + except ImportError: + from typing_extensions import Literal + +except ImportError: + pass + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/jerryneedell/CircuitPython_RFM.git" + +# pylint: disable=duplicate-code + +# Internal constants: +# Register names (FSK Mode even though we use LoRa instead, from table 85) +_RF95_REG_00_FIFO = const(0x00) +_RF95_REG_01_OP_MODE = const(0x01) +_RF95_REG_02_BITRATE_MSB = const(0x02) +_RF95_REG_03_BITRATE_LSB = const(0x03) +_RF95_REG_04_FDEV_MSB = const(0x4) +_RF95_REG_05_FDEV_LSB = const(0x5) +_RF95_REG_06_FRF_MSB = const(0x06) +_RF95_REG_07_FRF_MID = const(0x07) +_RF95_REG_08_FRF_LSB = const(0x08) +_RF95_REG_09_PA_CONFIG = const(0x09) +_RF95_REG_0A_PA_RAMP = const(0x0A) +_RF95_REG_0B_OCP = const(0x0B) +_RF95_REG_0C_LNA = const(0x0C) +_RF95_REG_0D_RX_CFG = const(0x0D) +_RF95_REG_0E_RSSI_CFG = const(0x0E) +_RF95_REG_0F_RSSI_COLLISION = const(0x0F) +_RF95_REG_10_RSSI_THRESH = const(0x10) +_RF95_REG_11_RSSI_VALUE = const(0x11) +_RF95_REG_12_RX_BW = const(0x12) +_RF95_REG_13_AFC_BW = const(0x13) +_RF95_REG_14_OOK_PEAK = const(0x14) +_RF95_REG_15_OOK_FIX = const(0x15) +_RF95_REG_16_OOK_AVG = const(0x16) +_RF95_REG_1A_AFC_FEI_CTL = const(0x1A) +_RF95_REG_1B_AFC_MSB = const(0x1B) +_RF95_REG_1C_AFC_LSB = const(0x1C) +_RF95_REG_1D_FEI_MSB = const(0x1D) +_RF95_REG_1E_FEI_LSB = const(0x1E) +_RF95_REG_1F_PREAMBLE_DETECT = const(0x1F) +_RF95_REG_20_RX_TIMEOUT_1 = const(0x20) +_RF95_REG_21_RX_TIMEOUT_2 = const(0x21) +_RF95_REG_22_RX_TIMEOUT_3 = const(0x22) +_RF95_REG_23_RX_DELAY = const(0x23) +_RF95_REG_24_OSC = const(0x24) +_RF95_REG_25_PREAMBLE_MSB = const(0x25) +_RF95_REG_26_PREAMBLE_LSB = const(0x26) +_RF95_REG_27_SYNC_CONFIG = const(0x27) +_RF95_REG_28_SYNC_VALUE_1 = const(0x28) +_RF95_REG_29_SYNC_VALUE_2 = const(0x29) +_RF95_REG_2A_SYNC_VALUE_3 = const(0x2A) +_RF95_REG_2B_SYNC_VALUE_4 = const(0x2B) +_RF95_REG_2C_SYNC_VALUE_5 = const(0x2C) +_RF95_REG_2D_SYNC_VALUE_6 = const(0x2D) +_RF95_REG_2E_SYNC_VALUE_7 = const(0x2E) +_RF95_REG_2F_SYNC_VALUE_8 = const(0x2F) +_RF95_REG_30_PACKET_CONFIG_1 = const(0x30) +_RF95_REG_31_PACKET_CONFIG_2 = const(0x31) +_RF95_REG_32_PAYLOAD_LENGTH = const(0x32) +_RF95_REG_33_NODE_ADDR = const(0x33) +_RF95_REG_34_BROADCAST_ADDR = const(0x34) +_RF95_REG_35_FIFO_THRESH = const(0x35) +_RF95_REG_36_SEQ_CFG_1 = const(0x36) +_RF95_REG_37_SEQ_CFG_2 = const(0x37) +_RF95_REG_38_TIMER_RES = const(0x38) +_RF95_REG_39_TIMER1_COEF = const(0x39) +_RF95_REG_3A_TIMER2_COEF = const(0x3A) +_RF95_REG_3B_IMAGE_CAL = const(0x3B) +_RF95_REG_3C_TEMP = const(0x3C) +_RF95_REG_3D_LOW_BAT = const(0x3D) +_RF95_REG_3E_IRQ_FLAGS_1 = const(0x3E) +_RF95_REG_3F_IRQ_FLAGS_2 = const(0x3F) + +_RF95_REG_40_DIO_MAPPING1 = const(0x40) +_RF95_REG_41_DIO_MAPPING2 = const(0x41) +_RF95_REG_42_VERSION = const(0x42) + +_RF95_REG_44_PIII_IOP = const(0x44) + +_RF95_REG_4B_TCXO = const(0x4B) +_RF95_REG_4D_PA_DAC = const(0x4D) +_RF95_REG_5B_FORMER_TEMP = const(0x5B) +_RF95_REG_5B_BIT_RATE_FRAC = const(0x5D) +_RF95_REG_61_AGC_REF = const(0x61) +_RF95_REG_62_AGC_THRESH1 = const(0x62) +_RF95_REG_63_AGC_THRESH2 = const(0x63) +_RF95_REG_64_AGC_THRESH3 = const(0x64) + + +_RF95_PA_DAC_DISABLE = const(0x04) +_RF95_PA_DAC_ENABLE = const(0x07) + +# The crystal oscillator frequency of the module +_RF95_FXOSC = 32000000.0 + +# The Frequency Synthesizer step = RH_RF95_FXOSC / 2^^19 +_RF95_FSTEP = _RF95_FXOSC / 524288 + +# RadioHead specific compatibility constants. +_RH_BROADCAST_ADDRESS = const(0xFF) + +# The acknowledgement bit in the FLAGS +# The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved +# for application layer use. +_RH_FLAGS_ACK = const(0x80) +_RH_FLAGS_RETRY = const(0x40) + +# User facing constants: +SLEEP_MODE = 0b000 +STANDBY_MODE = 0b001 +FS_TX_MODE = 0b010 +TX_MODE = 0b011 +FS_RX_MODE = 0b100 +RX_MODE = 0b101 + + +# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-public-methods +class RFM9xFSK(RFMSPI): + """Interface to a RFM95/6/7/8 FSK radio module. Allows sending and + receiving bytes of data in FSK mode at a support board frequency + (433/915mhz). + + :param busio.SPI spi: The SPI bus connected to the chip. Ensure SCK, MOSI, and MISO are + connected. + :param ~digitalio.DigitalInOut cs: A DigitalInOut object connected to the chip's CS/chip select + line. + :param ~digitalio.DigitalInOut reset: A DigitalInOut object connected to the chip's RST/reset + line. + :param int frequency: The center frequency to configure for radio transmission and reception. + Must be a frequency supported by your hardware (i.e. either 433 or 915mhz). + :param bytes sync_word: A byte string up to 8 bytes long which represents the syncronization + word used by received and transmitted packets. Read the datasheet for a full understanding + of this value! However by default the library will set a value that matches the RadioHead + Arduino library. + :param int preamble_length: The number of bytes to pre-pend to a data packet as a preamble. + This is by default 4 to match the RadioHead library. + :param bool high_power: Indicate if the chip is a high power variant that supports boosted + transmission power. The default is True as it supports the common RFM69HCW modules sold by + Adafruit. + + Also note this library tries to be compatible with raw RadioHead Arduino + library communication. This means the library sets up the radio modulation + to match RadioHead's defaults and assumes that each packet contains a + 4 byte header compatible with RadioHead's implementation. + Advanced RadioHead features like address/node specific packets + or "reliable datagram" delivery are supported however due to the + limitations noted, "reliable datagram" is still subject to missed packets but with it, + sender is notified if a packet has potentially been missed. + """ + + operation_mode = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, bits=3) + low_frequency_mode = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, offset=3, bits=1) + modulation_type = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, offset=5, bits=2) + modulation_shaping = RFMSPI.RegisterBits(_RF95_REG_0A_PA_RAMP, offset=5, bits=2) + # Long range/LoRa mode can only be set in sleep mode! + long_range_mode = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, offset=7, bits=1) + sync_on = RFMSPI.RegisterBits(_RF95_REG_27_SYNC_CONFIG, offset=4, bits=1) + sync_size = RFMSPI.RegisterBits(_RF95_REG_27_SYNC_CONFIG, offset=0, bits=3) + output_power = RFMSPI.RegisterBits(_RF95_REG_09_PA_CONFIG, bits=4) + max_power = RFMSPI.RegisterBits(_RF95_REG_09_PA_CONFIG, offset=4, bits=3) + pa_select = RFMSPI.RegisterBits(_RF95_REG_09_PA_CONFIG, offset=7, bits=1) + pa_dac = RFMSPI.RegisterBits(_RF95_REG_4D_PA_DAC, bits=3) + dio0_mapping = RFMSPI.RegisterBits(_RF95_REG_40_DIO_MAPPING1, offset=6, bits=2) + lna_boost_hf = RFMSPI.RegisterBits(_RF95_REG_0C_LNA, offset=0, bits=2) + rx_bw_mantissa = RFMSPI.RegisterBits(_RF95_REG_12_RX_BW, offset=3, bits=2) + rx_bw_exponent = RFMSPI.RegisterBits(_RF95_REG_12_RX_BW, offset=0, bits=3) + afc_bw_mantissa = RFMSPI.RegisterBits(_RF95_REG_13_AFC_BW, offset=3, bits=2) + afc_bw_exponent = RFMSPI.RegisterBits(_RF95_REG_13_AFC_BW, offset=0, bits=3) + packet_format = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=7, bits=1) + dc_free = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=5, bits=2) + crc_on = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=4, bits=1) + crc_auto_clear_off = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=3, bits=1) + address_filter = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=1, bits=2) + crc_type = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=0, bits=1) + mode_ready = RFMSPI.RegisterBits(_RF95_REG_3E_IRQ_FLAGS_1, offset=7) + ook_bit_sync_on = RFMSPI.RegisterBits(_RF95_REG_14_OOK_PEAK, offset=5, bits=1) + ook_thresh_type = RFMSPI.RegisterBits(_RF95_REG_14_OOK_PEAK, offset=4, bits=2) + ook_thresh_step = RFMSPI.RegisterBits(_RF95_REG_14_OOK_PEAK, offset=0, bits=3) + ook_peak_thresh_dec = RFMSPI.RegisterBits(_RF95_REG_16_OOK_AVG, offset=5, bits=3) + ook_average_offset = RFMSPI.RegisterBits(_RF95_REG_16_OOK_AVG, offset=2, bits=2) + ook_average_thresh_filt = RFMSPI.RegisterBits(_RF95_REG_16_OOK_AVG, offset=0, bits=2) + + def __init__( # noqa: PLR0913 + self, + spi: busio.SPI, + cs: digitalio.DigitalInOut, # pylint: disable=invalid-name + rst: digitalio.DigitalInOut, + frequency: int, + *, + sync_word: bytes = b"\x2d\xd4", + preamble_length: int = 4, + high_power: bool = True, + baudrate: int = 5000000, + crc: bool = True, + ) -> None: + super().__init__(spi, cs, baudrate=baudrate) + self.module = "RFM9X" + self.max_packet_length = 252 + self.high_power = high_power + # Device support SPI mode 0 (polarity & phase = 0) up to a max of 10mhz. + # Set Default Baudrate to 5MHz to avoid problems + # self._device = spidev.SPIDevice(spi, cs, baudrate=baudrate, polarity=0, phase=0) + # Setup reset as a digital output - initially High + # This line is pulled low as an output quickly to trigger a reset. + self._rst = rst + # initialize Reset High + self._rst.switch_to_output(value=True) + self.reset() + # No device type check! Catch an error from the very first request and + # throw a nicer message to indicate possible wiring problems. + version = self.read_u8(address=_RF95_REG_42_VERSION) + if version != 18: + raise RuntimeError( + "Failed to find rfm9x with expected version -- check wiring. Version found:", + hex(version), + ) + + # Set sleep mode, wait 10s and confirm in sleep mode (basic device check). + # Also set long range mode (LoRa mode) as it can only be done in sleep. + self.sleep() + time.sleep(0.01) + self.long_range_mode = False + if self.operation_mode != SLEEP_MODE or self.long_range_mode: + raise RuntimeError("Failed to configure radio for FSK mode, check wiring!") + # clear default setting for access to LF registers if frequency > 525MHz + if frequency > 525: + self.low_frequency_mode = 0 + # Set mode idle + self.idle() + # Setup the chip in a similar way to the RadioHead RFM69 library. + # Set FIFO TX condition to not empty and the default FIFO threshold to 15. + self.write_u8(_RF95_REG_35_FIFO_THRESH, 0b10001111) + # Set the syncronization word. + self.sync_word = sync_word + self.preamble_length = preamble_length # Set the preamble length. + self.frequency_mhz = frequency # Set frequency. + # Configure modulation for RadioHead library GFSK_Rb250Fd250 mode + # by default. Users with advanced knowledge can manually reconfigure + # for any other mode (consulting the datasheet is absolutely + # necessary!). + self.modulation_shaping = 0b01 # Gaussian filter, BT=1.0 + self.bitrate = 250000 # 250kbs + self.frequency_deviation = 250000 # 250khz + self.rx_bw_mantissa = 0b00 + self.rx_bw_exponent = 0b000 + self.afc_bw_mantissa = 0b00 + self.afc_bw_exponent = 0b000 + self.packet_format = 1 # Variable length. + self.dc_free = 0b10 # Whitening + # Set transmit power to 13 dBm, a safe value any module supports. + self._tx_power = 13 + self.tx_power = self._tx_power + + # Default to enable CRC checking on incoming packets. + self.enable_crc = crc + """CRC Enable state""" + self.snr = None + + def reset(self) -> None: + """Perform a reset of the chip.""" + # See section 7.2.2 of the datasheet for reset description. + self._rst.value = False # Set Reset Low + time.sleep(0.0001) # 100 us + self._rst.value = True # set Reset High + time.sleep(0.005) # 5 ms + + def idle(self) -> None: + """Enter idle standby mode.""" + self.operation_mode = STANDBY_MODE + + def sleep(self) -> None: + """Enter sleep mode.""" + self.operation_mode = SLEEP_MODE + + def listen(self) -> None: + """Listen for packets to be received by the chip. Use :py:func:`receive` + to listen, wait and retrieve packets as they're available. + """ + self.operation_mode = RX_MODE + self.dio0_mapping = 0b00 # Interrupt on rx done. + + def transmit(self) -> None: + """Transmit a packet which is queued in the FIFO. This is a low level + function for entering transmit mode and more. For generating and + transmitting a packet of data use :py:func:`send` instead. + """ + self.operation_mode = TX_MODE + self.dio0_mapping = 0b00 # Interrupt on tx done. + + @property + def sync_word(self) -> bytearray: + """The synchronization word value. This is a byte string up to 8 bytes long (64 bits) + which indicates the synchronization word for transmitted and received packets. Any + received packet which does not include this sync word will be ignored. The default value + is 0x2D, 0xD4 which matches the RadioHead RFM69 library. Setting a value of None will + disable synchronization word matching entirely. + """ + # Handle when sync word is disabled.. + if not self.sync_on: + return None + # Sync word is not disabled so read the current value. + sync_word_length = self.sync_size + 1 # Sync word size is offset by 1 + # according to datasheet. + sync_word = bytearray(sync_word_length) + self.read_into(_RF95_REG_28_SYNC_VALUE_1, sync_word) + return sync_word + + @sync_word.setter + def sync_word(self, val: Optional[bytearray]) -> None: + # Handle disabling sync word when None value is set. + if val is None: + self.sync_on = 0 + else: + # Check sync word is at most 8 bytes. + assert 1 <= len(val) <= 8 + # Update the value, size and turn on the sync word. + self.write_from(_RF95_REG_28_SYNC_VALUE_1, val) + self.sync_size = len(val) - 1 # Again sync word size is offset by + # 1 according to datasheet. + self.sync_on = 1 + + @property + def bitrate(self) -> float: + """The modulation bitrate in bits/second (or chip rate if Manchester encoding is enabled). + Can be a value from ~489 to 32mbit/s, but see the datasheet for the exact supported + values. + """ + msb = self.read_u8(_RF95_REG_02_BITRATE_MSB) + lsb = self.read_u8(_RF95_REG_03_BITRATE_LSB) + return _RF95_FXOSC / ((msb << 8) | lsb) + + @bitrate.setter + def bitrate(self, val: float) -> None: + assert (_RF95_FXOSC / 65535) <= val <= 32000000.0 + # Round up to the next closest bit-rate value with addition of 0.5. + bitrate = int((_RF95_FXOSC / val) + 0.5) & 0xFFFF + self.write_u8(_RF95_REG_02_BITRATE_MSB, bitrate >> 8) + self.write_u8(_RF95_REG_03_BITRATE_LSB, bitrate & 0xFF) + + @property + def frequency_deviation(self) -> float: + """The frequency deviation in Hertz.""" + msb = self.read_u8(_RF95_REG_04_FDEV_MSB) + lsb = self.read_u8(_RF95_REG_05_FDEV_LSB) + return _RF95_FSTEP * ((msb << 8) | lsb) + + @frequency_deviation.setter + def frequency_deviation(self, val: float) -> None: + assert 0 <= val <= (_RF95_FSTEP * 16383) # fdev is a 14-bit unsigned value + # Round up to the next closest integer value with addition of 0.5. + fdev = int((val / _RF95_FSTEP) + 0.5) & 0x3FFF + self.write_u8(_RF95_REG_04_FDEV_MSB, fdev >> 8) + self.write_u8(_RF95_REG_05_FDEV_LSB, fdev & 0xFF) + + @property + def temperature(self) -> float: + """The internal temperature of the chip.. See Sec 5.5.7 of the DataSheet + calibrated or very accurate. + """ + temp = self.read_u8(_RF95_REG_3C_TEMP) + return temp + + @property + def preamble_length(self) -> int: + """The length of the preamble for sent and received packets, an unsigned + 16-bit value. Received packets must match this length or they are + ignored! Set to 4 to match the RF69. + """ + msb = self.read_u8(_RF95_REG_25_PREAMBLE_MSB) + lsb = self.read_u8(_RF95_REG_26_PREAMBLE_LSB) + return ((msb << 8) | lsb) & 0xFFFF + + @preamble_length.setter + def preamble_length(self, val: int) -> None: + assert 0 <= val <= 65535 + self.write_u8(_RF95_REG_25_PREAMBLE_MSB, (val >> 8) & 0xFF) + self.write_u8(_RF95_REG_26_PREAMBLE_LSB, val & 0xFF) + + @property + def frequency_mhz(self) -> Literal[433.0, 915.0]: + """The frequency of the radio in Megahertz. Only the allowed values for + your radio must be specified (i.e. 433 vs. 915 mhz)! + """ + msb = self.read_u8(_RF95_REG_06_FRF_MSB) + mid = self.read_u8(_RF95_REG_07_FRF_MID) + lsb = self.read_u8(_RF95_REG_08_FRF_LSB) + frf = ((msb << 16) | (mid << 8) | lsb) & 0xFFFFFF + frequency = (frf * _RF95_FSTEP) / 1000000.0 + return frequency + + @frequency_mhz.setter + def frequency_mhz(self, val: Literal[433.0, 915.0]) -> None: + if val < 240 or val > 960: + raise RuntimeError("frequency_mhz must be between 240 and 960") + # Calculate FRF register 24-bit value. + frf = int((val * 1000000.0) / _RF95_FSTEP) & 0xFFFFFF + # Extract byte values and update registers. + msb = frf >> 16 + mid = (frf >> 8) & 0xFF + lsb = frf & 0xFF + self.write_u8(_RF95_REG_06_FRF_MSB, msb) + self.write_u8(_RF95_REG_07_FRF_MID, mid) + self.write_u8(_RF95_REG_08_FRF_LSB, lsb) + + @property + def tx_power(self) -> int: + """The transmit power in dBm. Can be set to a value from 5 to 23 for + high power devices (RFM95/96/97/98, high_power=True) or -1 to 14 for low + power devices. Only integer power levels are actually set (i.e. 12.5 + will result in a value of 12 dBm). + The actual maximum setting for high_power=True is 20dBm but for values > 20 + the PA_BOOST will be enabled resulting in an additional gain of 3dBm. + The actual setting is reduced by 3dBm. + The reported value will reflect the reduced setting. + """ + if self.high_power: + return self.output_power + 5 + return self.output_power - 1 + + @tx_power.setter + def tx_power(self, val: int) -> None: + val = int(val) + if self.high_power: + if val < 5 or val > 23: + raise RuntimeError("tx_power must be between 5 and 23") + # Enable power amp DAC if power is above 20 dB. + # Lower setting by 3db when PA_BOOST enabled - see Data Sheet Section 6.4 + if val > 20: + self.pa_dac = _RF95_PA_DAC_ENABLE + val -= 3 + else: + self.pa_dac = _RF95_PA_DAC_DISABLE + self.pa_select = True + self.output_power = (val - 5) & 0x0F + else: + assert -1 <= val <= 14 + self.pa_select = False + self.max_power = 0b111 # Allow max power output. + self.output_power = (val + 1) & 0x0F + + @property + def rssi(self) -> float: + """The received strength indicator (in dBm) of the last received message.""" + # Read RSSI register and convert to value using formula in datasheet. + # Remember in LoRa mode the payload register changes function to RSSI! + raw_rssi = self.read_u8(_RF95_REG_11_RSSI_VALUE) + return -raw_rssi / 2.0 + + @property + def enable_crc(self) -> bool: + """Set to True to enable hardware CRC checking of incoming packets. + Incoming packets that fail the CRC check are not processed. Set to + False to disable CRC checking and process all incoming packets.""" + return self.crc_on + + @enable_crc.setter + def enable_crc(self, val: bool) -> None: + # Optionally enable CRC checking on incoming packets. + if val: + self.crc_on = 1 + self.crc_type = 0 # use CCITT for RF69 compatibility + else: + self.crc_on = 0 + + def crc_error(self) -> bool: + """crc status""" + return (self.read_u8(_RF95_REG_3F_IRQ_FLAGS_2) & 0x2) >> 1 + + @property + def enable_address_filter(self) -> bool: + """Set to True to enable address filtering. + Incoming packets that do no match the node address or broadcast address + will be ignored.""" + return self.address_filter + + @enable_address_filter.setter + def enable_address_filter(self, val: bool) -> None: + # Enable address filtering on incoming packets. + if val: + self.address_filter = 2 # accept node address or broadcast address + else: + self.address_filter = 0 + + @property + def fsk_node_address(self) -> int: + """Node Address for Address Filtering""" + return self.read_u8(_RF95_REG_33_NODE_ADDR) + + @fsk_node_address.setter + def fsk_node_address(self, val: int) -> None: + assert 0 <= val <= 255 + self.write_u8(_RF95_REG_33_NODE_ADDR, val) + + @property + def fsk_broadcast_address(self) -> int: + """Node Address for Address Filtering""" + return self.read_u8(_RF95_REG_34_BROADCAST_ADDR) + + @fsk_broadcast_address.setter + def fsk_broadcast_address(self, val: int) -> None: + assert 0 <= val <= 255 + self.write_u8(_RF95_REG_34_BROADCAST_ADDR, val) + + @property + def ook_fixed_threshold(self) -> int: + """Fixed threshold for data slicer in OOK mode""" + return self.read_u8(_RF95_REG_15_OOK_FIX) + + @ook_fixed_threshold.setter + def ook_fixed_threshold(self, val: int) -> None: + assert 0 <= val <= 255 + self.write_u8(_RF95_REG_15_OOK_FIX, val) + + def packet_sent(self) -> bool: + """Transmit status""" + return (self.read_u8(_RF95_REG_3F_IRQ_FLAGS_2) & 0x8) >> 3 + + def payload_ready(self) -> bool: + """Receive status""" + return (self.read_u8(_RF95_REG_3F_IRQ_FLAGS_2) & 0x4) >> 2 + + def clear_interrupt(self) -> None: + """Clear interrupt Flags""" + self.write_u8(_RF95_REG_3E_IRQ_FLAGS_1, 0xFF) + self.write_u8(_RF95_REG_3F_IRQ_FLAGS_2, 0xFF) + + def fill_fifo(self, payload: bytearray) -> None: + """Write the payload to the FIFO.""" + complete_payload = bytearray(1) # prepend packet length to payload + complete_payload[0] = len(payload) + # put the payload lengthe in the beginning of the packet for RFM69 + complete_payload = complete_payload + payload + # Write payload to transmit fifo + self.write_from(_RF95_REG_00_FIFO, complete_payload) + + def read_fifo(self) -> bytearray: + """Read the data from the FIFO.""" + # Read the length of the FIFO. + fifo_length = self.read_u8(_RF95_REG_00_FIFO) + # Handle if the received packet is too small to include the 4 byte + # RadioHead header and at least one byte of data --reject this packet and ignore it. + if fifo_length > 0: # read and clear the FIFO if anything in it + packet = bytearray(fifo_length) + # read the packet + self.read_into(_RF95_REG_00_FIFO, packet, fifo_length) + if fifo_length < 5: + packet = None + return packet diff --git a/adafruit_rfm/rfm_common.py b/adafruit_rfm/rfm_common.py new file mode 100644 index 0000000..bfd0f55 --- /dev/null +++ b/adafruit_rfm/rfm_common.py @@ -0,0 +1,539 @@ +# SPDX-FileCopyrightText: 2024 Jerry Needell for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" + +* Author(s): Jerry Needell +""" + +import asyncio +import random +import time + +from adafruit_bus_device import spi_device + +try: + from typing import Callable, Optional, Type + + import busio + import digitalio + from circuitpython_typing import ReadableBuffer, WriteableBuffer + +except ImportError: + pass + +from micropython import const + +HAS_SUPERVISOR = False + +try: + import supervisor + + if hasattr(supervisor, "ticks_ms"): + HAS_SUPERVISOR = True +except ImportError: + pass + + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/jerryneedell/CircuitPython_RFM.git" + + +# RadioHead specific compatibility constants. +_RH_BROADCAST_ADDRESS = const(0xFF) + +# The acknowledgement bit in the FLAGS +# The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved +# for application layer use. +_RH_FLAGS_ACK = const(0x80) +_RH_FLAGS_RETRY = const(0x40) + + +# supervisor.ticks_ms() contants +_TICKS_PERIOD = const(1 << 29) +_TICKS_MAX = const(_TICKS_PERIOD - 1) +_TICKS_HALFPERIOD = const(_TICKS_PERIOD // 2) + + +def ticks_diff(ticks1: int, ticks2: int) -> int: + """Compute the signed difference between two ticks values + assuming that they are within 2**28 ticks + """ + diff = (ticks1 - ticks2) & _TICKS_MAX + diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD + return diff + + +def asyncio_to_blocking(function): + """run async function as normal blocking function""" + + def blocking_function(self, *args, **kwargs): + return asyncio.run(function(self, *args, **kwargs)) + + return blocking_function + + +async def asyncio_check_timeout(flag: Callable, limit: float, timeout_poll: float) -> bool: + """test for timeout waiting for specified flag""" + timed_out = False + if HAS_SUPERVISOR: + start = supervisor.ticks_ms() + while not timed_out and not flag(): + if ticks_diff(supervisor.ticks_ms(), start) >= limit * 1000: + timed_out = True + await asyncio.sleep(timeout_poll) + else: + start = time.monotonic() + while not timed_out and not flag(): + if time.monotonic() - start >= limit: + timed_out = True + await asyncio.sleep(timeout_poll) + + return timed_out + + +# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-nested-blocks +class RFMSPI: + """Base class for SPI type devices""" + + class RegisterBits: + """Simplify register access""" + + # Class to simplify access to the many configuration bits avaialable + # on the chip's registers. This is a subclass here instead of using + # a higher level module to increase the efficiency of memory usage + # (all of the instances of this bit class will share the same buffer + # used by the parent RFM69 class instance vs. each having their own + # buffer and taking too much memory). + + # Quirk of pylint that it requires public methods for a class. This + # is a decorator class in Python and by design it has no public methods. + # Instead it uses dunder accessors like get and set below. For some + # reason pylint can't figure this out so disable the check. + # pylint: disable=too-few-public-methods + + # Again pylint fails to see the true intent of this code and warns + # against private access by calling the write and read functions below. + # This is by design as this is an internally used class. Disable the + # check from pylint. + # pylint: disable=protected-access + + def __init__(self, address: int, *, offset: int = 0, bits: int = 1) -> None: + assert 0 <= offset <= 7 + assert 1 <= bits <= 8 + assert (offset + bits) <= 8 + self._address = address + self._mask = 0 + for _ in range(bits): + self._mask <<= 1 + self._mask |= 1 + self._mask <<= offset + self._offset = offset + + def __get__(self, obj: Optional["RFM"], objtype: Type["RFM"]) -> int: + reg_value = obj.read_u8(self._address) + return (reg_value & self._mask) >> self._offset + + def __set__(self, obj: Optional["RFM"], val: int) -> None: + reg_value = obj.read_u8(self._address) + reg_value &= ~self._mask + reg_value |= (val & 0xFF) << self._offset + obj.write_u8(self._address, reg_value) + + # pylint: disable-msg=too-many-arguments + def __init__( # noqa: PLR0913 + self, + spi: busio.SPI, + cs_pin: digitalio.DigitalInOut, + baudrate: int = 5000000, + polarity: int = 0, + phase: int = 0, + ): + self.spi_device = spi_device.SPIDevice( + spi, cs_pin, baudrate=baudrate, polarity=polarity, phase=phase + ) + # initialize last RSSI reading + self.last_rssi = 0.0 + """The RSSI of the last received packet. Stored when the packet was received. + The instantaneous RSSI value may not be accurate once the + operating mode has been changed. + """ + self.last_snr = 0.0 + """The SNR of the last received packet. Stored when the packet was received. + The instantaneous SNR value may not be accurate once the + operating mode has been changed. + """ + # initialize timeouts and delays delays + self.ack_wait = 0.1 + """The delay time before attempting a retry after not receiving an ACK""" + self.receive_timeout = 0.5 + """The amount of time to poll for a received packet. + If no packet is received, the returned packet will be None + """ + self.xmit_timeout = 2.0 + """The amount of time to wait for the HW to transmit the packet. + This is mainly used to prevent a hang due to a HW issue + """ + self.ack_retries = 5 + """The number of ACK retries before reporting a failure.""" + self.ack_delay = None + """The delay time before attemting to send an ACK. + If ACKs are being missed try setting this to .1 or .2. + """ + # initialize sequence number counter for reliabe datagram mode + self.sequence_number = 0 + # create seen Ids list + self.seen_ids = bytearray(256) + # initialize packet header + # node address - default is broadcast + self.node = _RH_BROADCAST_ADDRESS + """The default address of this Node. (0-255). + If not 255 (0xff) then only packets address to this node will be accepted. + First byte of the RadioHead header. + """ + # destination address - default is broadcast + self.destination = _RH_BROADCAST_ADDRESS + """The default destination address for packet transmissions. (0-255). + If 255 (0xff) then any receiving node should accept the packet. + Second byte of the RadioHead header. + """ + # ID - contains seq count for reliable datagram mode + self.identifier = 0 + """Automatically set to the sequence number when send_with_ack() used. + Third byte of the RadioHead header. + """ + # flags - identifies ack/reetry packet for reliable datagram mode + self.flags = 0 + """Upper 4 bits reserved for use by Reliable Datagram Mode. + Lower 4 bits may be used to pass information. + Fourth byte of the RadioHead header. + """ + self.radiohead = True + """Enable RadioHead compatibility""" + + self.crc_error_count = 0 + self.timeout_poll = 0.001 + + # pylint: enable-msg=too-many-arguments + + # Global buffer for SPI commands + _BUFFER = bytearray(4) + + # pylint: disable=no-member + # Reconsider pylint: disable when this can be tested + def read_into(self, address: int, buf: WriteableBuffer, length: Optional[int] = None) -> None: + """Read a number of bytes from the specified address into the provided + buffer. If length is not specified (the default) the entire buffer + will be filled.""" + if length is None: + length = len(buf) + with self.spi_device as device: + self._BUFFER[0] = address & 0x7F # Strip out top bit to set 0 + # value (read). + device.write(self._BUFFER, end=1) + device.readinto(buf, end=length) + + def read_u8(self, address: int) -> int: + """Read a single byte from the provided address and return it.""" + self.read_into(address, self._BUFFER, length=1) + return self._BUFFER[0] + + def write_from(self, address: int, buf: ReadableBuffer, length: Optional[int] = None) -> None: + """Write a number of bytes to the provided address and taken from the + provided buffer. If no length is specified (the default) the entire + buffer is written.""" + if length is None: + length = len(buf) + with self.spi_device as device: + self._BUFFER[0] = (address | 0x80) & 0xFF # Set top bit to 1 to + # indicate a write. + device.write(self._BUFFER, end=1) + device.write(buf, end=length) + + def write_u8(self, address: int, val: int) -> None: + """Write a byte register to the chip. Specify the 7-bit address and the + 8-bit value to write to that address.""" + with self.spi_device as device: + self._BUFFER[0] = (address | 0x80) & 0xFF # Set top bit to 1 to indicate a write. + self._BUFFER[1] = val & 0xFF + device.write(self._BUFFER, end=2) + + # pylint: disable=too-many-branches + + async def asyncio_send( # noqa: PLR0912 PLR0913 + self, + data: ReadableBuffer, + *, + keep_listening: bool = False, + destination: Optional[int] = None, + node: Optional[int] = None, + identifier: Optional[int] = None, + flags: Optional[int] = None, + ) -> bool: + """Send a string of data using the transmitter. + You can only send 252 bytes at a time + (limited by chip's FIFO size and appended headers). + if the propert radiohead is True then this appends a 4 byte header + to be compatible with the RadioHead library. + The header defaults to using the initialized attributes: + (destination,node,identifier,flags) + It may be temporarily overidden via the kwargs - destination,node,identifier,flags. + Values passed via kwargs do not alter the attribute settings. + The keep_listening argument should be set to True if you want to start listening + automatically after the packet is sent. The default setting is False. + + Returns: True if success or False if the send timed out. + """ + # Disable pylint warning to not use length as a check for zero. + # This is a puzzling warning as the below code is clearly the most + # efficient and proper way to ensure a precondition that the provided + # buffer be within an expected range of bounds. Disable this check. + # pylint: disable=len-as-condition + assert 0 < len(data) <= self.max_packet_length + # pylint: enable=len-as-condition + self.idle() # Stop receiving to clear FIFO and keep it clear. + # Combine header and data to form payload + if self.radiohead: + payload = bytearray(4) + if destination is None: # use attribute + payload[0] = self.destination + else: # use kwarg + payload[0] = destination + if node is None: # use attribute + payload[1] = self.node + else: # use kwarg + payload[1] = node + if identifier is None: # use attribute + payload[2] = self.identifier + else: # use kwarg + payload[2] = identifier + if flags is None: # use attribute + payload[3] = self.flags + else: # use kwarg + payload[3] = flags + payload = payload + data + elif destination is not None: # prepend destination for non RH packets + payload = destination.to_bytes(1, "big") + data + else: + payload = data + self.fill_fifo(payload) + # Turn on transmit mode to send out the packet. + self.transmit() + # Wait for packet_sent interrupt with explicit polling (not ideal but + # best that can be done right now without interrupts). + timed_out = await asyncio_check_timeout( + self.packet_sent, self.xmit_timeout, self.timeout_poll + ) + # Listen again if necessary and return the result packet. + if keep_listening: + self.listen() + else: + # Enter idle mode to stop receiving other packets. + self.idle() + self.clear_interrupt() + return not timed_out + + send = asyncio_to_blocking(asyncio_send) + + async def asyncio_send_with_ack(self, data: ReadableBuffer) -> bool: + """Reliable Datagram mode: + Send a packet with data and wait for an ACK response. + The packet header is automatically generated. + If enabled, the packet transmission will be retried on failure + """ + if not self.radiohead: + raise RuntimeError("send_with_ack onl suppoted in RadioHead mode") + if self.ack_retries: + retries_remaining = self.ack_retries + else: + retries_remaining = 1 + got_ack = False + self.sequence_number = (self.sequence_number + 1) & 0xFF + while not got_ack and retries_remaining: + self.identifier = self.sequence_number + await self.asyncio_send(data, keep_listening=True) + # Don't look for ACK from Broadcast message + if self.destination == _RH_BROADCAST_ADDRESS: + got_ack = True + else: + # wait for a packet from our destination + ack_packet = await self.asyncio_receive(timeout=self.ack_wait, with_header=True) + if ack_packet is not None: + if ack_packet[3] & _RH_FLAGS_ACK: + # check the ID + if ack_packet[2] == self.identifier: + got_ack = True + break + # pause before next retry -- random delay + if not got_ack: + # delay by random amount before next try + await asyncio.sleep(self.ack_wait + self.ack_wait * random.random()) + retries_remaining = retries_remaining - 1 + # set retry flag in packet header + self.flags |= _RH_FLAGS_RETRY + self.flags = 0 # clear flags + return got_ack + + send_with_ack = asyncio_to_blocking(asyncio_send_with_ack) + + async def asyncio_receive( + self, + *, + keep_listening: bool = True, + with_header: bool = False, + timeout: Optional[float] = None, + ) -> Optional[bytearray]: + """Wait to receive a packet from the receiver. If a packet is found the payload bytes + are returned, otherwise None is returned (which indicates the timeout elapsed with no + reception). + If keep_listening is True (the default) the chip will immediately enter listening mode + after reception of a packet, otherwise it will fall back to idle mode and ignore any + future reception. + Packets may have a 4-byte header for compatibility with the + RadioHead library. + The header consists of 4 bytes (To,From,ID,Flags). The default setting will strip + the header before returning the packet to the caller. + If with_header is True then the 4 byte header will be returned with the packet. + The payload then begins at packet[4]. + """ + if not self.radiohead and with_header: + raise RuntimeError("with_header only supported for RadioHead mode") + timed_out = False + if timeout is None: + timeout = self.receive_timeout + if timeout is not None: + # Wait for the payloadready signal. This is not ideal and will + # surely miss or overflow the FIFO when packets aren't read fast + # enough, however it's the best that can be done from Python without + # interrupt supports. + # Make sure we are listening for packets. + self.listen() + timed_out = await asyncio_check_timeout(self.payload_ready, timeout, self.timeout_poll) + # Payload ready is set, a packet is in the FIFO. + packet = None + # save last RSSI reading + self.last_rssi = self.rssi + self.last_snr = self.snr + + # Enter idle mode to stop receiving other packets. + self.idle() + if not timed_out: + if self.enable_crc and self.crc_error(): + self.crc_error_count += 1 + else: + packet = self.read_fifo() + if self.radiohead: + if packet is not None: + if ( + self.node != _RH_BROADCAST_ADDRESS # noqa: PLR1714 + and packet[0] != _RH_BROADCAST_ADDRESS + and packet[0] != self.node + ): + packet = None + if not with_header and packet is not None: # skip the header if not wanted + packet = packet[4:] + # Listen again if necessary and return the result packet. + if keep_listening: + self.listen() + else: + # Enter idle mode to stop receiving other packets. + self.idle() + self.clear_interrupt() + return packet + + receive = asyncio_to_blocking(asyncio_receive) + + async def asyncio_receive_with_ack( # noqa: PLR0912 + self, + *, + keep_listening: bool = True, + with_header: bool = False, + timeout: Optional[float] = None, + ) -> Optional[bytearray]: + """Wait to receive a RadioHead packet from the receiver then send an ACK packet in response. + AKA Reliable Datagram mode. + If a packet is found the payload bytes are returned, otherwise None is returned + (which indicates the timeout elapsed with no reception). + If keep_listening is True (the default) the chip will immediately enter listening mode + after receipt of a packet, otherwise it will fall back to idle mode and ignore + any incomming packets until it is called again. + All packets must have a 4-byte header for compatibility with the RadioHead library. + The header consists of 4 bytes (To,From,ID,Flags). The default setting will strip + the header before returning the packet to the caller. + If with_header is True then the 4 byte header will be returned with the packet. + The payload then begins at packet[4]. + """ + if not self.radiohead: + raise RuntimeError("receive_with_ack only supported for RadioHead mode") + timed_out = False + if timeout is None: + timeout = self.receive_timeout + if timeout is not None: + # Wait for the payloadready signal. This is not ideal and will + # surely miss or overflow the FIFO when packets aren't read fast + # enough, however it's the best that can be done from Python without + # interrupt supports. + # Make sure we are listening for packets. + self.listen() + timed_out = await asyncio_check_timeout(self.payload_ready, timeout, self.timeout_poll) + # Payload ready is set, a packet is in the FIFO. + packet = None + # save last RSSI reading + self.last_rssi = self.rssi + self.last_snr = self.snr + + # Enter idle mode to stop receiving other packets. + self.idle() + if not timed_out: + if self.enable_crc and self.crc_error(): + self.crc_error_count += 1 + else: + packet = self.read_fifo() + if self.radiohead: + if packet is not None: + if ( + self.node != _RH_BROADCAST_ADDRESS # noqa: PLR1714 + and packet[0] != _RH_BROADCAST_ADDRESS + and packet[0] != self.node + ): + packet = None + # send ACK unless this was an ACK or a broadcast + elif ((packet[3] & _RH_FLAGS_ACK) == 0) and ( + packet[0] != _RH_BROADCAST_ADDRESS + ): + # delay before sending Ack to give receiver a chance to get ready + if self.ack_delay is not None: + await asyncio.sleep(self.ack_delay) + # send ACK packet to sender (data is b'!') + await self.asyncio_send( + b"!", + destination=packet[1], + node=packet[0], + identifier=packet[2], + flags=(packet[3] | _RH_FLAGS_ACK), + ) + # reject Retries if we have seen this idetifier from this source before + if (self.seen_ids[packet[1]] == packet[2]) and ( + packet[3] & _RH_FLAGS_RETRY + ): + packet = None + else: # save the packet identifier for this source + self.seen_ids[packet[1]] = packet[2] + if ( + packet is not None and (packet[3] & _RH_FLAGS_ACK) != 0 + ): # Ignore it if it was an ACK packet + packet = None + if not with_header and packet is not None: # skip the header if not wanted + packet = packet[4:] + # Listen again if necessary and return the result packet. + if keep_listening: + self.listen() + else: + # Enter idle mode to stop receiving other packets. + self.idle() + self.clear_interrupt() + return packet + + receive_with_ack = asyncio_to_blocking(asyncio_receive_with_ack) diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5aca98376a1f7e593ebd9cf41a808512c2135635 GIT binary patch literal 4414 zcmd^BX;4#F6n=SG-XmlONeGrD5E6J{RVh+e928U#MG!$jWvO+UsvWh`x&VqGNx*en zx=qox7Dqv{kPwo%fZC$dDwVpRtz{HzTkSs8QhG0)%Y=-3@Kt!4ag|JcIo?$-F|?bXVS9UDUyev>MVZQ(H8K4#;BQW-t2CPorj8^KJrMX}QK zp+e<;4ldpXz~=)2GxNy811&)gt-}Q*yVQpsxr@VMoA##{)$1~=bZ1MmjeFw?uT(`8 z^g=09<=zW%r%buwN%iHtuKSg|+r7HkT0PYN*_u9k1;^Ss-Z!RBfJ?Un4w(awqp2b3 z%+myoFis_lTlCrGx2z$0BQdh+7?!JK#9K9@Z!VrG zNj6gK5r(b4?YDOLw|DPRoN7bdP{(>GEG41YcN~4r_SUHU2hgVtUwZG@s%edC;k7Sn zC)RvEnlq~raE2mY2ko64^m1KQL}3riixh?#J{o)IT+K-RdHae2eRX91-+g!y`8^># z-zI0ir>P%Xon)!@xp-BK2bDYUB9k613NRrY6%lVjbFcQc*pRqiK~8xtkNPLxt}e?&QsTB}^!39t_%Qb)~Ukn0O%iC;zt z<&A-y;3h++)>c1br`5VFM~5(83!HKx$L+my8sW_c#@x*|*vB1yU)_dt3vH;2hqPWx zAl^6@?ipx&U7pf`a*>Yq6C85nb+B=Fnn+(id$W#WB^uHAcZVG`qg;rWB}ubvi(Y>D z$ei>REw$#xp0SHAd^|1hq&9HJ=jKK8^zTH~nk)G?yUcmTh9vUM6Y0LMw4(gYVY$D$ zGl&WY&H<)BbJ&3sYbKjx1j^=3-0Q#f^}(aP1?8^`&FUWMp|rmtpK)bLQ1Zo?^s4jqK=Lfg*9&geMGVQ z#^-*!V`fG@;H&{M9S8%+;|h&Qrxym0Ar>WT4BCVLR8cGXF=JmEYN(sNT(9vl+S|%g z8r7nXQ(95i^`=+XHo|){$vf2$?=`F$^&wFlYXyXg$B{a>$-Fp+V}+D;9k=~Xl~?C4 zAB-;RKXdUzBJE{V&d&%R>aEfFe;vxqI$0@hwVM}gFeQR@j}a>DDxR+n+-*6|_)k%% z*mSpDV|=5I9!&VC&9tD%fcVygWZV!iIo2qFtm#!*(s|@ZT33*Ad;+<|3^+yrp*;oH zBSYLV(H1zTU?2WjrCQoQW)Z>J2a=dTriuvezBmu16`tM2fm7Q@d4^iqII-xFpwHGI zn9CL}QE*1vdj2PX{PIuqOe5dracsciH6OlAZATvE8rj6ykqdIjal2 z0S0S~PwHb-5?OQ-tU-^KTG@XNrEVSvo|HIP?H;7ZhYeZkhSqh-{reE!5di;1zk$#Y zCe7rOnlzFYJ6Z#Hm$GoidKB=2HBCwm`BbZVeZY4ukmG%1uz7p2URs6c9j-Gjj^oQV zsdDb3@k2e`C$1I5ML5U0Qs0C1GAp^?!*`=|Nm(vWz3j*j*8ucum2;r0^-6Aca=Gv) zc%}&;!+_*S2tlnnJnz0EKeRmw-Y!@9ob!XQBwiv}^u9MkaXHvM=!<3YX;+2#5Cj5pp?FEK750S3BgeSDtaE^ zXUM@xoV6yBFKfzvY20V&Lr0yC + Download Library Bundle + CircuitPython Reference Documentation + CircuitPython Support Forum + Discord Chat + Adafruit Learning System + Adafruit Blog + Adafruit Store + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/index.rst.license b/docs/index.rst.license new file mode 100644 index 0000000..c7437b1 --- /dev/null +++ b/docs/index.rst.license @@ -0,0 +1,4 @@ +SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +SPDX-FileCopyrightText: Copyright (c) 2024 Jerry Needell for Adafruit Industries + +SPDX-License-Identifier: MIT diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..979f568 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +sphinx +sphinxcontrib-jquery +sphinx-rtd-theme diff --git a/examples/rfm69_node1.py b/examples/rfm69_node1.py new file mode 100644 index 0000000..7e540e4 --- /dev/null +++ b/examples/rfm69_node1.py @@ -0,0 +1,64 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically + +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# set the time interval (seconds) for sending packets +transmit_interval = 5 +node = 1 +destination = 2 +rfm69.radiohead = False +rfm69.enable_address_filter = True +rfm69.fsk_node_address = node +rfm69.fsk_broadcast_address = 0xFF +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = None +# rfm69.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm69.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 5 seconds: + packet = rfm69.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter = counter + 1 + rfm69.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) diff --git a/examples/rfm69_raw.py b/examples/rfm69_raw.py new file mode 100644 index 0000000..d0a15c3 --- /dev/null +++ b/examples/rfm69_raw.py @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# set the time interval (seconds) for sending packets +rfm69.radiohead = False +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = None +# rfm69.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # Look for a new packet - wait up to 5 seconds: + packet = rfm69.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:]]) + print(f"RSSI: {rfm69.last_rssi}") diff --git a/examples/rfm69_rh_asyncio_node2.py b/examples/rfm69_rh_asyncio_node2.py new file mode 100644 index 0000000..96959af --- /dev/null +++ b/examples/rfm69_rh_asyncio_node2.py @@ -0,0 +1,109 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import asyncio +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm69.node = 2 +rfm69.destination = 100 +rfm69.ack_wait = 0.4 +# send startup message from my_node +# rfm69.send_with_ack(bytes("startup message from node {}".format(rfm69.node), "UTF-8")) +rfm69.listen() +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +# pylint: disable=too-few-public-methods +class Packet: + """Simple class to hold an value. Use .value to to read or write.""" + + def __init__(self): + self.received = False + + +# setup interrupt callback function +async def wait_for_packets(packet_status, lock): + while True: + if rfm69.payload_ready(): + if lock.locked(): + print("locked waiting for receive") + async with lock: + packet = await rfm69.asyncio_receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_status.received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm69.last_rssi}") + await asyncio.sleep(0.001) + + +async def send_packets(packet_status, lock): + # initialize counter + counter = 0 + ack_failed_counter = 0 + counter = 0 + transmit_interval = 5 + time_now = time.monotonic() + while True: + # If no packet was received during the timeout then None is returned. + if packet_status.received: + packet_status.received = False + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter += 1 + # send a mesage to destination_node from my_node + if lock.locked(): + print("locked waiting for send") + async with lock: + if not await rfm69.asyncio_send_with_ack( + bytes( + f"message from node {rfm69.node} {counter} {ack_failed_counter}", + "UTF-8", + ) + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) + await asyncio.sleep(0.001) + + +async def main(): + packet_status = Packet() + lock = asyncio.Lock() + task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) + task2 = asyncio.create_task(send_packets(packet_status, lock)) + + await asyncio.gather(task1, task2) # Don't forget "await"! + + +asyncio.run(main()) diff --git a/examples/rfm69_rh_header.py b/examples/rfm69_rh_header.py new file mode 100644 index 0000000..c3224ef --- /dev/null +++ b/examples/rfm69_rh_header.py @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to display raw packets including header + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + + +# set the time interval (seconds) for sending packets +transmit_interval = 10 + +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm69.receive(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"RSSI: {rfm69.last_rssi}") + # send reading after any packet received diff --git a/examples/rfm69_rh_node1.py b/examples/rfm69_rh_node1.py new file mode 100644 index 0000000..5ca9a95 --- /dev/null +++ b/examples/rfm69_rh_node1.py @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically between addressed nodes + +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set the time interval (seconds) for sending packets +transmit_interval = 10 +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" + +# set node addresses +rfm69.node = 1 +rfm69.destination = 2 +# initialize counter +counter = 0 +# send a broadcast message from my_node with ID = counter +rfm69.send(bytes(f"Startup message {counter} from node {rfm69.node}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +now = time.monotonic() +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm69.receive(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"Received RSSI: {rfm69.last_rssi}") + if time.monotonic() - now > transmit_interval: + now = time.monotonic() + counter = counter + 1 + # send a mesage to destination_node from my_node + rfm69.send( + bytes(f"message number {counter} from node {rfm69.node}", "UTF-8"), + keep_listening=True, + ) + button_pressed = None diff --git a/examples/rfm69_rh_node1_ack.py b/examples/rfm69_rh_node1_ack.py new file mode 100644 index 0000000..16b7474 --- /dev/null +++ b/examples/rfm69_rh_node1_ack.py @@ -0,0 +1,71 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically between addressed nodes with ACK + +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + + +# set the time interval (seconds) for sending packets +transmit_interval = 10 + +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" + +# set delay before sending ACK +rfm69.ack_delay = 0.1 +# set node addresses +rfm69.node = 1 +rfm69.destination = 2 +# initialize counter +counter = 0 +ack_failed_counter = 0 +# send startup message from my_node +rfm69.send_with_ack(bytes(f"startup message from node {rfm69.node}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm69.receive_with_ack(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"RSSI: {rfm69.last_rssi}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter += 1 + # send a mesage to destination_node from my_node + if not rfm69.send_with_ack( + bytes(f"message from node node {rfm69.node} {counter}", "UTF-8") + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm69_rh_node2.py b/examples/rfm69_rh_node2.py new file mode 100644 index 0000000..5b1a416 --- /dev/null +++ b/examples/rfm69_rh_node2.py @@ -0,0 +1,67 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically between addressed nodes + +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + + +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" + +# set node addresses +rfm69.node = 2 +rfm69.destination = 1 +# initialize counter +counter = 0 +# send a broadcast message from my_node with ID = counter +rfm69.send(bytes(f"startup message from node {rfm69.node} ", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm69.receive(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"Received RSSI: {rfm69.last_rssi}") + # send reading after any packet received + counter = counter + 1 + # after 10 messages send a response to destination_node from my_node with ID = counter&0xff + if counter % 10 == 0: + time.sleep(0.5) # brief delay before responding + rfm69.identifier = counter & 0xFF + rfm69.send( + bytes( + f"message number {counter} from node {rfm69.node} ", + "UTF-8", + ), + keep_listening=True, + ) diff --git a/examples/rfm69_rh_node2_ack.py b/examples/rfm69_rh_node2_ack.py new file mode 100644 index 0000000..e824b69 --- /dev/null +++ b/examples/rfm69_rh_node2_ack.py @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to receive addressed packed with ACK and send a response + +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" + +# set delay before transmitting ACK (seconds) +rfm69.ack_delay = 0.1 +# set node addresses +rfm69.node = 2 +rfm69.destination = 1 +# initialize counter +counter = 0 +ack_failed_counter = 0 + +# Wait to receive packets. +print("Waiting for packets...") +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm69.receive_with_ack(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"RSSI: {rfm69.last_rssi}") + # send response 2 sec after any packet received + time.sleep(2) + counter += 1 + # send a mesage to destination_node from my_node + if not rfm69.send_with_ack(bytes(f"response from node {rfm69.node} {counter}", "UTF-8")): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm69_rh_simpletest.py b/examples/rfm69_rh_simpletest.py new file mode 100644 index 0000000..698635a --- /dev/null +++ b/examples/rfm69_rh_simpletest.py @@ -0,0 +1,69 @@ +# SPDX-FileCopyrightText: 2024 Ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Simple demo of sending and recieving data with the RFM69 LoRa radio. +# Author: Jerry Needell +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" + +# Print out some chip state: +print(f"Temperature: {rfm69.temperature}C") +print(f"Frequency: {rfm69.frequency_mhz}mhz") +print(f"Bit rate: {rfm69.bitrate / 1000}kbit/s") +print(f"Frequency deviation: {rfm69.frequency_deviation}hz") + +# Send a packet. Note you can only send a packet up to 60 bytes in length. +# This is a limitation of the radio packet size, so if you need to send larger +# amounts of data you will need to break it into smaller send calls. Each send +# call will wait for the previous one to finish before continuing. +rfm69.send(bytes("Hello world!\r\n", "utf-8")) +print("Sent hello world message!") + +# Wait to receive packets. Note that this library can't receive data at a fast +# rate, in fact it can only receive and process one 60 byte packet at a time. +# This means you should only use this for low bandwidth scenarios, like sending +# and receiving a single message at a time. +print("Waiting for packets...") +while True: + packet = rfm69.receive() + # Optionally change the receive timeout from its default of 0.5 seconds: + # packet = rfm69.receive(timeout=5.0) + # If no packet was received during the timeout then None is returned. + if packet is None: + # Packet has not been received + print("Received nothing! Listening again...") + else: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # And decode to ASCII text and print it too. Note that you always + # receive raw bytes and need to convert to a text format like ASCII + # if you intend to do string processing on your data. Make sure the + # sending side is sending ASCII data before you try to decode! + try: + packet_text = str(packet, "ascii") + print(f"Received (ASCII): {packet_text}") + except UnicodeError: + print("Hex data: ", [hex(x) for x in packet]) + rssi = rfm69.last_rssi + print(f"Received signal strength: {rssi} dB") diff --git a/examples/rfm69_rh_transmit.py b/examples/rfm69_rh_transmit.py new file mode 100644 index 0000000..cf2f903 --- /dev/null +++ b/examples/rfm69_rh_transmit.py @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically + +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = None +# rfm69.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm69.send(bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +send_reading = False +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 5 seconds: + packet = rfm69.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + # clear flag to send data + send_reading = False + counter = counter + 1 + rfm69.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm69_transmit.py b/examples/rfm69_transmit.py new file mode 100644 index 0000000..dc94c0d --- /dev/null +++ b/examples/rfm69_transmit.py @@ -0,0 +1,76 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# set the time interval (seconds) for sending packets +node = 1 +destination = 2 +rfm69.radiohead = False +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = None +# rfm69.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM69 can go up to 20 dB: +# rfm69.tx_power = 20 + + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm69.send(bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +send_reading = False +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm69.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + # clear flag to send data + send_reading = False + counter = counter + 1 + rfm69.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm69ook_raw.py b/examples/rfm69ook_raw.py new file mode 100644 index 0000000..6dc1a9c --- /dev/null +++ b/examples/rfm69ook_raw.py @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# set the time interval (seconds) for sending packets +node = 1 +destination = 2 +rfm69.radiohead = False +print(rfm69.modulation_type) +rfm69.modulation_type = 1 +print(rfm69.modulation_type) +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = None +# rfm69.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # Look for a new packet - wait up to 5 seconds: + packet = rfm69.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:]]) + print(f"RSSI: {rfm69.last_rssi}") diff --git a/examples/rfm69ook_transmit.py b/examples/rfm69ook_transmit.py new file mode 100644 index 0000000..566a8d7 --- /dev/null +++ b/examples/rfm69ook_transmit.py @@ -0,0 +1,79 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm69 + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + + +# Initialze RFM radio +rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# set the time interval (seconds) for sending packets +node = 1 +destination = 2 +rfm69.radiohead = False +print(rfm69.modulation_type) +rfm69.modulation_type = 1 +print(rfm69.modulation_type) +# Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +rfm69.encryption_key = None +# rfm69.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM69 can go up to 20 dB: +# rfm69.tx_power = 20 + + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm69.send(bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +send_reading = False +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm69.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + # clear flag to send data + send_reading = False + counter = counter + 1 + rfm69.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9x_rh_asyncio_gps_node2.py b/examples/rfm9x_rh_asyncio_gps_node2.py new file mode 100644 index 0000000..c782c98 --- /dev/null +++ b/examples/rfm9x_rh_asyncio_gps_node2.py @@ -0,0 +1,161 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import asyncio +import time + +import adafruit_gps +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=10) + +# Create a GPS module instance. +gps = adafruit_gps.GPS(uart, debug=False) # Use UART/pyserial + +# Turn on the basic GGA and RMC info (what you typically want) +gps.send_command(b"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0") + +# Set update rate to once a second (1hz) which is what you typically want. +gps.send_command(b"PMTK220,1000") + + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.D10) +RESET = digitalio.DigitalInOut(board.D11) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm9x.node = 2 +rfm9x.destination = 100 +# send startup message from my_node +# rfm9x.send_with_ack(bytes("startup message from node {}".format(rfm9x.node), "UTF-8")) +rfm9x.listen() +# rfm9x.xmit_timeout = 5. +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +# pylint: disable=too-few-public-methods +class Packet: + """Simple class to hold an value. Use .value to to read or write.""" + + def __init__(self): + self.received = False + self.gps_time = "Unknown" + self.gps_latitude = 0.0 + self.gps_longitude = 0.0 + + +async def read_gps(packet_status, wait): + # Main loop runs forever printing the location, etc. every second. + last_print = time.monotonic() + while True: + # Make sure to call gps.update() every loop iteration and at least twice + # as fast as data comes from the GPS unit (usually every second). + # This returns a bool that's true if it parsed new data (you can ignore it + # though if you don't care and instead look at the has_fix property). + gps.update() + # Every second print out current location details if there's a fix. + current = time.monotonic() + if current - last_print >= 1.0: + last_print = current + if not gps.has_fix: + # Try again if we don't have a fix yet. + print("Waiting for fix...") + packet_status.gps_time = "Unknown" + packet_status.gps_latitude = 0.0 + packet_status.gps_longitude = 0.0 + continue + # We have a fix! (gps.has_fix is true) + # Print out details about the fix like location, date, etc. + print("=" * 40) # Print a separator line. + packet_status.gps_time = f"Fix timestamp: {gps.timestamp_utc.tm_mon}/{gps.timestamp_utc.tm_mday}/{gps.timestamp_utc.tm_year} {gps.timestamp_utc.tm_hour:02}:{gps.timestamp_utc.tm_min:02}:{gps.timestamp_utc.tm_sec:02}" # noqa: E501 + print(packet_status.gps_time) + print(f"Latitude: {gps.latitude:.6f} degrees") + print(f"Longitude: {gps.longitude:.6f} degrees") + packet_status.gps_latitude = gps.latitude + packet_status.gps_longitude = gps.longitude + if gps.satellites is not None: + print(f"# satellites: {gps.satellites}") + if gps.altitude_m is not None: + print(f"Altitude: {gps.altitude_m} meters") + await asyncio.sleep(wait) + + +async def wait_for_packets(packet_status, lock): + while True: + if rfm9x.payload_ready(): + if lock.locked(): + print("locked waiting for receive") + async with lock: + packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_status.received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + await asyncio.sleep(0.001) + + +async def send_packets(packet_status, lock): + # initialize counter + counter = 0 + ack_failed_counter = 0 + counter = 0 + transmit_interval = 5 + time_now = time.monotonic() + while True: + # If no packet was received during the timeout then None is returned. + if packet_status.received: + packet_status.received = False + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter += 1 + # send a mesage to destination_node from my_node + if lock.locked(): + print("locked waiting for send") + async with lock: + if not await rfm9x.asyncio_send_with_ack( + bytes( + f"message from node {rfm9x.node} {counter} {ack_failed_counter} {packet_status.gps_time} {packet_status.gps_latitude:.6f} {packet_status.gps_longitude:.6f}", # noqa: E501 + "UTF-8", + ) + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) + await asyncio.sleep(0.1) + + +async def main(): + packet_status = Packet() + lock = asyncio.Lock() + task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) + task2 = asyncio.create_task(send_packets(packet_status, lock)) + task3 = asyncio.create_task(read_gps(packet_status, 0.25)) + + await asyncio.gather(task1, task2, task3) # Don't forget "await"! + + +asyncio.run(main()) diff --git a/examples/rfm9x_rh_asyncio_listen.py b/examples/rfm9x_rh_asyncio_listen.py new file mode 100644 index 0000000..ec405dc --- /dev/null +++ b/examples/rfm9x_rh_asyncio_listen.py @@ -0,0 +1,69 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import asyncio + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# send startup message from my_node +# rfm9x.send(bytes("startup message from node {}".format(rfm9x.node), "UTF-8")) +rfm9x.listen() +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +# pylint: disable=too-few-public-methods +class Packet: + """Simple class to hold an value. Use .value to to read or write.""" + + def __init__(self): + self.received = False + + +# setup interrupt callback function +async def wait_for_packets(packet_status): + while True: + if rfm9x.payload_ready(): + packet = await rfm9x.asyncio_receive(with_header=True, timeout=None) + if packet is not None: + packet_status.received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + await asyncio.sleep(0.001) + + +async def main(): + packet_status = Packet() + task1 = asyncio.create_task(wait_for_packets(packet_status)) + + await asyncio.gather(task1) # Don't forget "await"! + + +asyncio.run(main()) diff --git a/examples/rfm9x_rh_asyncio_neopix_node2.py b/examples/rfm9x_rh_asyncio_neopix_node2.py new file mode 100644 index 0000000..1bda71c --- /dev/null +++ b/examples/rfm9x_rh_asyncio_neopix_node2.py @@ -0,0 +1,162 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import asyncio +import time + +import board +import busio +import digitalio +import neopixel + +from adafruit_rfm import rfm9x + +# On CircuitPlayground Express, and boards with built in status NeoPixel -> board.NEOPIXEL +# Otherwise choose an open pin connected to the Data In of the NeoPixel strip, i.e. board.D1 +pixel_pin = board.D5 + +# On a Raspberry pi, use this instead, not all pins are supported +# pixel_pin = board.D18 + +# The number of NeoPixels +num_pixels = 32 + +# The order of the pixel colors - RGB or GRB. Some NeoPixels have red and green reversed! +# For RGBW NeoPixels, simply change the ORDER to RGBW or GRBW. +ORDER = neopixel.GRB + +pixels = neopixel.NeoPixel( + pixel_pin, num_pixels, brightness=0.2, auto_write=False, pixel_order=ORDER +) + + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.D10) +RESET = digitalio.DigitalInOut(board.D11) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm9x.node = 2 +rfm9x.destination = 100 +# send startup message from my_node +# rfm9x.send_with_ack(bytes("startup message from node {}".format(rfm9x.node), "UTF-8")) +rfm9x.listen() +# rfm9x.xmit_timeout = 5. +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +# pylint: disable=too-few-public-methods +class Packet: + """Simple class to hold an value. Use .value to to read or write.""" + + def __init__(self): + self.received = False + + +def wheel(pos): + # Input a value 0 to 255 to get a color value. + # The colours are a transition r - g - b - back to r. + if pos < 0 or pos > 255: + r = g = b = 0 + elif pos < 85: + r = int(pos * 3) + g = int(255 - pos * 3) + b = 0 + elif pos < 170: + pos -= 85 + r = int(255 - pos * 3) + g = 0 + b = int(pos * 3) + else: + pos -= 170 + r = 0 + g = int(pos * 3) + b = int(255 - pos * 3) + return (r, g, b) if ORDER in (neopixel.RGB, neopixel.GRB) else (r, g, b, 0) + + +async def rainbow_cycle(wait, lock): + while True: + if not lock.locked(): + for j in range(255): + for i in range(num_pixels): + pixel_index = (i * 256 // num_pixels) + j + pixels[i] = wheel(pixel_index & 255) + pixels.show() + await asyncio.sleep(wait) + + +async def wait_for_packets(packet_status, lock): + while True: + if rfm9x.payload_ready(): + if lock.locked(): + print("locked waiting for receive") + async with lock: + packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_status.received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + await asyncio.sleep(0.001) + + +async def send_packets(packet_status, lock): + # initialize counter + counter = 0 + ack_failed_counter = 0 + counter = 0 + transmit_interval = 5 + time_now = time.monotonic() + while True: + # If no packet was received during the timeout then None is returned. + if packet_status.received: + packet_status.received = False + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter += 1 + # send a mesage to destination_node from my_node + if lock.locked(): + print("locked waiting for send") + async with lock: + if not await rfm9x.asyncio_send_with_ack( + bytes( + f"message from node {rfm9x.node} {counter} {ack_failed_counter}", + "UTF-8", + ) + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) + await asyncio.sleep(0.1) + + +async def main(): + packet_status = Packet() + lock = asyncio.Lock() + task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) + task2 = asyncio.create_task(send_packets(packet_status, lock)) + task3 = asyncio.create_task(rainbow_cycle(0.001, lock)) # rainbow cycle with 1ms delay per step + + await asyncio.gather(task1, task2, task3) # Don't forget "await"! + + +asyncio.run(main()) diff --git a/examples/rfm9x_rh_asyncio_node1.py b/examples/rfm9x_rh_asyncio_node1.py new file mode 100644 index 0000000..4aebeb1 --- /dev/null +++ b/examples/rfm9x_rh_asyncio_node1.py @@ -0,0 +1,103 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import asyncio +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm9x.node = 1 +rfm9x.destination = 100 +# send startup message from my_node +rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) +rfm9x.listen() +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +# pylint: disable=too-few-public-methods +class Packet: + """Simple class to hold an value. Use .value to to read or write.""" + + def __init__(self): + self.received = False + + +# setup interrupt callback function +async def wait_for_packets(packet_status, lock): + while True: + if rfm9x.payload_ready(): + if lock.locked(): + print("locked waiting for receive") + async with lock: + packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_status.received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + await asyncio.sleep(0.001) + + +async def send_packets(packet_status, lock): + # initialize counter + counter = 0 + ack_failed_counter = 0 + counter = 0 + transmit_interval = 5 + time_now = time.monotonic() + while True: + # If no packet was received during the timeout then None is returned. + if packet_status.received: + packet_status.received = False + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter += 1 + # send a mesage to destination_node from my_node + if lock.locked(): + print("locked waiting for send") + async with lock: + if not await rfm9x.asyncio_send_with_ack( + bytes( + f"message from node {rfm9x.node} {counter} {ack_failed_counter}", + "UTF-8", + ) + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) + await asyncio.sleep(0.1) + + +async def main(): + packet_status = Packet() + lock = asyncio.Lock() + task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) + task2 = asyncio.create_task(send_packets(packet_status, lock)) + + await asyncio.gather(task1, task2) # Don't forget "await"! + + +asyncio.run(main()) diff --git a/examples/rfm9x_rh_asyncio_node2.py b/examples/rfm9x_rh_asyncio_node2.py new file mode 100644 index 0000000..e9b02ef --- /dev/null +++ b/examples/rfm9x_rh_asyncio_node2.py @@ -0,0 +1,108 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import asyncio +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm9x.node = 2 +rfm9x.destination = 100 +# send startup message from my_node +# rfm9x.send_with_ack(bytes("startup message from node {}".format(rfm9x.node), "UTF-8")) +rfm9x.listen() +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +# pylint: disable=too-few-public-methods +class Packet: + """Simple class to hold an value. Use .value to to read or write.""" + + def __init__(self): + self.received = False + + +# setup interrupt callback function +async def wait_for_packets(packet_status, lock): + while True: + if rfm9x.payload_ready(): + if lock.locked(): + print("locked waiting for receive") + async with lock: + packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_status.received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + await asyncio.sleep(0.001) + + +async def send_packets(packet_status, lock): + # initialize counter + counter = 0 + ack_failed_counter = 0 + counter = 0 + transmit_interval = 5 + time_now = time.monotonic() + while True: + # If no packet was received during the timeout then None is returned. + if packet_status.received: + packet_status.received = False + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter += 1 + # send a mesage to destination_node from my_node + if lock.locked(): + print("locked waiting for send") + async with lock: + if not await rfm9x.asyncio_send_with_ack( + bytes( + f"message from node {rfm9x.node} {counter} {ack_failed_counter}", + "UTF-8", + ) + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) + await asyncio.sleep(0.001) + + +async def main(): + packet_status = Packet() + lock = asyncio.Lock() + task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) + task2 = asyncio.create_task(send_packets(packet_status, lock)) + + await asyncio.gather(task1, task2) # Don't forget "await"! + + +asyncio.run(main()) diff --git a/examples/rfm9x_rh_asyncio_raw.py b/examples/rfm9x_rh_asyncio_raw.py new file mode 100644 index 0000000..ddd7db0 --- /dev/null +++ b/examples/rfm9x_rh_asyncio_raw.py @@ -0,0 +1,87 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import asyncio + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# send startup message from my_node +rfm9x.send(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) +rfm9x.listen() +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +# pylint: disable=too-few-public-methods +class Packet: + """Simple class to hold an value. Use .value to to read or write.""" + + def __init__(self): + self.received = False + + +# setup interrupt callback function +async def wait_for_packets(packet_status): + while True: + if rfm9x.payload_ready(): + packet = await rfm9x.asyncio_receive(with_header=True, timeout=None) + if packet is not None: + packet_status.received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + await asyncio.sleep(0.001) + + +async def send_packets(packet_status): + # initialize counter + counter = 0 + while True: + # If no packet was received during the timeout then None is returned. + if packet_status.received: + packet_status.received = False + await asyncio.sleep(0.5) # delay .5 seconds before responding + counter += 1 + # send a mesage to destination_node from my_node + await rfm9x.asyncio_send( + bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8"), + keep_listening=True, + ) + await asyncio.sleep(0.001) + + +async def main(): + packet_status = Packet() + task1 = asyncio.create_task(wait_for_packets(packet_status)) + task2 = asyncio.create_task(send_packets(packet_status)) + + await asyncio.gather(task1, task2) # Don't forget "await"! + + +asyncio.run(main()) diff --git a/examples/rfm9x_rh_base.py b/examples/rfm9x_rh_base.py new file mode 100644 index 0000000..1400e38 --- /dev/null +++ b/examples/rfm9x_rh_base.py @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm9x.node = 100 +rfm9x.destination = 0xFF +# send startup message from my_node +rfm9x.send( + bytes(f"startup message from base {rfm9x.node}", "UTF-8"), + keep_listening=True, +) +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +while True: + if rfm9x.payload_ready(): + packet = rfm9x.receive_with_ack(with_header=True, timeout=None) + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") diff --git a/examples/rfm9x_rh_header.py b/examples/rfm9x_rh_header.py new file mode 100644 index 0000000..b62feb5 --- /dev/null +++ b/examples/rfm9x_rh_header.py @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to display raw packets including header +# Author: Jerry Needell +# +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9x.receive(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"RSSI: {rfm9x.last_rssi}") + # send reading after any packet received diff --git a/examples/rfm9x_rh_interrupt_node1.py b/examples/rfm9x_rh_interrupt_node1.py new file mode 100644 index 0000000..7d82e3b --- /dev/null +++ b/examples/rfm9x_rh_interrupt_node1.py @@ -0,0 +1,87 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import board +import busio +import digitalio +import RPi.GPIO as io + +from adafruit_rfm import rfm9x + + +# setup interrupt callback function +def rfm9x_callback(rfm9x_irq): + global packet_received # noqa: PLW0603 + print("IRQ detected ", rfm9x_irq, rfm9x.packet_sent(), rfm9x.payload_ready()) + # check to see if this was a rx interrupt - ignore tx + if rfm9x.payload_ready(): + packet = rfm9x.receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm9x.tx_power = 23 + +# configure the interrupt pin and event handling. +RFM9X_G0 = 22 +io.setmode(io.BCM) +io.setup(RFM9X_G0, io.IN, pull_up_down=io.PUD_DOWN) # activate input +io.add_event_detect(RFM9X_G0, io.RISING) +io.add_event_callback(RFM9X_G0, rfm9x_callback) + +packet_received = False + +# set delay before sending ACK +rfm9x.ack_delay = None +# set node addresses +rfm9x.node = 1 +rfm9x.destination = 2 +# initialize counter +counter = 0 +ack_failed_counter = 0 +# send startup message from my_node +rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) +rfm9x.listen() +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # If no packet was received during the timeout then None is returned. + if packet_received: + packet_received = False + counter += 1 + # send a mesage to destination_node from my_node + if not rfm9x.send_with_ack( + bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9x_rh_interrupt_node2.py b/examples/rfm9x_rh_interrupt_node2.py new file mode 100644 index 0000000..8a1edb0 --- /dev/null +++ b/examples/rfm9x_rh_interrupt_node2.py @@ -0,0 +1,87 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import board +import busio +import digitalio +import RPi.GPIO as io + +from adafruit_rfm import rfm9x + + +# setup interrupt callback function +def rfm9x_callback(rfm9x_irq): + global packet_received # Noqa: PLW0603 + # print("IRQ detected ", rfm9x_irq, rfm9x.packet_sent(), rfm9x.payload_ready()) + # check to see if this was a rx interrupt - ignore tx + if rfm9x.payload_ready(): + packet = rfm9x.receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm9x.tx_power = 23 + +# configure the interrupt pin and event handling. +RFM9X_G0 = 22 +io.setmode(io.BCM) +io.setup(RFM9X_G0, io.IN, pull_up_down=io.PUD_DOWN) # activate input +io.add_event_detect(RFM9X_G0, io.RISING) +io.add_event_callback(RFM9X_G0, rfm9x_callback) + +packet_received = False + +# set delay before sending ACK +rfm9x.ack_delay = 0.25 +# set node addresses +rfm9x.node = 2 +rfm9x.destination = 1 +# initialize counter +counter = 0 +ack_failed_counter = 0 +# send startup message from my_node +rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) +rfm9x.listen() +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # If no packet was received during the timeout then None is returned. + if packet_received: + packet_received = False + counter += 1 + # send a mesage to destination_node from my_node + if not rfm9x.send_with_ack( + bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9x_rh_node1.py b/examples/rfm9x_rh_node1.py new file mode 100644 index 0000000..224a3ae --- /dev/null +++ b/examples/rfm9x_rh_node1.py @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically between addressed nodes +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + + +# set the time interval (seconds) for sending packets +transmit_interval = 2 + +# set node addresses +rfm9x.node = 1 +rfm9x.destination = 2 +# initialize counter +counter = 0 +# send a broadcast message from my_node with ID = counter +rfm9x.send(bytes(f"Startup message {counter} from node {rfm9x.node}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +now = time.monotonic() +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9x.receive(with_header=True, timeput=5.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"Received RSSI: {rfm9x.last_rssi}") + if time.monotonic() - now > transmit_interval: + now = time.monotonic() + counter = counter + 1 + # send a mesage to destination_node from my_node + rfm9x.send( + bytes(f"message number {counter} from node {rfm9x.node}", "UTF-8"), + keep_listening=True, + ) + button_pressed = None diff --git a/examples/rfm9x_rh_node1_ack.py b/examples/rfm9x_rh_node1_ack.py new file mode 100644 index 0000000..31c2430 --- /dev/null +++ b/examples/rfm9x_rh_node1_ack.py @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically between addressed nodes with ACK +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + + +# set the time interval (seconds) for sending packets +transmit_interval = 10 + +# set node addresses +rfm9x.node = 1 +rfm9x.destination = 2 +# initialize counter +counter = 0 +ack_failed_counter = 0 +# send startup message from my_node +rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9x.receive_with_ack(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"RSSI: {rfm9x.last_rssi}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter += 1 + # send a mesage to destination_node from my_node + if not rfm9x.send_with_ack( + bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9x_rh_node2.py b/examples/rfm9x_rh_node2.py new file mode 100644 index 0000000..05ff566 --- /dev/null +++ b/examples/rfm9x_rh_node2.py @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically between addressed nodes +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm9x.node = 2 +rfm9x.destination = 1 +# initialize counter +counter = 0 +# send a broadcast message from my_node with ID = counter +rfm9x.send(bytes(f"startup message from node {rfm9x.node} ", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9x.receive(with_header=True, timeout=5.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"Received RSSI: {rfm9x.last_rssi}") + # send reading after any packet received + counter = counter + 1 + # after 10 messages send a response to destination_node from my_node with ID = counter&0xff + if counter % 10 == 0: + time.sleep(0.5) # brief delay before responding + rfm9x.identifier = counter & 0xFF + rfm9x.send( + bytes( + f"message number {counter} from node {rfm9x.node} ", + "UTF-8", + ), + keep_listening=True, + ) diff --git a/examples/rfm9x_rh_node2_ack.py b/examples/rfm9x_rh_node2_ack.py new file mode 100644 index 0000000..62f681b --- /dev/null +++ b/examples/rfm9x_rh_node2_ack.py @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to receive addressed packed with ACK and send a response +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm9x.node = 2 +rfm9x.destination = 1 +# initialize counter +counter = 0 +ack_failed_counter = 0 + +# Wait to receive packets. +print("Waiting for packets...") +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9x.receive_with_ack(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"RSSI: {rfm9x.last_rssi}") + # send response 2 sec after any packet received + time.sleep(2) + counter += 1 + # send a mesage to destination_node from my_node + if not rfm9x.send_with_ack(bytes(f"response from node {rfm9x.node} {counter}", "UTF-8")): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9x_rh_simpletest.py b/examples/rfm9x_rh_simpletest.py new file mode 100644 index 0000000..5fd64b5 --- /dev/null +++ b/examples/rfm9x_rh_simpletest.py @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: 2024 Ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Simple demo of sending and recieving data with the RFM95 LoRa radio. +# Author: Jerry Needell +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm9x.tx_power = 23 + +# Send a packet. Note you can only send a packet up to 252 bytes in length. +# This is a limitation of the radio packet size, so if you need to send larger +# amounts of data you will need to break it into smaller send calls. Each send +# call will wait for the previous one to finish before continuing. +rfm9x.send(bytes("Hello world!\r\n", "utf-8")) +print("Sent Hello World message!") + +# Wait to receive packets. Note that this library can't receive data at a fast +# rate, in fact it can only receive and process one 252 byte packet at a time. +# This means you should only use this for low bandwidth scenarios, like sending +# and receiving a single message at a time. +print("Waiting for packets...") + +while True: + packet = rfm9x.receive() + # Optionally change the receive timeout from its default of 0.5 seconds: + # packet = rfm9x.receive(timeout=5.0) + # If no packet was received during the timeout then None is returned. + if packet is None: + # Packet has not been received + print("Received nothing! Listening again...") + else: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # And decode to ASCII text and print it too. Note that you always + # receive raw bytes and need to convert to a text format like ASCII + # if you intend to do string processing on your data. Make sure the + # sending side is sending ASCII data before you try to decode! + try: + packet_text = str(packet, "ascii") + print(f"Received (ASCII): {packet_text}") + except UnicodeError: + print("Hex data: ", [hex(x) for x in packet]) + # Also read the RSSI (signal strength) of the last received message and + # print it. + rssi = rfm9x.last_rssi + print(f"Received signal strength: {rssi} dB") diff --git a/examples/rfm9x_rh_transmit.py b/examples/rfm9x_rh_transmit.py new file mode 100644 index 0000000..9b7fa9f --- /dev/null +++ b/examples/rfm9x_rh_transmit.py @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9x + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + + +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm9x.tx_power = 23 + + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm9x.send(bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +send_reading = False +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm9x.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + # clear flag to send data + send_reading = False + counter = counter + 1 + rfm9x.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xfsk_node1.py b/examples/rfm9xfsk_node1.py new file mode 100644 index 0000000..868b768 --- /dev/null +++ b/examples/rfm9xfsk_node1.py @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import random +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) +node = 1 +destination = 2 + +rfm9xfsk.radiohead = False +rfm9xfsk.enable_address_filter = True +rfm9xfsk.fsk_node_address = node +rfm9xfsk.fsk_broadcast_address = 0xFF +# set the time interval (seconds) for sending packets +transmit_interval = 2 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +# rfm9xfsk.tx_power = 23 + + +# initialize counter +counter = 0 +# send a startup mesage +rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm9xfsk.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}", rfm9xfsk.last_rssi) + # clear flag to send data + time.sleep(random.random()) + counter = counter + 1 + rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) diff --git a/examples/rfm9xfsk_node2.py b/examples/rfm9xfsk_node2.py new file mode 100644 index 0000000..d32d85c --- /dev/null +++ b/examples/rfm9xfsk_node2.py @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import random +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) +node = 2 +destination = 1 + +rfm9xfsk.radiohead = False +rfm9xfsk.enable_address_filter = True +rfm9xfsk.fsk_node_address = node +rfm9xfsk.fsk_broadcast_address = 0xFF +# set the time interval (seconds) for sending packets +transmit_interval = 2 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +# rfm9xfsk.tx_power = 23 + + +# initialize counter +counter = 0 +# send a startup mesage +rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm9xfsk.receive(timeout=0.5) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}", rfm9xfsk.last_rssi) + if time.monotonic() - time_now > transmit_interval + random.random(): + counter = counter + 1 + rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) + time_now = time.monotonic() diff --git a/examples/rfm9xfsk_raw.py b/examples/rfm9xfsk_raw.py new file mode 100644 index 0000000..ccf69e0 --- /dev/null +++ b/examples/rfm9xfsk_raw.py @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to display raw packets including header +# Author: Jerry Needell +# +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) +rfm9xfsk.radiohead = False +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9xfsk.receive() + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:]]) + print(f"RSSI: {rfm9xfsk.last_rssi}") + # send reading after any packet received diff --git a/examples/rfm9xfsk_rh_asyncio_node1.py b/examples/rfm9xfsk_rh_asyncio_node1.py new file mode 100644 index 0000000..e959c5f --- /dev/null +++ b/examples/rfm9xfsk_rh_asyncio_node1.py @@ -0,0 +1,104 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import asyncio +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm9x.node = 1 +rfm9x.destination = 100 +rfm9x.ack_wait = 0.4 +# send startup message from my_node +rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) +rfm9x.listen() +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +# pylint: disable=too-few-public-methods +class Packet: + """Simple class to hold an value. Use .value to to read or write.""" + + def __init__(self): + self.received = False + + +# setup interrupt callback function +async def wait_for_packets(packet_status, lock): + while True: + if rfm9x.payload_ready(): + if lock.locked(): + print("locked waiting for receive") + async with lock: + packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_status.received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + await asyncio.sleep(0.001) + + +async def send_packets(packet_status, lock): + # initialize counter + counter = 0 + ack_failed_counter = 0 + counter = 0 + transmit_interval = 5 + time_now = time.monotonic() + while True: + # If no packet was received during the timeout then None is returned. + if packet_status.received: + packet_status.received = False + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter += 1 + # send a mesage to destination_node from my_node + if lock.locked(): + print("locked waiting for send") + async with lock: + if not await rfm9x.asyncio_send_with_ack( + bytes( + f"message from node {rfm9x.node} {counter} {ack_failed_counter}", + "UTF-8", + ) + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) + await asyncio.sleep(0.1) + + +async def main(): + packet_status = Packet() + lock = asyncio.Lock() + task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) + task2 = asyncio.create_task(send_packets(packet_status, lock)) + + await asyncio.gather(task1, task2) # Don't forget "await"! + + +asyncio.run(main()) diff --git a/examples/rfm9xfsk_rh_base.py b/examples/rfm9xfsk_rh_base.py new file mode 100644 index 0000000..4814df9 --- /dev/null +++ b/examples/rfm9xfsk_rh_base.py @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set node addresses +rfm9x.node = 100 +rfm9x.destination = 0xFF +rfm9x.ack_delay = 0.1 +# send startup message from my_node +rfm9x.send( + bytes(f"startup message from base {rfm9x.node}", "UTF-8"), + keep_listening=True, +) +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer + + +while True: + if rfm9x.payload_ready(): + packet = rfm9x.receive_with_ack(with_header=True, timeout=None) + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") diff --git a/examples/rfm9xfsk_rh_header.py b/examples/rfm9xfsk_rh_header.py new file mode 100644 index 0000000..37537a6 --- /dev/null +++ b/examples/rfm9xfsk_rh_header.py @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to display raw packets including header +# Author: Jerry Needell +# +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9xfsk.receive(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"RSSI: {rfm9xfsk.last_rssi}") + # send reading after any packet received diff --git a/examples/rfm9xfsk_rh_interrupt_node1.py b/examples/rfm9xfsk_rh_interrupt_node1.py new file mode 100644 index 0000000..f029860 --- /dev/null +++ b/examples/rfm9xfsk_rh_interrupt_node1.py @@ -0,0 +1,89 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import board +import busio +import digitalio +import RPi.GPIO as io + +from adafruit_rfm import rfm9xfsk + + +# setup interrupt callback function +def rfm9x_callback(rfm9x_irq): + global packet_received # noqa: PLW0603 + print("OP MODE: ", hex(rfm9x.read_u8(0x01))) + print("IRQ FLAGS 2: ", hex(rfm9x.read_u8(0x3F))) + print("IRQ detected ", rfm9x_irq, rfm9x.packet_sent(), rfm9x.payload_ready()) + # check to see if this was a rx interrupt - ignore tx + if rfm9x.payload_ready(): + packet = rfm9x.receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm9x.tx_power = 23 + +# configure the interrupt pin and event handling. +RFM9X_G0 = 22 +io.setmode(io.BCM) +io.setup(RFM9X_G0, io.IN, pull_up_down=io.PUD_DOWN) # activate input +io.add_event_detect(RFM9X_G0, io.RISING) +io.add_event_callback(RFM9X_G0, rfm9x_callback) + +packet_received = False + +# set delay before sending ACK +rfm9x.ack_delay = None +# set node addresses +rfm9x.node = 1 +rfm9x.destination = 2 +# initialize counter +counter = 0 +ack_failed_counter = 0 +rfm9x.listen() +# send startup message from my_node +rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # If no packet was received during the timeout then None is returned. + if packet_received: + packet_received = False + counter += 1 + # send a mesage to destination_node from my_node + if not rfm9x.send_with_ack( + bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9xfsk_rh_interrupt_node2.py b/examples/rfm9xfsk_rh_interrupt_node2.py new file mode 100644 index 0000000..48e51ff --- /dev/null +++ b/examples/rfm9xfsk_rh_interrupt_node2.py @@ -0,0 +1,89 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example using Interrupts to send a message and then wait indefinitely for messages +# to be received. Interrupts are used only for receive. sending is done with polling. +# This example is for systems that support interrupts like the Raspberry Pi with "blinka" +# CircuitPython does not support interrupts so it will not work on Circutpython boards +# Author: Tony DiCola, Jerry Needell +import board +import busio +import digitalio +import RPi.GPIO as io + +from adafruit_rfm import rfm9xfsk + + +# setup interrupt callback function +def rfm9x_callback(rfm9x_irq): + global packet_received # noqa: PLW0603 + print("OP MODE: ", hex(rfm9x.read_u8(0x01))) + print("IRQ FLAGS 2: ", hex(rfm9x.read_u8(0x3F))) + print("IRQ detected ", rfm9x_irq, rfm9x.packet_sent(), rfm9x.payload_ready()) + # check to see if this was a rx interrupt - ignore tx + if rfm9x.payload_ready(): + packet = rfm9x.receive_with_ack(with_header=True, timeout=None) + if packet is not None: + packet_received = True + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm9x.last_rssi}") + + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9x = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm9x.tx_power = 23 + +# configure the interrupt pin and event handling. +RFM9X_G0 = 22 +io.setmode(io.BCM) +io.setup(RFM9X_G0, io.IN, pull_up_down=io.PUD_DOWN) # activate input +io.add_event_detect(RFM9X_G0, io.RISING) +io.add_event_callback(RFM9X_G0, rfm9x_callback) + +packet_received = False + +# set delay before sending ACK +rfm9x.ack_delay = None +# set node addresses +rfm9x.node = 2 +rfm9x.destination = 1 +# initialize counter +counter = 0 +ack_failed_counter = 0 +rfm9x.listen() +# send startup message from my_node +rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # If no packet was received during the timeout then None is returned. + if packet_received: + packet_received = False + counter += 1 + # send a mesage to destination_node from my_node + if not rfm9x.send_with_ack( + bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9xfsk_rh_node1.py b/examples/rfm9xfsk_rh_node1.py new file mode 100644 index 0000000..dd9ec29 --- /dev/null +++ b/examples/rfm9xfsk_rh_node1.py @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# set node addresses +rfm9xfsk.node = 1 +rfm9xfsk.destination = 2 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +# rfm9xfsk.tx_power = 23 + + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm9xfsk.send(b"\2" + bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm9xfsk.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter = counter + 1 + rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xfsk_rh_node1_ack.py b/examples/rfm9xfsk_rh_node1_ack.py new file mode 100644 index 0000000..0b0ad56 --- /dev/null +++ b/examples/rfm9xfsk_rh_node1_ack.py @@ -0,0 +1,67 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically between addressed nodes with ACK + +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + + +# set the time interval (seconds) for sending packets +transmit_interval = 10 + +# set delay before sending ACK +rfm9xfsk.ack_delay = 0.1 +# set node addresses +rfm9xfsk.node = 1 +rfm9xfsk.destination = 2 +# initialize counter +counter = 0 +ack_failed_counter = 0 +# send startup message from my_node +rfm9xfsk.send_with_ack(bytes(f"startup message from node {rfm9xfsk.node}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9xfsk.receive_with_ack(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"RSSI: {rfm9xfsk.last_rssi}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter += 1 + # send a mesage to destination_node from my_node + if not rfm9xfsk.send_with_ack( + bytes(f"message from node node {rfm9xfsk.node} {counter}", "UTF-8") + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9xfsk_rh_node2.py b/examples/rfm9xfsk_rh_node2.py new file mode 100644 index 0000000..ff82260 --- /dev/null +++ b/examples/rfm9xfsk_rh_node2.py @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + + +# set node addresses +rfm9xfsk.node = 2 +rfm9xfsk.destination = 1 +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +# rfm9xfsk.tx_power = 23 + + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm9xfsk.send(b"\2" + bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm9xfsk.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + counter = counter + 1 + rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xfsk_rh_node2_ack.py b/examples/rfm9xfsk_rh_node2_ack.py new file mode 100644 index 0000000..994964e --- /dev/null +++ b/examples/rfm9xfsk_rh_node2_ack.py @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to receive addressed packed with ACK and send a response + +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + + +# set delay before transmitting ACK (seconds) +rfm9xfsk.ack_delay = 0.1 +rfm9xfsk.ack_wait = 0.2 +# set node addresses +rfm9xfsk.node = 2 +rfm9xfsk.destination = 1 +# initialize counter +counter = 0 +ack_failed_counter = 0 + +# Wait to receive packets. +print("Waiting for packets...") +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9xfsk.receive_with_ack(with_header=True) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:4]]) + print(f"Received (raw payload): {packet[4:]}") + print(f"RSSI: {rfm9xfsk.last_rssi}") + # send response 2 sec after any packet received + time.sleep(2) + counter += 1 + # send a mesage to destination_node from my_node + if not rfm9xfsk.send_with_ack( + bytes(f"response from node {rfm9xfsk.node} {counter}", "UTF-8") + ): + ack_failed_counter += 1 + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9xfsk_rh_simpletest.py b/examples/rfm9xfsk_rh_simpletest.py new file mode 100644 index 0000000..b20d441 --- /dev/null +++ b/examples/rfm9xfsk_rh_simpletest.py @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: 2024 Ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Simple demo of sending and recieving data with the rfm9x FSK radio. +# Author: Jerry Needell +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Print out some chip state: +print(f"Temperature: {rfm9xfsk.temperature}C") +print(f"Frequency: {rfm9xfsk.frequency_mhz}mhz") +print(f"Bit rate: {rfm9xfsk.bitrate / 1000}kbit/s") +print(f"Frequency deviation: {rfm9xfsk.frequency_deviation}hz") + +# Send a packet. Note you can only send a packet up to 252 bytes in length. +# This is a limitation of the radio packet size, so if you need to send larger +# amounts of data you will need to break it into smaller send calls. Each send +# call will wait for the previous one to finish before continuing. +rfm9xfsk.send(bytes("Hello world!\r\n", "utf-8")) +print("Sent hello world message!") + +# Wait to receive packets. Note that this library can't receive data at a fast +# rate, in fact it can only receive and process one 252 byte packet at a time. +# This means you should only use this for low bandwidth scenarios, like sending +# and receiving a single message at a time. +print("Waiting for packets...") +while True: + packet = rfm9xfsk.receive() + # Optionally change the receive timeout from its default of 0.5 seconds: + # packet = rfm9xfsk.receive(timeout=5.0) + # If no packet was received during the timeout then None is returned. + if packet is None: + # Packet has not been received + print("Received nothing! Listening again...") + else: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # And decode to ASCII text or HEX if the ASCII decode fails + try: + packet_text = str(packet, "ascii") + print(f"Received (ASCII): {packet_text}") + except UnicodeDecodeError: + print("Hex data: ", [hex(x) for x in packet]) + rssi = rfm9xfsk.last_rssi + print(f"Received signal strength: {rssi} dB") diff --git a/examples/rfm9xfsk_rh_transmit.py b/examples/rfm9xfsk_rh_transmit.py new file mode 100644 index 0000000..bfe7568 --- /dev/null +++ b/examples/rfm9xfsk_rh_transmit.py @@ -0,0 +1,65 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm9xfsk.tx_power = 23 + + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +send_reading = False +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm9xfsk.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + # clear flag to send data + send_reading = False + counter = counter + 1 + rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xfsk_transmit.py b/examples/rfm9xfsk_transmit.py new file mode 100644 index 0000000..4fa0220 --- /dev/null +++ b/examples/rfm9xfsk_transmit.py @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +print( + rfm9xfsk.fsk_node_address, + rfm9xfsk.fsk_broadcast_address, + rfm9xfsk.enable_address_filter, +) + +rfm9xfsk.radiohead = False +# rfm9xfsk.enable_address_filter=True +# rfm9xfsk.fsk_node_address=0x2 +# rfm9xfsk.fsk_broadcast_address=0xff +print( + rfm9xfsk.fsk_node_address, + rfm9xfsk.fsk_broadcast_address, + rfm9xfsk.enable_address_filter, +) +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm9xfsk.tx_power = 23 + + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +send_reading = False +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm9xfsk.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + # clear flag to send data + send_reading = False + counter = counter + 1 + rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xook_raw.py b/examples/rfm9xook_raw.py new file mode 100644 index 0000000..8a6038a --- /dev/null +++ b/examples/rfm9xook_raw.py @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to display raw packets including header +# Author: Jerry Needell +# +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) +rfm9xfsk.radiohead = False +print(rfm9xfsk.modulation_type) +rfm9xfsk.modulation_type = 1 +print(rfm9xfsk.modulation_type) # Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +while True: + # Look for a new packet: only accept if addresses to my_node + packet = rfm9xfsk.receive() + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print("Received (raw header):", [hex(x) for x in packet[0:]]) + print(f"RSSI: {rfm9xfsk.last_rssi}") + # send reading after any packet received diff --git a/examples/rfm9xook_transmit.py b/examples/rfm9xook_transmit.py new file mode 100644 index 0000000..e904aad --- /dev/null +++ b/examples/rfm9xook_transmit.py @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +from adafruit_rfm import rfm9xfsk + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +print( + rfm9xfsk.fsk_node_address, + rfm9xfsk.fsk_broadcast_address, + rfm9xfsk.enable_address_filter, +) + +rfm9xfsk.radiohead = False +print(rfm9xfsk.modulation_type) +rfm9xfsk.modulation_type = 1 +print(rfm9xfsk.modulation_type) +# rfm9xfsk.enable_address_filter=True +# rfm9xfsk.fsk_node_address=0x2 +# rfm9xfsk.fsk_broadcast_address=0xff +print( + rfm9xfsk.fsk_node_address, + rfm9xfsk.fsk_broadcast_address, + rfm9xfsk.enable_address_filter, +) +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm9xfsk.tx_power = 23 + + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +send_reading = False +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm9xfsk.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + # clear flag to send data + send_reading = False + counter = counter + 1 + rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/optional_requirements.txt b/optional_requirements.txt new file mode 100644 index 0000000..d4e27c4 --- /dev/null +++ b/optional_requirements.txt @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..81061b4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney, written for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Jerry Needell for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +[build-system] +requires = [ + "setuptools", + "wheel", + "setuptools-scm", +] + +[project] +name = "adafruit-circuitpython-rfm" +description = "Support for RFM69 and RFM9x modules" +version = "0.0.0+auto.0" +readme = "README.rst" +authors = [ + {name = "Adafruit Industries", email = "circuitpython@adafruit.com"} +] +urls = {Homepage = "https://github.com/jerryneedell/Adafruit_CircuitPython_RFM"} +keywords = [ + "adafruit", + "blinka", + "circuitpython", + "micropython", + "rfm", + "RFM69", + "RFM9x", + "radio", +] +license = {text = "MIT"} +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Embedded Systems", + "Topic :: System :: Hardware", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", +] +dynamic = ["dependencies", "optional-dependencies"] + +[tool.setuptools] +# TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, +# CHANGE `py_modules = ['...']` TO `packages = ['...']` +#py-modules = ["adafruit_rfm"] +packages = ["adafruit_rfm"] + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements.txt"]} +optional-dependencies = {optional = {file = ["optional_requirements.txt"]}} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..35ac53b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Jerry Needell for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +Adafruit-Blinka +adafruit-circuitpython-busdevice +asyncio diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..db37c83 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,99 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +target-version = "py38" +line-length = 100 + +[lint] +select = ["I", "PL", "UP"] + +extend-select = [ + "D419", # empty-docstring + "E501", # line-too-long + "W291", # trailing-whitespace + "PLC0414", # useless-import-alias + "PLC2401", # non-ascii-name + "PLC2801", # unnecessary-dunder-call + "PLC3002", # unnecessary-direct-lambda-call + "E999", # syntax-error + "PLE0101", # return-in-init + "F706", # return-outside-function + "F704", # yield-outside-function + "PLE0116", # continue-in-finally + "PLE0117", # nonlocal-without-binding + "PLE0241", # duplicate-bases + "PLE0302", # unexpected-special-method-signature + "PLE0604", # invalid-all-object + "PLE0605", # invalid-all-format + "PLE0643", # potential-index-error + "PLE0704", # misplaced-bare-raise + "PLE1141", # dict-iter-missing-items + "PLE1142", # await-outside-async + "PLE1205", # logging-too-many-args + "PLE1206", # logging-too-few-args + "PLE1307", # bad-string-format-type + "PLE1310", # bad-str-strip-call + "PLE1507", # invalid-envvar-value + "PLE2502", # bidirectional-unicode + "PLE2510", # invalid-character-backspace + "PLE2512", # invalid-character-sub + "PLE2513", # invalid-character-esc + "PLE2514", # invalid-character-nul + "PLE2515", # invalid-character-zero-width-space + "PLR0124", # comparison-with-itself + "PLR0202", # no-classmethod-decorator + "PLR0203", # no-staticmethod-decorator + "UP004", # useless-object-inheritance + "PLR0206", # property-with-parameters + "PLR0904", # too-many-public-methods + "PLR0911", # too-many-return-statements + "PLR0912", # too-many-branches + "PLR0913", # too-many-arguments + "PLR0914", # too-many-locals + "PLR0915", # too-many-statements + "PLR0916", # too-many-boolean-expressions + "PLR1702", # too-many-nested-blocks + "PLR1704", # redefined-argument-from-local + "PLR1711", # useless-return + "C416", # unnecessary-comprehension + "PLR1733", # unnecessary-dict-index-lookup + "PLR1736", # unnecessary-list-index-lookup + + # ruff reports this rule is unstable + #"PLR6301", # no-self-use + + "PLW0108", # unnecessary-lambda + "PLW0120", # useless-else-on-loop + "PLW0127", # self-assigning-variable + "PLW0129", # assert-on-string-literal + "B033", # duplicate-value + "PLW0131", # named-expr-without-context + "PLW0245", # super-without-brackets + "PLW0406", # import-self + "PLW0602", # global-variable-not-assigned + "PLW0603", # global-statement + "PLW0604", # global-at-module-level + + # fails on the try: import typing used by libraries + #"F401", # unused-import + + "F841", # unused-variable + "E722", # bare-except + "PLW0711", # binary-op-exception + "PLW1501", # bad-open-mode + "PLW1508", # invalid-envvar-default + "PLW1509", # subprocess-popen-preexec-fn + "PLW2101", # useless-with-lock + "PLW3301", # nested-min-max +] + +ignore = [ + "PLR2004", # magic-value-comparison + "UP030", # format literals + "PLW1514", # unspecified-encoding + +] + +[format] +line-ending = "lf" From a966869b696c04f01dff62e032d3eb4b1524d543 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 29 Jul 2024 14:43:14 -0400 Subject: [PATCH 2/9] add rfm_simpletest.pty placeholder --- examples/rfm_simpletest.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 examples/rfm_simpletest.py diff --git a/examples/rfm_simpletest.py b/examples/rfm_simpletest.py new file mode 100644 index 0000000..5d9bc2b --- /dev/null +++ b/examples/rfm_simpletest.py @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Jerry Needell for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense From 11cdda78788efc7e33be6680d9e5287d4cc140fa Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 29 Jul 2024 14:52:48 -0400 Subject: [PATCH 3/9] fix READNE --- README.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.rst b/README.rst index 1468746..7a591a8 100644 --- a/README.rst +++ b/README.rst @@ -38,12 +38,6 @@ or individual libraries can be installed using `circup `_. - -.. todo:: Describe the Adafruit product this library works with. For PCBs, you can also add the -image from the assets folder in the PCB's GitHub repo. - -`Purchase one from the Adafruit shop `_ - Installing from PyPI ===================== .. note:: This library is not available on PyPI yet. Install documentation is included From cff6d740b66cc02edd544903b5c36240caebe973 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Wed, 31 Jul 2024 13:40:45 -0400 Subject: [PATCH 4/9] revising examples --- adafruit_rfm/rfm69.py | 30 ++--- adafruit_rfm/rfm9x.py | 20 +--- adafruit_rfm/rfm9xfsk.py | 12 +- adafruit_rfm/rfm_common.py | 10 +- examples/rfm69_raw.py | 46 -------- examples/rfm69_rh_asyncio_node2.py | 109 ------------------ examples/rfm69_rh_header.py | 47 -------- examples/rfm69_rh_node1.py | 63 ---------- examples/rfm69_rh_node1_ack.py | 71 ------------ examples/rfm69_rh_node2.py | 67 ----------- examples/rfm69_rh_node2_ack.py | 59 ---------- examples/rfm69_rh_simpletest.py | 69 ----------- examples/rfm69_rh_transmit.py | 62 ---------- examples/rfm69ook_raw.py | 51 -------- examples/rfm9x_rh_asyncio_node2.py | 108 ----------------- examples/rfm9x_rh_node2.py | 63 ---------- examples/rfm9x_rh_node2_ack.py | 54 --------- examples/rfm9xfsk_raw.py | 39 ------- examples/rfm9xfsk_rh_asyncio_node1.py | 104 ----------------- examples/rfm9xfsk_rh_header.py | 40 ------- examples/rfm9xfsk_rh_interrupt_node1.py | 89 -------------- examples/rfm9xfsk_rh_interrupt_node2.py | 89 -------------- examples/rfm9xfsk_rh_node1.py | 66 ----------- examples/rfm9xfsk_rh_node1_ack.py | 67 ----------- examples/rfm9xfsk_rh_node2.py | 66 ----------- examples/rfm9xfsk_rh_node2_ack.py | 59 ---------- examples/rfm9xfsk_rh_simpletest.py | 63 ---------- examples/rfm9xfsk_rh_transmit.py | 65 ----------- examples/rfm9xook_raw.py | 41 ------- .../{rfm9x_rh_simpletest.py => rfm_raw.py} | 45 +++++--- ...cio_listen.py => rfm_rh_asyncio_listen.py} | 33 ++++-- ...io_raw.py => rfm_rh_asyncio_listen_ack.py} | 53 +++++---- ...yncio_node1.py => rfm_rh_asyncio_node1.py} | 41 +++++-- .../{rfm9xfsk_rh_base.py => rfm_rh_base.py} | 37 ++++-- .../{rfm9x_rh_base.py => rfm_rh_base_ack.py} | 36 ++++-- .../{rfm9x_rh_node1.py => rfm_rh_node1.py} | 35 ++++-- ...9x_rh_node1_ack.py => rfm_rh_node1_ack.py} | 35 ++++-- examples/rfm_simpletest.py | 82 ++++++++++++- 38 files changed, 315 insertions(+), 1811 deletions(-) delete mode 100644 examples/rfm69_raw.py delete mode 100644 examples/rfm69_rh_asyncio_node2.py delete mode 100644 examples/rfm69_rh_header.py delete mode 100644 examples/rfm69_rh_node1.py delete mode 100644 examples/rfm69_rh_node1_ack.py delete mode 100644 examples/rfm69_rh_node2.py delete mode 100644 examples/rfm69_rh_node2_ack.py delete mode 100644 examples/rfm69_rh_simpletest.py delete mode 100644 examples/rfm69_rh_transmit.py delete mode 100644 examples/rfm69ook_raw.py delete mode 100644 examples/rfm9x_rh_asyncio_node2.py delete mode 100644 examples/rfm9x_rh_node2.py delete mode 100644 examples/rfm9x_rh_node2_ack.py delete mode 100644 examples/rfm9xfsk_raw.py delete mode 100644 examples/rfm9xfsk_rh_asyncio_node1.py delete mode 100644 examples/rfm9xfsk_rh_header.py delete mode 100644 examples/rfm9xfsk_rh_interrupt_node1.py delete mode 100644 examples/rfm9xfsk_rh_interrupt_node2.py delete mode 100644 examples/rfm9xfsk_rh_node1.py delete mode 100644 examples/rfm9xfsk_rh_node1_ack.py delete mode 100644 examples/rfm9xfsk_rh_node2.py delete mode 100644 examples/rfm9xfsk_rh_node2_ack.py delete mode 100644 examples/rfm9xfsk_rh_simpletest.py delete mode 100644 examples/rfm9xfsk_rh_transmit.py delete mode 100644 examples/rfm9xook_raw.py rename examples/{rfm9x_rh_simpletest.py => rfm_raw.py} (68%) rename examples/{rfm9x_rh_asyncio_listen.py => rfm_rh_asyncio_listen.py} (72%) rename examples/{rfm9x_rh_asyncio_raw.py => rfm_rh_asyncio_listen_ack.py} (64%) rename examples/{rfm9x_rh_asyncio_node1.py => rfm_rh_asyncio_node1.py} (74%) rename examples/{rfm9xfsk_rh_base.py => rfm_rh_base.py} (58%) rename examples/{rfm9x_rh_base.py => rfm_rh_base_ack.py} (57%) rename examples/{rfm9x_rh_node1.py => rfm_rh_node1.py} (66%) rename examples/{rfm9x_rh_node1_ack.py => rfm_rh_node1_ack.py} (67%) diff --git a/adafruit_rfm/rfm69.py b/adafruit_rfm/rfm69.py index ab110ae..e4c2108 100644 --- a/adafruit_rfm/rfm69.py +++ b/adafruit_rfm/rfm69.py @@ -6,17 +6,15 @@ `adafruit_rfm69` ==================================================== -CircuitPython RFM69 packet radio module. This supports basic RadioHead-compatible sending and +CircuitPython RFM69 packet radio module. This supports sending and receiving of packets with RFM69 series radios (433/915Mhz). .. warning:: This is NOT for LoRa radios! -.. note:: This is a 'best effort' at receiving data using pure Python code--there is not interrupt - support so you might lose packets if they're sent too quickly for the board to process them. - You will have the most luck using this in simple low bandwidth scenarios like sending and - receiving a 60 byte packet at a time--don't try to receive many kilobytes of data at a time! +.. note:: This is a 'best effort' at receiving data using pure Python code. You might lose packets + if they're sent too quickly for the board to process them. -* Author(s): Tony DiCola, Jerry Needell +* Author(s): Jerry Needell """ import time @@ -47,7 +45,7 @@ __version__ = "0.0.0+auto.0" -__repo__ = "https://github.com/jerryneedell/CircuitPython_RFM.git" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RFM.git" # Internal constants: @@ -145,20 +143,18 @@ class RFM69(RFMSPI): transmission power. The default is True as it supports the common RFM69HCW modules sold by Adafruit. - .. note:: The D0/interrupt line is currently unused by this module and can remain unconnected. - Remember this library makes a best effort at receiving packets with pure Python code. Trying to receive packets too quickly will result in lost data so limit yourself to simple scenarios of sending and receiving single packets at a time. - Also note this library tries to be compatible with raw RadioHead Arduino library communication. + Also note this library defaults to be compatible with RadioHead Arduino library communication. This means the library sets up the radio modulation to match RadioHead's default of GFSK encoding, 250kbit/s bitrate, and 250khz frequency deviation. To change this requires explicitly - setting the radio's bitrate and encoding register bits. Read the datasheet and study the init - function to see an example of this--advanced users only! Advanced RadioHead features like - address/node specific packets or "reliable datagram" delivery are supported however due to the - limitations noted, "reliable datagram" is still subject to missed packets but with it, the - sender is notified if a packe has potentially been missed. + setting the radio's bitrate and encoding register bits. + Read the datasheet and study the init function to see an example of this--advanced users only! + Advanced RadioHead features like address/node specific packets or "reliable datagram" delivery + are supported however due to the limitations noted, "reliable datagram" is still subject to + missed packets. """ # Control bits from the registers of the chip: @@ -646,12 +642,8 @@ def read_fifo(self) -> bytearray: """Read the packet from the FIFO.""" # Read the length of the FIFO. fifo_length = self.read_u8(_RF69_REG_00_FIFO) - # Handle if the received packet is too small to include the 4 byte - # RadioHead header and at least one byte of data --reject this packet and ignore it. if fifo_length > 0: # read and clear the FIFO if anything in it packet = bytearray(fifo_length) # read the packet self.read_into(_RF69_REG_00_FIFO, packet, fifo_length) - if fifo_length < 5: - packet = None return packet diff --git a/adafruit_rfm/rfm9x.py b/adafruit_rfm/rfm9x.py index ac62d99..144c0f4 100644 --- a/adafruit_rfm/rfm9x.py +++ b/adafruit_rfm/rfm9x.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries +# SPDX-FileCopyrightText: 2024 Jerry Needell for Adafruit Industries # # SPDX-License-Identifier: MIT @@ -6,11 +6,9 @@ `adafruit_rfm9x` ==================================================== -CircuitPython module for the RFM95/6/7/8 LoRa 433/915mhz radio modules. This is -adapted from the Radiohead library RF95 code from: -http: www.airspayce.com/mikem/arduino/RadioHead/ +CircuitPython module for the RFM95/6/7/8 LoRa 433/915mhz radio modules. -* Author(s): Tony DiCola, Jerry Needell +* Author(s): Jerry Needell """ import time @@ -32,7 +30,7 @@ pass __version__ = "0.0.0+auto.0" -__repo__ = "https://github.com/jerryneedell/CircuitPython_RFM.git" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RFM.git" # pylint: disable=duplicate-code @@ -143,12 +141,10 @@ class RFM9x(RFMSPI): Also note this library tries to be compatible with raw RadioHead Arduino library communication. This means the library sets up the radio modulation - to match RadioHead's defaults and assumes that each packet contains a - 4 byte header compatible with RadioHead's implementation. + to match RadioHead's defaults. Advanced RadioHead features like address/node specific packets or "reliable datagram" delivery are supported however due to the - limitations noted, "reliable datagram" is still subject to missed packets but with it, - sender is notified if a packet has potentially been missed. + limitations noted, "reliable datagram" is still subject to missed packets. """ operation_mode = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, bits=3) @@ -523,8 +519,6 @@ def read_fifo(self) -> bytearray: """Read the data from the FIFO.""" # Read the length of the FIFO. fifo_length = self.read_u8(_RF95_REG_13_RX_NB_BYTES) - # Handle if the received packet is too small to include the 4 byte - # RadioHead header and at least one byte of data --reject this packet and ignore it. if fifo_length > 0: # read and clear the FIFO if anything in it packet = bytearray(fifo_length) current_addr = self.read_u8(_RF95_REG_10_FIFO_RX_CURRENT_ADDR) @@ -534,6 +528,4 @@ def read_fifo(self) -> bytearray: # clear interrupt self.write_u8(_RF95_REG_12_IRQ_FLAGS, 0xFF) - if fifo_length < 5: - packet = None return packet diff --git a/adafruit_rfm/rfm9xfsk.py b/adafruit_rfm/rfm9xfsk.py index 589c0ef..d456b86 100644 --- a/adafruit_rfm/rfm9xfsk.py +++ b/adafruit_rfm/rfm9xfsk.py @@ -32,7 +32,7 @@ pass __version__ = "0.0.0+auto.0" -__repo__ = "https://github.com/jerryneedell/CircuitPython_RFM.git" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RFM.git" # pylint: disable=duplicate-code @@ -170,12 +170,10 @@ class RFM9xFSK(RFMSPI): Also note this library tries to be compatible with raw RadioHead Arduino library communication. This means the library sets up the radio modulation - to match RadioHead's defaults and assumes that each packet contains a - 4 byte header compatible with RadioHead's implementation. + to match RadioHead's defaults. Advanced RadioHead features like address/node specific packets or "reliable datagram" delivery are supported however due to the - limitations noted, "reliable datagram" is still subject to missed packets but with it, - sender is notified if a packet has potentially been missed. + limitations noted, "reliable datagram" is still subject to missed packets. """ operation_mode = RFMSPI.RegisterBits(_RF95_REG_01_OP_MODE, bits=3) @@ -567,12 +565,8 @@ def read_fifo(self) -> bytearray: """Read the data from the FIFO.""" # Read the length of the FIFO. fifo_length = self.read_u8(_RF95_REG_00_FIFO) - # Handle if the received packet is too small to include the 4 byte - # RadioHead header and at least one byte of data --reject this packet and ignore it. if fifo_length > 0: # read and clear the FIFO if anything in it packet = bytearray(fifo_length) # read the packet self.read_into(_RF95_REG_00_FIFO, packet, fifo_length) - if fifo_length < 5: - packet = None return packet diff --git a/adafruit_rfm/rfm_common.py b/adafruit_rfm/rfm_common.py index bfd0f55..04096db 100644 --- a/adafruit_rfm/rfm_common.py +++ b/adafruit_rfm/rfm_common.py @@ -37,7 +37,7 @@ __version__ = "0.0.0+auto.0" -__repo__ = "https://github.com/jerryneedell/CircuitPython_RFM.git" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RFM.git" # RadioHead specific compatibility constants. @@ -378,7 +378,7 @@ async def asyncio_send_with_ack(self, data: ReadableBuffer) -> bool: send_with_ack = asyncio_to_blocking(asyncio_send_with_ack) - async def asyncio_receive( + async def asyncio_receive( # noqa: PLR0912 self, *, keep_listening: bool = True, @@ -425,6 +425,9 @@ async def asyncio_receive( else: packet = self.read_fifo() if self.radiohead: + if len(packet) < 5: + # reject the packet if it is too small to contain the RAdioHead Header + packet = None if packet is not None: if ( self.node != _RH_BROADCAST_ADDRESS # noqa: PLR1714 @@ -492,6 +495,9 @@ async def asyncio_receive_with_ack( # noqa: PLR0912 else: packet = self.read_fifo() if self.radiohead: + if len(packet) < 5: + # reject the packet if it is too small to contain the RAdioHead Header + packet = None if packet is not None: if ( self.node != _RH_BROADCAST_ADDRESS # noqa: PLR1714 diff --git a/examples/rfm69_raw.py b/examples/rfm69_raw.py deleted file mode 100644 index d0a15c3..0000000 --- a/examples/rfm69_raw.py +++ /dev/null @@ -1,46 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) -# set the time interval (seconds) for sending packets -rfm69.radiohead = False -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = None -# rfm69.encryption_key = ( -# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" -# ) - - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # Look for a new packet - wait up to 5 seconds: - packet = rfm69.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:]]) - print(f"RSSI: {rfm69.last_rssi}") diff --git a/examples/rfm69_rh_asyncio_node2.py b/examples/rfm69_rh_asyncio_node2.py deleted file mode 100644 index 96959af..0000000 --- a/examples/rfm69_rh_asyncio_node2.py +++ /dev/null @@ -1,109 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example using Interrupts to send a message and then wait indefinitely for messages -# to be received. Interrupts are used only for receive. sending is done with polling. -# This example is for systems that support interrupts like the Raspberry Pi with "blinka" -# CircuitPython does not support interrupts so it will not work on Circutpython boards -# Author: Tony DiCola, Jerry Needell -import asyncio -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set node addresses -rfm69.node = 2 -rfm69.destination = 100 -rfm69.ack_wait = 0.4 -# send startup message from my_node -# rfm69.send_with_ack(bytes("startup message from node {}".format(rfm69.node), "UTF-8")) -rfm69.listen() -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer - - -# pylint: disable=too-few-public-methods -class Packet: - """Simple class to hold an value. Use .value to to read or write.""" - - def __init__(self): - self.received = False - - -# setup interrupt callback function -async def wait_for_packets(packet_status, lock): - while True: - if rfm69.payload_ready(): - if lock.locked(): - print("locked waiting for receive") - async with lock: - packet = await rfm69.asyncio_receive_with_ack(with_header=True, timeout=None) - if packet is not None: - packet_status.received = True - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - print([hex(x) for x in packet]) - print(f"RSSI: {rfm69.last_rssi}") - await asyncio.sleep(0.001) - - -async def send_packets(packet_status, lock): - # initialize counter - counter = 0 - ack_failed_counter = 0 - counter = 0 - transmit_interval = 5 - time_now = time.monotonic() - while True: - # If no packet was received during the timeout then None is returned. - if packet_status.received: - packet_status.received = False - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - counter += 1 - # send a mesage to destination_node from my_node - if lock.locked(): - print("locked waiting for send") - async with lock: - if not await rfm69.asyncio_send_with_ack( - bytes( - f"message from node {rfm69.node} {counter} {ack_failed_counter}", - "UTF-8", - ) - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) - await asyncio.sleep(0.001) - - -async def main(): - packet_status = Packet() - lock = asyncio.Lock() - task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) - task2 = asyncio.create_task(send_packets(packet_status, lock)) - - await asyncio.gather(task1, task2) # Don't forget "await"! - - -asyncio.run(main()) diff --git a/examples/rfm69_rh_header.py b/examples/rfm69_rh_header.py deleted file mode 100644 index c3224ef..0000000 --- a/examples/rfm69_rh_header.py +++ /dev/null @@ -1,47 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to display raw packets including header - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) - - -# set the time interval (seconds) for sending packets -transmit_interval = 10 - -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm69.receive(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"RSSI: {rfm69.last_rssi}") - # send reading after any packet received diff --git a/examples/rfm69_rh_node1.py b/examples/rfm69_rh_node1.py deleted file mode 100644 index 5ca9a95..0000000 --- a/examples/rfm69_rh_node1.py +++ /dev/null @@ -1,63 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically between addressed nodes - -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set the time interval (seconds) for sending packets -transmit_interval = 10 -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" - -# set node addresses -rfm69.node = 1 -rfm69.destination = 2 -# initialize counter -counter = 0 -# send a broadcast message from my_node with ID = counter -rfm69.send(bytes(f"Startup message {counter} from node {rfm69.node}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -now = time.monotonic() -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm69.receive(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:4]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"Received RSSI: {rfm69.last_rssi}") - if time.monotonic() - now > transmit_interval: - now = time.monotonic() - counter = counter + 1 - # send a mesage to destination_node from my_node - rfm69.send( - bytes(f"message number {counter} from node {rfm69.node}", "UTF-8"), - keep_listening=True, - ) - button_pressed = None diff --git a/examples/rfm69_rh_node1_ack.py b/examples/rfm69_rh_node1_ack.py deleted file mode 100644 index 16b7474..0000000 --- a/examples/rfm69_rh_node1_ack.py +++ /dev/null @@ -1,71 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically between addressed nodes with ACK - -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) - - -# set the time interval (seconds) for sending packets -transmit_interval = 10 - -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" - -# set delay before sending ACK -rfm69.ack_delay = 0.1 -# set node addresses -rfm69.node = 1 -rfm69.destination = 2 -# initialize counter -counter = 0 -ack_failed_counter = 0 -# send startup message from my_node -rfm69.send_with_ack(bytes(f"startup message from node {rfm69.node}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -time_now = time.monotonic() -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm69.receive_with_ack(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:4]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"RSSI: {rfm69.last_rssi}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - counter += 1 - # send a mesage to destination_node from my_node - if not rfm69.send_with_ack( - bytes(f"message from node node {rfm69.node} {counter}", "UTF-8") - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm69_rh_node2.py b/examples/rfm69_rh_node2.py deleted file mode 100644 index 5b1a416..0000000 --- a/examples/rfm69_rh_node2.py +++ /dev/null @@ -1,67 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically between addressed nodes - -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) - - -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" - -# set node addresses -rfm69.node = 2 -rfm69.destination = 1 -# initialize counter -counter = 0 -# send a broadcast message from my_node with ID = counter -rfm69.send(bytes(f"startup message from node {rfm69.node} ", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -time_now = time.monotonic() -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm69.receive(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:4]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"Received RSSI: {rfm69.last_rssi}") - # send reading after any packet received - counter = counter + 1 - # after 10 messages send a response to destination_node from my_node with ID = counter&0xff - if counter % 10 == 0: - time.sleep(0.5) # brief delay before responding - rfm69.identifier = counter & 0xFF - rfm69.send( - bytes( - f"message number {counter} from node {rfm69.node} ", - "UTF-8", - ), - keep_listening=True, - ) diff --git a/examples/rfm69_rh_node2_ack.py b/examples/rfm69_rh_node2_ack.py deleted file mode 100644 index e824b69..0000000 --- a/examples/rfm69_rh_node2_ack.py +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to receive addressed packed with ACK and send a response - -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) - -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" - -# set delay before transmitting ACK (seconds) -rfm69.ack_delay = 0.1 -# set node addresses -rfm69.node = 2 -rfm69.destination = 1 -# initialize counter -counter = 0 -ack_failed_counter = 0 - -# Wait to receive packets. -print("Waiting for packets...") -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm69.receive_with_ack(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:4]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"RSSI: {rfm69.last_rssi}") - # send response 2 sec after any packet received - time.sleep(2) - counter += 1 - # send a mesage to destination_node from my_node - if not rfm69.send_with_ack(bytes(f"response from node {rfm69.node} {counter}", "UTF-8")): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm69_rh_simpletest.py b/examples/rfm69_rh_simpletest.py deleted file mode 100644 index 698635a..0000000 --- a/examples/rfm69_rh_simpletest.py +++ /dev/null @@ -1,69 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Simple demo of sending and recieving data with the RFM69 LoRa radio. -# Author: Jerry Needell -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" - -# Print out some chip state: -print(f"Temperature: {rfm69.temperature}C") -print(f"Frequency: {rfm69.frequency_mhz}mhz") -print(f"Bit rate: {rfm69.bitrate / 1000}kbit/s") -print(f"Frequency deviation: {rfm69.frequency_deviation}hz") - -# Send a packet. Note you can only send a packet up to 60 bytes in length. -# This is a limitation of the radio packet size, so if you need to send larger -# amounts of data you will need to break it into smaller send calls. Each send -# call will wait for the previous one to finish before continuing. -rfm69.send(bytes("Hello world!\r\n", "utf-8")) -print("Sent hello world message!") - -# Wait to receive packets. Note that this library can't receive data at a fast -# rate, in fact it can only receive and process one 60 byte packet at a time. -# This means you should only use this for low bandwidth scenarios, like sending -# and receiving a single message at a time. -print("Waiting for packets...") -while True: - packet = rfm69.receive() - # Optionally change the receive timeout from its default of 0.5 seconds: - # packet = rfm69.receive(timeout=5.0) - # If no packet was received during the timeout then None is returned. - if packet is None: - # Packet has not been received - print("Received nothing! Listening again...") - else: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # And decode to ASCII text and print it too. Note that you always - # receive raw bytes and need to convert to a text format like ASCII - # if you intend to do string processing on your data. Make sure the - # sending side is sending ASCII data before you try to decode! - try: - packet_text = str(packet, "ascii") - print(f"Received (ASCII): {packet_text}") - except UnicodeError: - print("Hex data: ", [hex(x) for x in packet]) - rssi = rfm69.last_rssi - print(f"Received signal strength: {rssi} dB") diff --git a/examples/rfm69_rh_transmit.py b/examples/rfm69_rh_transmit.py deleted file mode 100644 index cf2f903..0000000 --- a/examples/rfm69_rh_transmit.py +++ /dev/null @@ -1,62 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically - -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) -# set the time interval (seconds) for sending packets -transmit_interval = 5 - -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = None -# rfm69.encryption_key = ( -# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" -# ) - -# initialize counter -counter = 0 -# send a broadcast mesage -rfm69.send(bytes(f"message number {counter}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -send_reading = False -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 5 seconds: - packet = rfm69.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - # clear flag to send data - send_reading = False - counter = counter + 1 - rfm69.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm69ook_raw.py b/examples/rfm69ook_raw.py deleted file mode 100644 index 6dc1a9c..0000000 --- a/examples/rfm69ook_raw.py +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) -# set the time interval (seconds) for sending packets -node = 1 -destination = 2 -rfm69.radiohead = False -print(rfm69.modulation_type) -rfm69.modulation_type = 1 -print(rfm69.modulation_type) -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = None -# rfm69.encryption_key = ( -# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" -# ) - - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # Look for a new packet - wait up to 5 seconds: - packet = rfm69.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:]]) - print(f"RSSI: {rfm69.last_rssi}") diff --git a/examples/rfm9x_rh_asyncio_node2.py b/examples/rfm9x_rh_asyncio_node2.py deleted file mode 100644 index e9b02ef..0000000 --- a/examples/rfm9x_rh_asyncio_node2.py +++ /dev/null @@ -1,108 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example using Interrupts to send a message and then wait indefinitely for messages -# to be received. Interrupts are used only for receive. sending is done with polling. -# This example is for systems that support interrupts like the Raspberry Pi with "blinka" -# CircuitPython does not support interrupts so it will not work on Circutpython boards -# Author: Tony DiCola, Jerry Needell -import asyncio -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9x - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set node addresses -rfm9x.node = 2 -rfm9x.destination = 100 -# send startup message from my_node -# rfm9x.send_with_ack(bytes("startup message from node {}".format(rfm9x.node), "UTF-8")) -rfm9x.listen() -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer - - -# pylint: disable=too-few-public-methods -class Packet: - """Simple class to hold an value. Use .value to to read or write.""" - - def __init__(self): - self.received = False - - -# setup interrupt callback function -async def wait_for_packets(packet_status, lock): - while True: - if rfm9x.payload_ready(): - if lock.locked(): - print("locked waiting for receive") - async with lock: - packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) - if packet is not None: - packet_status.received = True - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") - await asyncio.sleep(0.001) - - -async def send_packets(packet_status, lock): - # initialize counter - counter = 0 - ack_failed_counter = 0 - counter = 0 - transmit_interval = 5 - time_now = time.monotonic() - while True: - # If no packet was received during the timeout then None is returned. - if packet_status.received: - packet_status.received = False - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - counter += 1 - # send a mesage to destination_node from my_node - if lock.locked(): - print("locked waiting for send") - async with lock: - if not await rfm9x.asyncio_send_with_ack( - bytes( - f"message from node {rfm9x.node} {counter} {ack_failed_counter}", - "UTF-8", - ) - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) - await asyncio.sleep(0.001) - - -async def main(): - packet_status = Packet() - lock = asyncio.Lock() - task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) - task2 = asyncio.create_task(send_packets(packet_status, lock)) - - await asyncio.gather(task1, task2) # Don't forget "await"! - - -asyncio.run(main()) diff --git a/examples/rfm9x_rh_node2.py b/examples/rfm9x_rh_node2.py deleted file mode 100644 index 05ff566..0000000 --- a/examples/rfm9x_rh_node2.py +++ /dev/null @@ -1,63 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically between addressed nodes -# Author: Jerry Needell -# -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9x - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set node addresses -rfm9x.node = 2 -rfm9x.destination = 1 -# initialize counter -counter = 0 -# send a broadcast message from my_node with ID = counter -rfm9x.send(bytes(f"startup message from node {rfm9x.node} ", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -time_now = time.monotonic() -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm9x.receive(with_header=True, timeout=5.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:4]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"Received RSSI: {rfm9x.last_rssi}") - # send reading after any packet received - counter = counter + 1 - # after 10 messages send a response to destination_node from my_node with ID = counter&0xff - if counter % 10 == 0: - time.sleep(0.5) # brief delay before responding - rfm9x.identifier = counter & 0xFF - rfm9x.send( - bytes( - f"message number {counter} from node {rfm9x.node} ", - "UTF-8", - ), - keep_listening=True, - ) diff --git a/examples/rfm9x_rh_node2_ack.py b/examples/rfm9x_rh_node2_ack.py deleted file mode 100644 index 62f681b..0000000 --- a/examples/rfm9x_rh_node2_ack.py +++ /dev/null @@ -1,54 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to receive addressed packed with ACK and send a response -# Author: Jerry Needell -# -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9x - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set node addresses -rfm9x.node = 2 -rfm9x.destination = 1 -# initialize counter -counter = 0 -ack_failed_counter = 0 - -# Wait to receive packets. -print("Waiting for packets...") -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm9x.receive_with_ack(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:4]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"RSSI: {rfm9x.last_rssi}") - # send response 2 sec after any packet received - time.sleep(2) - counter += 1 - # send a mesage to destination_node from my_node - if not rfm9x.send_with_ack(bytes(f"response from node {rfm9x.node} {counter}", "UTF-8")): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9xfsk_raw.py b/examples/rfm9xfsk_raw.py deleted file mode 100644 index ccf69e0..0000000 --- a/examples/rfm9xfsk_raw.py +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to display raw packets including header -# Author: Jerry Needell -# -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) -rfm9xfsk.radiohead = False -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm9xfsk.receive() - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:]]) - print(f"RSSI: {rfm9xfsk.last_rssi}") - # send reading after any packet received diff --git a/examples/rfm9xfsk_rh_asyncio_node1.py b/examples/rfm9xfsk_rh_asyncio_node1.py deleted file mode 100644 index e959c5f..0000000 --- a/examples/rfm9xfsk_rh_asyncio_node1.py +++ /dev/null @@ -1,104 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -import asyncio -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set node addresses -rfm9x.node = 1 -rfm9x.destination = 100 -rfm9x.ack_wait = 0.4 -# send startup message from my_node -rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) -rfm9x.listen() -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer - - -# pylint: disable=too-few-public-methods -class Packet: - """Simple class to hold an value. Use .value to to read or write.""" - - def __init__(self): - self.received = False - - -# setup interrupt callback function -async def wait_for_packets(packet_status, lock): - while True: - if rfm9x.payload_ready(): - if lock.locked(): - print("locked waiting for receive") - async with lock: - packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) - if packet is not None: - packet_status.received = True - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") - await asyncio.sleep(0.001) - - -async def send_packets(packet_status, lock): - # initialize counter - counter = 0 - ack_failed_counter = 0 - counter = 0 - transmit_interval = 5 - time_now = time.monotonic() - while True: - # If no packet was received during the timeout then None is returned. - if packet_status.received: - packet_status.received = False - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - counter += 1 - # send a mesage to destination_node from my_node - if lock.locked(): - print("locked waiting for send") - async with lock: - if not await rfm9x.asyncio_send_with_ack( - bytes( - f"message from node {rfm9x.node} {counter} {ack_failed_counter}", - "UTF-8", - ) - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) - await asyncio.sleep(0.1) - - -async def main(): - packet_status = Packet() - lock = asyncio.Lock() - task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) - task2 = asyncio.create_task(send_packets(packet_status, lock)) - - await asyncio.gather(task1, task2) # Don't forget "await"! - - -asyncio.run(main()) diff --git a/examples/rfm9xfsk_rh_header.py b/examples/rfm9xfsk_rh_header.py deleted file mode 100644 index 37537a6..0000000 --- a/examples/rfm9xfsk_rh_header.py +++ /dev/null @@ -1,40 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to display raw packets including header -# Author: Jerry Needell -# -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm9xfsk.receive(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"RSSI: {rfm9xfsk.last_rssi}") - # send reading after any packet received diff --git a/examples/rfm9xfsk_rh_interrupt_node1.py b/examples/rfm9xfsk_rh_interrupt_node1.py deleted file mode 100644 index f029860..0000000 --- a/examples/rfm9xfsk_rh_interrupt_node1.py +++ /dev/null @@ -1,89 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example using Interrupts to send a message and then wait indefinitely for messages -# to be received. Interrupts are used only for receive. sending is done with polling. -# This example is for systems that support interrupts like the Raspberry Pi with "blinka" -# CircuitPython does not support interrupts so it will not work on Circutpython boards -# Author: Tony DiCola, Jerry Needell -import board -import busio -import digitalio -import RPi.GPIO as io - -from adafruit_rfm import rfm9xfsk - - -# setup interrupt callback function -def rfm9x_callback(rfm9x_irq): - global packet_received # noqa: PLW0603 - print("OP MODE: ", hex(rfm9x.read_u8(0x01))) - print("IRQ FLAGS 2: ", hex(rfm9x.read_u8(0x3F))) - print("IRQ detected ", rfm9x_irq, rfm9x.packet_sent(), rfm9x.payload_ready()) - # check to see if this was a rx interrupt - ignore tx - if rfm9x.payload_ready(): - packet = rfm9x.receive_with_ack(with_header=True, timeout=None) - if packet is not None: - packet_received = True - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") - - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm9x.tx_power = 23 - -# configure the interrupt pin and event handling. -RFM9X_G0 = 22 -io.setmode(io.BCM) -io.setup(RFM9X_G0, io.IN, pull_up_down=io.PUD_DOWN) # activate input -io.add_event_detect(RFM9X_G0, io.RISING) -io.add_event_callback(RFM9X_G0, rfm9x_callback) - -packet_received = False - -# set delay before sending ACK -rfm9x.ack_delay = None -# set node addresses -rfm9x.node = 1 -rfm9x.destination = 2 -# initialize counter -counter = 0 -ack_failed_counter = 0 -rfm9x.listen() -# send startup message from my_node -rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # If no packet was received during the timeout then None is returned. - if packet_received: - packet_received = False - counter += 1 - # send a mesage to destination_node from my_node - if not rfm9x.send_with_ack( - bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9xfsk_rh_interrupt_node2.py b/examples/rfm9xfsk_rh_interrupt_node2.py deleted file mode 100644 index 48e51ff..0000000 --- a/examples/rfm9xfsk_rh_interrupt_node2.py +++ /dev/null @@ -1,89 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example using Interrupts to send a message and then wait indefinitely for messages -# to be received. Interrupts are used only for receive. sending is done with polling. -# This example is for systems that support interrupts like the Raspberry Pi with "blinka" -# CircuitPython does not support interrupts so it will not work on Circutpython boards -# Author: Tony DiCola, Jerry Needell -import board -import busio -import digitalio -import RPi.GPIO as io - -from adafruit_rfm import rfm9xfsk - - -# setup interrupt callback function -def rfm9x_callback(rfm9x_irq): - global packet_received # noqa: PLW0603 - print("OP MODE: ", hex(rfm9x.read_u8(0x01))) - print("IRQ FLAGS 2: ", hex(rfm9x.read_u8(0x3F))) - print("IRQ detected ", rfm9x_irq, rfm9x.packet_sent(), rfm9x.payload_ready()) - # check to see if this was a rx interrupt - ignore tx - if rfm9x.payload_ready(): - packet = rfm9x.receive_with_ack(with_header=True, timeout=None) - if packet is not None: - packet_received = True - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") - - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm9x.tx_power = 23 - -# configure the interrupt pin and event handling. -RFM9X_G0 = 22 -io.setmode(io.BCM) -io.setup(RFM9X_G0, io.IN, pull_up_down=io.PUD_DOWN) # activate input -io.add_event_detect(RFM9X_G0, io.RISING) -io.add_event_callback(RFM9X_G0, rfm9x_callback) - -packet_received = False - -# set delay before sending ACK -rfm9x.ack_delay = None -# set node addresses -rfm9x.node = 2 -rfm9x.destination = 1 -# initialize counter -counter = 0 -ack_failed_counter = 0 -rfm9x.listen() -# send startup message from my_node -rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # If no packet was received during the timeout then None is returned. - if packet_received: - packet_received = False - counter += 1 - # send a mesage to destination_node from my_node - if not rfm9x.send_with_ack( - bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9xfsk_rh_node1.py b/examples/rfm9xfsk_rh_node1.py deleted file mode 100644 index dd9ec29..0000000 --- a/examples/rfm9xfsk_rh_node1.py +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically -# Author: Jerry Needell -# -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set the time interval (seconds) for sending packets -transmit_interval = 5 - -# set node addresses -rfm9xfsk.node = 1 -rfm9xfsk.destination = 2 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -# rfm9xfsk.tx_power = 23 - - -# initialize counter -counter = 0 -# send a broadcast mesage -rfm9xfsk.send(b"\2" + bytes(f"message number {counter}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm9xfsk.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - counter = counter + 1 - rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xfsk_rh_node1_ack.py b/examples/rfm9xfsk_rh_node1_ack.py deleted file mode 100644 index 0b0ad56..0000000 --- a/examples/rfm9xfsk_rh_node1_ack.py +++ /dev/null @@ -1,67 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically between addressed nodes with ACK - -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - - -# set the time interval (seconds) for sending packets -transmit_interval = 10 - -# set delay before sending ACK -rfm9xfsk.ack_delay = 0.1 -# set node addresses -rfm9xfsk.node = 1 -rfm9xfsk.destination = 2 -# initialize counter -counter = 0 -ack_failed_counter = 0 -# send startup message from my_node -rfm9xfsk.send_with_ack(bytes(f"startup message from node {rfm9xfsk.node}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -time_now = time.monotonic() -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm9xfsk.receive_with_ack(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:4]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"RSSI: {rfm9xfsk.last_rssi}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - counter += 1 - # send a mesage to destination_node from my_node - if not rfm9xfsk.send_with_ack( - bytes(f"message from node node {rfm9xfsk.node} {counter}", "UTF-8") - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9xfsk_rh_node2.py b/examples/rfm9xfsk_rh_node2.py deleted file mode 100644 index ff82260..0000000 --- a/examples/rfm9xfsk_rh_node2.py +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically -# Author: Jerry Needell -# -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - - -# set node addresses -rfm9xfsk.node = 2 -rfm9xfsk.destination = 1 -# set the time interval (seconds) for sending packets -transmit_interval = 5 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -# rfm9xfsk.tx_power = 23 - - -# initialize counter -counter = 0 -# send a broadcast mesage -rfm9xfsk.send(b"\2" + bytes(f"message number {counter}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm9xfsk.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - counter = counter + 1 - rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xfsk_rh_node2_ack.py b/examples/rfm9xfsk_rh_node2_ack.py deleted file mode 100644 index 994964e..0000000 --- a/examples/rfm9xfsk_rh_node2_ack.py +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to receive addressed packed with ACK and send a response - -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - - -# set delay before transmitting ACK (seconds) -rfm9xfsk.ack_delay = 0.1 -rfm9xfsk.ack_wait = 0.2 -# set node addresses -rfm9xfsk.node = 2 -rfm9xfsk.destination = 1 -# initialize counter -counter = 0 -ack_failed_counter = 0 - -# Wait to receive packets. -print("Waiting for packets...") -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm9xfsk.receive_with_ack(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:4]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"RSSI: {rfm9xfsk.last_rssi}") - # send response 2 sec after any packet received - time.sleep(2) - counter += 1 - # send a mesage to destination_node from my_node - if not rfm9xfsk.send_with_ack( - bytes(f"response from node {rfm9xfsk.node} {counter}", "UTF-8") - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9xfsk_rh_simpletest.py b/examples/rfm9xfsk_rh_simpletest.py deleted file mode 100644 index b20d441..0000000 --- a/examples/rfm9xfsk_rh_simpletest.py +++ /dev/null @@ -1,63 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Simple demo of sending and recieving data with the rfm9x FSK radio. -# Author: Jerry Needell -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - -# Print out some chip state: -print(f"Temperature: {rfm9xfsk.temperature}C") -print(f"Frequency: {rfm9xfsk.frequency_mhz}mhz") -print(f"Bit rate: {rfm9xfsk.bitrate / 1000}kbit/s") -print(f"Frequency deviation: {rfm9xfsk.frequency_deviation}hz") - -# Send a packet. Note you can only send a packet up to 252 bytes in length. -# This is a limitation of the radio packet size, so if you need to send larger -# amounts of data you will need to break it into smaller send calls. Each send -# call will wait for the previous one to finish before continuing. -rfm9xfsk.send(bytes("Hello world!\r\n", "utf-8")) -print("Sent hello world message!") - -# Wait to receive packets. Note that this library can't receive data at a fast -# rate, in fact it can only receive and process one 252 byte packet at a time. -# This means you should only use this for low bandwidth scenarios, like sending -# and receiving a single message at a time. -print("Waiting for packets...") -while True: - packet = rfm9xfsk.receive() - # Optionally change the receive timeout from its default of 0.5 seconds: - # packet = rfm9xfsk.receive(timeout=5.0) - # If no packet was received during the timeout then None is returned. - if packet is None: - # Packet has not been received - print("Received nothing! Listening again...") - else: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # And decode to ASCII text or HEX if the ASCII decode fails - try: - packet_text = str(packet, "ascii") - print(f"Received (ASCII): {packet_text}") - except UnicodeDecodeError: - print("Hex data: ", [hex(x) for x in packet]) - rssi = rfm9xfsk.last_rssi - print(f"Received signal strength: {rssi} dB") diff --git a/examples/rfm9xfsk_rh_transmit.py b/examples/rfm9xfsk_rh_transmit.py deleted file mode 100644 index bfe7568..0000000 --- a/examples/rfm9xfsk_rh_transmit.py +++ /dev/null @@ -1,65 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically -# Author: Jerry Needell -# -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set the time interval (seconds) for sending packets -transmit_interval = 5 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm9xfsk.tx_power = 23 - - -# initialize counter -counter = 0 -# send a broadcast mesage -rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -send_reading = False -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm9xfsk.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - # clear flag to send data - send_reading = False - counter = counter + 1 - rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xook_raw.py b/examples/rfm9xook_raw.py deleted file mode 100644 index 8a6038a..0000000 --- a/examples/rfm9xook_raw.py +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to display raw packets including header -# Author: Jerry Needell -# -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) -rfm9xfsk.radiohead = False -print(rfm9xfsk.modulation_type) -rfm9xfsk.modulation_type = 1 -print(rfm9xfsk.modulation_type) # Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm9xfsk.receive() - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:]]) - print(f"RSSI: {rfm9xfsk.last_rssi}") - # send reading after any packet received diff --git a/examples/rfm9x_rh_simpletest.py b/examples/rfm_raw.py similarity index 68% rename from examples/rfm9x_rh_simpletest.py rename to examples/rfm_raw.py index 5fd64b5..4d8efed 100644 --- a/examples/rfm9x_rh_simpletest.py +++ b/examples/rfm_raw.py @@ -1,14 +1,13 @@ # SPDX-FileCopyrightText: 2024 Ladyada for Adafruit Industries # SPDX-License-Identifier: MIT -# Simple demo of sending and recieving data with the RFM95 LoRa radio. +# Simple demo of sending and recieving data with the RFM9x or RFM69 radios. # Author: Jerry Needell + import board import busio import digitalio -from adafruit_rfm import rfm9x - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -21,30 +20,42 @@ spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +# from adafruit_rfm import rfm9x + +# rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +from adafruit_rfm import rfm9xfsk + +rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! +# rfm = rfm9x.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm9x.tx_power = 23 +# Disable the RadioHead Header +rfm.radiohead = False -# Send a packet. Note you can only send a packet up to 252 bytes in length. +# Send a packet. Note you can only send a packet containing up to 60 bytes for an RFM69 +# and 252 bytes forn an RFM9x. # This is a limitation of the radio packet size, so if you need to send larger # amounts of data you will need to break it into smaller send calls. Each send # call will wait for the previous one to finish before continuing. -rfm9x.send(bytes("Hello world!\r\n", "utf-8")) +rfm.send(bytes("Hello world!\r\n", "utf-8")) print("Sent Hello World message!") -# Wait to receive packets. Note that this library can't receive data at a fast -# rate, in fact it can only receive and process one 252 byte packet at a time. -# This means you should only use this for low bandwidth scenarios, like sending -# and receiving a single message at a time. +# Wait to receive packets. print("Waiting for packets...") while True: - packet = rfm9x.receive() + packet = rfm.receive() # Optionally change the receive timeout from its default of 0.5 seconds: # packet = rfm9x.receive(timeout=5.0) # If no packet was received during the timeout then None is returned. @@ -66,5 +77,5 @@ print("Hex data: ", [hex(x) for x in packet]) # Also read the RSSI (signal strength) of the last received message and # print it. - rssi = rfm9x.last_rssi + rssi = rfm.last_rssi print(f"Received signal strength: {rssi} dB") diff --git a/examples/rfm9x_rh_asyncio_listen.py b/examples/rfm_rh_asyncio_listen.py similarity index 72% rename from examples/rfm9x_rh_asyncio_listen.py rename to examples/rfm_rh_asyncio_listen.py index ec405dc..923bace 100644 --- a/examples/rfm9x_rh_asyncio_listen.py +++ b/examples/rfm_rh_asyncio_listen.py @@ -12,8 +12,6 @@ import busio import digitalio -from adafruit_rfm import rfm9x - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -26,11 +24,30 @@ spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +from adafruit_rfm import rfm9x + +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +# from adafruit_rfm import rfm9xfsk + +# rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 + +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + # send startup message from my_node -# rfm9x.send(bytes("startup message from node {}".format(rfm9x.node), "UTF-8")) -rfm9x.listen() +# rfm.send(bytes("startup message from node {}".format(rfm.node), "UTF-8")) +rfm.listen() # Wait to receive packets. print("Waiting for packets...") # initialize flag and timer @@ -47,15 +64,15 @@ def __init__(self): # setup interrupt callback function async def wait_for_packets(packet_status): while True: - if rfm9x.payload_ready(): - packet = await rfm9x.asyncio_receive(with_header=True, timeout=None) + if rfm.payload_ready(): + packet = await rfm.asyncio_receive(with_header=True, timeout=None) if packet is not None: packet_status.received = True # Received a packet! # Print out the raw bytes of the packet: print(f"Received (raw bytes): {packet}") print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") + print(f"RSSI: {rfm.last_rssi}") await asyncio.sleep(0.001) diff --git a/examples/rfm9x_rh_asyncio_raw.py b/examples/rfm_rh_asyncio_listen_ack.py similarity index 64% rename from examples/rfm9x_rh_asyncio_raw.py rename to examples/rfm_rh_asyncio_listen_ack.py index ddd7db0..a7961ea 100644 --- a/examples/rfm9x_rh_asyncio_raw.py +++ b/examples/rfm_rh_asyncio_listen_ack.py @@ -12,8 +12,6 @@ import busio import digitalio -from adafruit_rfm import rfm9x - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -26,11 +24,30 @@ spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +from adafruit_rfm import rfm9x + +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +# from adafruit_rfm import rfm9xfsk + +# rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 + +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + # send startup message from my_node -rfm9x.send(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) -rfm9x.listen() +# rfm.send(bytes("startup message from node {}".format(rfm.node), "UTF-8")) +rfm.listen() # Wait to receive packets. print("Waiting for packets...") # initialize flag and timer @@ -47,41 +64,23 @@ def __init__(self): # setup interrupt callback function async def wait_for_packets(packet_status): while True: - if rfm9x.payload_ready(): - packet = await rfm9x.asyncio_receive(with_header=True, timeout=None) + if rfm.payload_ready(): + packet = await rfm.asyncio_receive_with_ack(with_header=True, timeout=None) if packet is not None: packet_status.received = True # Received a packet! # Print out the raw bytes of the packet: print(f"Received (raw bytes): {packet}") print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") - await asyncio.sleep(0.001) - - -async def send_packets(packet_status): - # initialize counter - counter = 0 - while True: - # If no packet was received during the timeout then None is returned. - if packet_status.received: - packet_status.received = False - await asyncio.sleep(0.5) # delay .5 seconds before responding - counter += 1 - # send a mesage to destination_node from my_node - await rfm9x.asyncio_send( - bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8"), - keep_listening=True, - ) + print(f"RSSI: {rfm.last_rssi}") await asyncio.sleep(0.001) async def main(): packet_status = Packet() task1 = asyncio.create_task(wait_for_packets(packet_status)) - task2 = asyncio.create_task(send_packets(packet_status)) - await asyncio.gather(task1, task2) # Don't forget "await"! + await asyncio.gather(task1) # Don't forget "await"! asyncio.run(main()) diff --git a/examples/rfm9x_rh_asyncio_node1.py b/examples/rfm_rh_asyncio_node1.py similarity index 74% rename from examples/rfm9x_rh_asyncio_node1.py rename to examples/rfm_rh_asyncio_node1.py index 4aebeb1..e57448c 100644 --- a/examples/rfm9x_rh_asyncio_node1.py +++ b/examples/rfm_rh_asyncio_node1.py @@ -8,8 +8,6 @@ import busio import digitalio -from adafruit_rfm import rfm9x - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -22,14 +20,33 @@ spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +from adafruit_rfm import rfm9x + +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +# from adafruit_rfm import rfm9xfsk + +# rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 + +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + # set node addresses -rfm9x.node = 1 -rfm9x.destination = 100 +rfm.node = 1 +rfm.destination = 100 # send startup message from my_node -rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) -rfm9x.listen() +rfm.send_with_ack(bytes(f"startup message from node {rfm.node}", "UTF-8")) +rfm.listen() # Wait to receive packets. print("Waiting for packets...") # initialize flag and timer @@ -46,18 +63,18 @@ def __init__(self): # setup interrupt callback function async def wait_for_packets(packet_status, lock): while True: - if rfm9x.payload_ready(): + if rfm.payload_ready(): if lock.locked(): print("locked waiting for receive") async with lock: - packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) + packet = await rfm.asyncio_receive_with_ack(with_header=True, timeout=None) if packet is not None: packet_status.received = True # Received a packet! # Print out the raw bytes of the packet: print(f"Received (raw bytes): {packet}") print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") + print(f"RSSI: {rfm.last_rssi}") await asyncio.sleep(0.001) @@ -80,9 +97,9 @@ async def send_packets(packet_status, lock): if lock.locked(): print("locked waiting for send") async with lock: - if not await rfm9x.asyncio_send_with_ack( + if not await rfm.asyncio_send_with_ack( bytes( - f"message from node {rfm9x.node} {counter} {ack_failed_counter}", + f"message from node {rfm.node} {counter} {ack_failed_counter}", "UTF-8", ) ): diff --git a/examples/rfm9xfsk_rh_base.py b/examples/rfm_rh_base.py similarity index 58% rename from examples/rfm9xfsk_rh_base.py rename to examples/rfm_rh_base.py index 4814df9..9563892 100644 --- a/examples/rfm9xfsk_rh_base.py +++ b/examples/rfm_rh_base.py @@ -5,8 +5,6 @@ import busio import digitalio -from adafruit_rfm import rfm9xfsk - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -19,15 +17,32 @@ spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio -rfm9x = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +# from adafruit_rfm import rfm9x + +# rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +from adafruit_rfm import rfm9xfsk + +rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 + +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # set node addresses -rfm9x.node = 100 -rfm9x.destination = 0xFF -rfm9x.ack_delay = 0.1 +rfm.node = 100 +rfm.destination = 0xFF # send startup message from my_node -rfm9x.send( - bytes(f"startup message from base {rfm9x.node}", "UTF-8"), +rfm.send( + bytes(f"startup message from base {rfm.node}", "UTF-8"), keep_listening=True, ) # Wait to receive packets. @@ -36,11 +51,11 @@ while True: - if rfm9x.payload_ready(): - packet = rfm9x.receive_with_ack(with_header=True, timeout=None) + if rfm.payload_ready(): + packet = rfm.receive(with_header=True, timeout=None) if packet is not None: # Received a packet! # Print out the raw bytes of the packet: print(f"Received (raw bytes): {packet}") print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") + print(f"RSSI: {rfm.last_rssi}") diff --git a/examples/rfm9x_rh_base.py b/examples/rfm_rh_base_ack.py similarity index 57% rename from examples/rfm9x_rh_base.py rename to examples/rfm_rh_base_ack.py index 1400e38..523d661 100644 --- a/examples/rfm9x_rh_base.py +++ b/examples/rfm_rh_base_ack.py @@ -5,8 +5,6 @@ import busio import digitalio -from adafruit_rfm import rfm9x - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -19,14 +17,32 @@ spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +from adafruit_rfm import rfm9x + +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +# from adafruit_rfm import rfm9xfsk + +# rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 + +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # set node addresses -rfm9x.node = 100 -rfm9x.destination = 0xFF +rfm.node = 100 +rfm.destination = 0xFF # send startup message from my_node -rfm9x.send( - bytes(f"startup message from base {rfm9x.node}", "UTF-8"), +rfm.send( + bytes(f"startup message from base {rfm.node}", "UTF-8"), keep_listening=True, ) # Wait to receive packets. @@ -35,11 +51,11 @@ while True: - if rfm9x.payload_ready(): - packet = rfm9x.receive_with_ack(with_header=True, timeout=None) + if rfm.payload_ready(): + packet = rfm.receive_with_ack(with_header=True, timeout=None) if packet is not None: # Received a packet! # Print out the raw bytes of the packet: print(f"Received (raw bytes): {packet}") print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") + print(f"RSSI: {rfm.last_rssi}") diff --git a/examples/rfm9x_rh_node1.py b/examples/rfm_rh_node1.py similarity index 66% rename from examples/rfm9x_rh_node1.py rename to examples/rfm_rh_node1.py index 224a3ae..4278e4b 100644 --- a/examples/rfm9x_rh_node1.py +++ b/examples/rfm_rh_node1.py @@ -10,8 +10,6 @@ import busio import digitalio -from adafruit_rfm import rfm9x - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -24,39 +22,56 @@ spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +from adafruit_rfm import rfm9x + +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +# from adafruit_rfm import rfm9xfsk + +# rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # set the time interval (seconds) for sending packets transmit_interval = 2 # set node addresses -rfm9x.node = 1 -rfm9x.destination = 2 +rfm.node = 1 +rfm.destination = 100 # initialize counter counter = 0 # send a broadcast message from my_node with ID = counter -rfm9x.send(bytes(f"Startup message {counter} from node {rfm9x.node}", "UTF-8")) +rfm.send(bytes(f"Startup message {counter} from node {rfm.node}", "UTF-8")) # Wait to receive packets. print("Waiting for packets...") now = time.monotonic() while True: # Look for a new packet: only accept if addresses to my_node - packet = rfm9x.receive(with_header=True, timeput=5.0) + packet = rfm.receive(with_header=True, timeout=5.0) # If no packet was received during the timeout then None is returned. if packet is not None: # Received a packet! # Print out the raw bytes of the packet: print("Received (raw header):", [hex(x) for x in packet[0:4]]) print(f"Received (raw payload): {packet[4:]}") - print(f"Received RSSI: {rfm9x.last_rssi}") + print(f"Received RSSI: {rfm.last_rssi}") if time.monotonic() - now > transmit_interval: now = time.monotonic() counter = counter + 1 # send a mesage to destination_node from my_node - rfm9x.send( - bytes(f"message number {counter} from node {rfm9x.node}", "UTF-8"), + rfm.send( + bytes(f"message number {counter} from node {rfm.node}", "UTF-8"), keep_listening=True, ) button_pressed = None diff --git a/examples/rfm9x_rh_node1_ack.py b/examples/rfm_rh_node1_ack.py similarity index 67% rename from examples/rfm9x_rh_node1_ack.py rename to examples/rfm_rh_node1_ack.py index 31c2430..9c31c90 100644 --- a/examples/rfm9x_rh_node1_ack.py +++ b/examples/rfm_rh_node1_ack.py @@ -10,8 +10,6 @@ import busio import digitalio -from adafruit_rfm import rfm9x - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -24,20 +22,37 @@ spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +# from adafruit_rfm import rfm9x + +# rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +from adafruit_rfm import rfm9xfsk + +rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # set the time interval (seconds) for sending packets transmit_interval = 10 # set node addresses -rfm9x.node = 1 -rfm9x.destination = 2 +rfm.node = 1 +rfm.destination = 100 # initialize counter counter = 0 ack_failed_counter = 0 # send startup message from my_node -rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) +rfm.send_with_ack(bytes(f"startup message from node {rfm.node}", "UTF-8")) # Wait to receive packets. print("Waiting for packets...") @@ -45,22 +60,20 @@ time_now = time.monotonic() while True: # Look for a new packet: only accept if addresses to my_node - packet = rfm9x.receive_with_ack(with_header=True) + packet = rfm.receive_with_ack(with_header=True) # If no packet was received during the timeout then None is returned. if packet is not None: # Received a packet! # Print out the raw bytes of the packet: print("Received (raw header):", [hex(x) for x in packet[0:4]]) print(f"Received (raw payload): {packet[4:]}") - print(f"RSSI: {rfm9x.last_rssi}") + print(f"RSSI: {rfm.last_rssi}") # send reading after any packet received if time.monotonic() - time_now > transmit_interval: # reset timeer time_now = time.monotonic() counter += 1 # send a mesage to destination_node from my_node - if not rfm9x.send_with_ack( - bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") - ): + if not rfm.send_with_ack(bytes(f"message from node node {rfm.node} {counter}", "UTF-8")): ack_failed_counter += 1 print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm_simpletest.py b/examples/rfm_simpletest.py index 5d9bc2b..2e5107b 100644 --- a/examples/rfm_simpletest.py +++ b/examples/rfm_simpletest.py @@ -1,4 +1,78 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# SPDX-FileCopyrightText: Copyright (c) 2024 Jerry Needell for Adafruit Industries -# -# SPDX-License-Identifier: Unlicense +# SPDX-FileCopyrightText: 2024 Ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Simple demo of sending and recieving data with the RFM9x or RFM69 radios. +# Author: Jerry Needell + +import board +import busio +import digitalio + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +# from adafruit_rfm import rfm9x + +# rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +from adafruit_rfm import rfm9xfsk + +rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 + +# rfm = rfm9x.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Send a packet. Note you can only send a packet containing up to 60 bytes for an RFM69 +# and 252 bytes forn an RFM9x. +# This is a limitation of the radio packet size, so if you need to send larger +# amounts of data you will need to break it into smaller send calls. Each send +# call will wait for the previous one to finish before continuing. +rfm.send(bytes("Hello world!\r\n", "utf-8")) +print("Sent Hello World message!") + +# Wait to receive packets. +print("Waiting for packets...") + +while True: + packet = rfm.receive() + # Optionally change the receive timeout from its default of 0.5 seconds: + # packet = rfm9x.receive(timeout=5.0) + # If no packet was received during the timeout then None is returned. + if packet is None: + # Packet has not been received + print("Received nothing! Listening again...") + else: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + # And decode to ASCII text and print it too. Note that you always + # receive raw bytes and need to convert to a text format like ASCII + # if you intend to do string processing on your data. Make sure the + # sending side is sending ASCII data before you try to decode! + try: + packet_text = str(packet, "ascii") + print(f"Received (ASCII): {packet_text}") + except UnicodeError: + print("Hex data: ", [hex(x) for x in packet]) + # Also read the RSSI (signal strength) of the last received message and + # print it. + rssi = rfm.last_rssi + print(f"Received signal strength: {rssi} dB") From 123be5abca1d34e7994511af2f65ebd4a7642da3 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Wed, 31 Jul 2024 16:55:10 -0400 Subject: [PATCH 5/9] more work on examples --- README.rst | 3 +- examples/rfm9x_rh_interrupt_node1.py | 87 --------------------------- examples/rfm9x_rh_interrupt_node2.py | 87 --------------------------- examples/rfm_raw.py | 3 +- examples/rfm_transmit.py | 90 ++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 177 deletions(-) delete mode 100644 examples/rfm9x_rh_interrupt_node1.py delete mode 100644 examples/rfm9x_rh_interrupt_node2.py create mode 100644 examples/rfm_transmit.py diff --git a/README.rst b/README.rst index 7a591a8..bb1eeca 100644 --- a/README.rst +++ b/README.rst @@ -94,8 +94,7 @@ Or the following command to update an existing version: Usage Example ============= -.. todo:: Add a quick, simple example. It and other examples should live in the -examples folder and be included in docs/examples.rst. +See examples in the GitHub Repository. Documentation ============= diff --git a/examples/rfm9x_rh_interrupt_node1.py b/examples/rfm9x_rh_interrupt_node1.py deleted file mode 100644 index 7d82e3b..0000000 --- a/examples/rfm9x_rh_interrupt_node1.py +++ /dev/null @@ -1,87 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example using Interrupts to send a message and then wait indefinitely for messages -# to be received. Interrupts are used only for receive. sending is done with polling. -# This example is for systems that support interrupts like the Raspberry Pi with "blinka" -# CircuitPython does not support interrupts so it will not work on Circutpython boards -# Author: Tony DiCola, Jerry Needell -import board -import busio -import digitalio -import RPi.GPIO as io - -from adafruit_rfm import rfm9x - - -# setup interrupt callback function -def rfm9x_callback(rfm9x_irq): - global packet_received # noqa: PLW0603 - print("IRQ detected ", rfm9x_irq, rfm9x.packet_sent(), rfm9x.payload_ready()) - # check to see if this was a rx interrupt - ignore tx - if rfm9x.payload_ready(): - packet = rfm9x.receive_with_ack(with_header=True, timeout=None) - if packet is not None: - packet_received = True - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") - - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm9x.tx_power = 23 - -# configure the interrupt pin and event handling. -RFM9X_G0 = 22 -io.setmode(io.BCM) -io.setup(RFM9X_G0, io.IN, pull_up_down=io.PUD_DOWN) # activate input -io.add_event_detect(RFM9X_G0, io.RISING) -io.add_event_callback(RFM9X_G0, rfm9x_callback) - -packet_received = False - -# set delay before sending ACK -rfm9x.ack_delay = None -# set node addresses -rfm9x.node = 1 -rfm9x.destination = 2 -# initialize counter -counter = 0 -ack_failed_counter = 0 -# send startup message from my_node -rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) -rfm9x.listen() -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # If no packet was received during the timeout then None is returned. - if packet_received: - packet_received = False - counter += 1 - # send a mesage to destination_node from my_node - if not rfm9x.send_with_ack( - bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm9x_rh_interrupt_node2.py b/examples/rfm9x_rh_interrupt_node2.py deleted file mode 100644 index 8a1edb0..0000000 --- a/examples/rfm9x_rh_interrupt_node2.py +++ /dev/null @@ -1,87 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example using Interrupts to send a message and then wait indefinitely for messages -# to be received. Interrupts are used only for receive. sending is done with polling. -# This example is for systems that support interrupts like the Raspberry Pi with "blinka" -# CircuitPython does not support interrupts so it will not work on Circutpython boards -# Author: Tony DiCola, Jerry Needell -import board -import busio -import digitalio -import RPi.GPIO as io - -from adafruit_rfm import rfm9x - - -# setup interrupt callback function -def rfm9x_callback(rfm9x_irq): - global packet_received # Noqa: PLW0603 - # print("IRQ detected ", rfm9x_irq, rfm9x.packet_sent(), rfm9x.payload_ready()) - # check to see if this was a rx interrupt - ignore tx - if rfm9x.payload_ready(): - packet = rfm9x.receive_with_ack(with_header=True, timeout=None) - if packet is not None: - packet_received = True - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") - - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm9x.tx_power = 23 - -# configure the interrupt pin and event handling. -RFM9X_G0 = 22 -io.setmode(io.BCM) -io.setup(RFM9X_G0, io.IN, pull_up_down=io.PUD_DOWN) # activate input -io.add_event_detect(RFM9X_G0, io.RISING) -io.add_event_callback(RFM9X_G0, rfm9x_callback) - -packet_received = False - -# set delay before sending ACK -rfm9x.ack_delay = 0.25 -# set node addresses -rfm9x.node = 2 -rfm9x.destination = 1 -# initialize counter -counter = 0 -ack_failed_counter = 0 -# send startup message from my_node -rfm9x.send_with_ack(bytes(f"startup message from node {rfm9x.node}", "UTF-8")) -rfm9x.listen() -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # If no packet was received during the timeout then None is returned. - if packet_received: - packet_received = False - counter += 1 - # send a mesage to destination_node from my_node - if not rfm9x.send_with_ack( - bytes(f"message from node node {rfm9x.node} {counter}", "UTF-8") - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm_raw.py b/examples/rfm_raw.py index 4d8efed..defe180 100644 --- a/examples/rfm_raw.py +++ b/examples/rfm_raw.py @@ -66,6 +66,7 @@ # Received a packet! # Print out the raw bytes of the packet: print(f"Received (raw bytes): {packet}") + print("Hex data: ", [hex(x) for x in packet]) # And decode to ASCII text and print it too. Note that you always # receive raw bytes and need to convert to a text format like ASCII # if you intend to do string processing on your data. Make sure the @@ -74,7 +75,7 @@ packet_text = str(packet, "ascii") print(f"Received (ASCII): {packet_text}") except UnicodeError: - print("Hex data: ", [hex(x) for x in packet]) + pass # Also read the RSSI (signal strength) of the last received message and # print it. rssi = rfm.last_rssi diff --git a/examples/rfm_transmit.py b/examples/rfm_transmit.py new file mode 100644 index 0000000..7a15ae0 --- /dev/null +++ b/examples/rfm_transmit.py @@ -0,0 +1,90 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialze RFM radio +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +# from adafruit_rfm import rfm9x + +# rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +from adafruit_rfm import rfm9xfsk + +rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 + +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + +# uncommnet to Disable the RadioHead Header +# rfm.radiohead = False + +# in FSK/OOK modes rfo RFM69 or RFM9X - addresss filtering may be enabled +# rfm.enable_address_filter=True +# rfm.fsk_node_address=0x2 +# rfm.fsk_broadcast_address=0xff + +# set the time interval (seconds) for sending packets +transmit_interval = 5 + +# Note that the radio is configured in LoRa mode so you can't control sync +# word, encryption, frequency deviation, or other settings! + +# You can however adjust the transmit power (in dB). The default is 13 dB but +# high power radios like the RFM95 can go up to 23 dB: +rfm.tx_power = 23 + + +# initialize counter +counter = 0 +# send a broadcast mesage +rfm.send(bytes(f"message number {counter}", "UTF-8")) + +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +send_reading = False +time_now = time.monotonic() +while True: + # Look for a new packet - wait up to 2 seconds: + packet = rfm.receive(timeout=2.0) + # If no packet was received during the timeout then None is returned. + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw bytes): {packet}") + print("Hex data: ", [hex(x) for x in packet]) + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + # clear flag to send data + send_reading = False + counter = counter + 1 + rfm.send(bytes(f"message number {counter}", "UTF-8")) From b4ab57e44fb1472ea1dbe14e8deac45af34ce2a0 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Thu, 1 Aug 2024 07:12:00 -0400 Subject: [PATCH 6/9] more example revisions --- README.rst | 4 - examples/rfm69ook_transmit.py | 79 --------- examples/rfm9x_rh_asyncio_gps_node2.py | 161 ----------------- examples/rfm9x_rh_asyncio_neopix_node2.py | 162 ------------------ examples/rfm9x_rh_header.py | 40 ----- examples/rfm9x_rh_transmit.py | 66 ------- examples/rfm9xfsk_node1.py | 66 ------- examples/rfm9xfsk_node2.py | 66 ------- examples/rfm9xfsk_transmit.py | 80 --------- examples/rfm9xook_transmit.py | 83 --------- examples/{rfm69_node1.py => rfm_fsk_node1.py} | 43 +++-- .../{rfm69_transmit.py => rfm_fsk_node2.py} | 67 ++++---- 12 files changed, 68 insertions(+), 849 deletions(-) delete mode 100644 examples/rfm69ook_transmit.py delete mode 100644 examples/rfm9x_rh_asyncio_gps_node2.py delete mode 100644 examples/rfm9x_rh_asyncio_neopix_node2.py delete mode 100644 examples/rfm9x_rh_header.py delete mode 100644 examples/rfm9x_rh_transmit.py delete mode 100644 examples/rfm9xfsk_node1.py delete mode 100644 examples/rfm9xfsk_node2.py delete mode 100644 examples/rfm9xfsk_transmit.py delete mode 100644 examples/rfm9xook_transmit.py rename examples/{rfm69_node1.py => rfm_fsk_node1.py} (63%) rename examples/{rfm69_transmit.py => rfm_fsk_node2.py} (57%) diff --git a/README.rst b/README.rst index bb1eeca..4ed4e8e 100644 --- a/README.rst +++ b/README.rst @@ -40,10 +40,6 @@ or individual libraries can be installed using Installing from PyPI ===================== -.. note:: This library is not available on PyPI yet. Install documentation is included - as a standard element. Stay tuned for PyPI availability! - -.. todo:: Remove the above note if PyPI version is/will be available at time of release. On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from PyPI `_. diff --git a/examples/rfm69ook_transmit.py b/examples/rfm69ook_transmit.py deleted file mode 100644 index 566a8d7..0000000 --- a/examples/rfm69ook_transmit.py +++ /dev/null @@ -1,79 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically -# Author: Jerry Needell -# -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm69 - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - - -# Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) -# set the time interval (seconds) for sending packets -node = 1 -destination = 2 -rfm69.radiohead = False -print(rfm69.modulation_type) -rfm69.modulation_type = 1 -print(rfm69.modulation_type) -# Optionally set an encryption key (16 byte AES key). MUST match both -# on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = None -# rfm69.encryption_key = ( -# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" -# ) - -# set the time interval (seconds) for sending packets -transmit_interval = 5 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM69 can go up to 20 dB: -# rfm69.tx_power = 20 - - -# initialize counter -counter = 0 -# send a broadcast mesage -rfm69.send(bytes(f"message number {counter}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -send_reading = False -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm69.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - # clear flag to send data - send_reading = False - counter = counter + 1 - rfm69.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9x_rh_asyncio_gps_node2.py b/examples/rfm9x_rh_asyncio_gps_node2.py deleted file mode 100644 index c782c98..0000000 --- a/examples/rfm9x_rh_asyncio_gps_node2.py +++ /dev/null @@ -1,161 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example using Interrupts to send a message and then wait indefinitely for messages -# to be received. Interrupts are used only for receive. sending is done with polling. -# This example is for systems that support interrupts like the Raspberry Pi with "blinka" -# CircuitPython does not support interrupts so it will not work on Circutpython boards -# Author: Tony DiCola, Jerry Needell -import asyncio -import time - -import adafruit_gps -import board -import busio -import digitalio - -from adafruit_rfm import rfm9x - -uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=10) - -# Create a GPS module instance. -gps = adafruit_gps.GPS(uart, debug=False) # Use UART/pyserial - -# Turn on the basic GGA and RMC info (what you typically want) -gps.send_command(b"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0") - -# Set update rate to once a second (1hz) which is what you typically want. -gps.send_command(b"PMTK220,1000") - - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.D10) -RESET = digitalio.DigitalInOut(board.D11) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set node addresses -rfm9x.node = 2 -rfm9x.destination = 100 -# send startup message from my_node -# rfm9x.send_with_ack(bytes("startup message from node {}".format(rfm9x.node), "UTF-8")) -rfm9x.listen() -# rfm9x.xmit_timeout = 5. -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer - - -# pylint: disable=too-few-public-methods -class Packet: - """Simple class to hold an value. Use .value to to read or write.""" - - def __init__(self): - self.received = False - self.gps_time = "Unknown" - self.gps_latitude = 0.0 - self.gps_longitude = 0.0 - - -async def read_gps(packet_status, wait): - # Main loop runs forever printing the location, etc. every second. - last_print = time.monotonic() - while True: - # Make sure to call gps.update() every loop iteration and at least twice - # as fast as data comes from the GPS unit (usually every second). - # This returns a bool that's true if it parsed new data (you can ignore it - # though if you don't care and instead look at the has_fix property). - gps.update() - # Every second print out current location details if there's a fix. - current = time.monotonic() - if current - last_print >= 1.0: - last_print = current - if not gps.has_fix: - # Try again if we don't have a fix yet. - print("Waiting for fix...") - packet_status.gps_time = "Unknown" - packet_status.gps_latitude = 0.0 - packet_status.gps_longitude = 0.0 - continue - # We have a fix! (gps.has_fix is true) - # Print out details about the fix like location, date, etc. - print("=" * 40) # Print a separator line. - packet_status.gps_time = f"Fix timestamp: {gps.timestamp_utc.tm_mon}/{gps.timestamp_utc.tm_mday}/{gps.timestamp_utc.tm_year} {gps.timestamp_utc.tm_hour:02}:{gps.timestamp_utc.tm_min:02}:{gps.timestamp_utc.tm_sec:02}" # noqa: E501 - print(packet_status.gps_time) - print(f"Latitude: {gps.latitude:.6f} degrees") - print(f"Longitude: {gps.longitude:.6f} degrees") - packet_status.gps_latitude = gps.latitude - packet_status.gps_longitude = gps.longitude - if gps.satellites is not None: - print(f"# satellites: {gps.satellites}") - if gps.altitude_m is not None: - print(f"Altitude: {gps.altitude_m} meters") - await asyncio.sleep(wait) - - -async def wait_for_packets(packet_status, lock): - while True: - if rfm9x.payload_ready(): - if lock.locked(): - print("locked waiting for receive") - async with lock: - packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) - if packet is not None: - packet_status.received = True - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") - await asyncio.sleep(0.001) - - -async def send_packets(packet_status, lock): - # initialize counter - counter = 0 - ack_failed_counter = 0 - counter = 0 - transmit_interval = 5 - time_now = time.monotonic() - while True: - # If no packet was received during the timeout then None is returned. - if packet_status.received: - packet_status.received = False - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - counter += 1 - # send a mesage to destination_node from my_node - if lock.locked(): - print("locked waiting for send") - async with lock: - if not await rfm9x.asyncio_send_with_ack( - bytes( - f"message from node {rfm9x.node} {counter} {ack_failed_counter} {packet_status.gps_time} {packet_status.gps_latitude:.6f} {packet_status.gps_longitude:.6f}", # noqa: E501 - "UTF-8", - ) - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) - await asyncio.sleep(0.1) - - -async def main(): - packet_status = Packet() - lock = asyncio.Lock() - task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) - task2 = asyncio.create_task(send_packets(packet_status, lock)) - task3 = asyncio.create_task(read_gps(packet_status, 0.25)) - - await asyncio.gather(task1, task2, task3) # Don't forget "await"! - - -asyncio.run(main()) diff --git a/examples/rfm9x_rh_asyncio_neopix_node2.py b/examples/rfm9x_rh_asyncio_neopix_node2.py deleted file mode 100644 index 1bda71c..0000000 --- a/examples/rfm9x_rh_asyncio_neopix_node2.py +++ /dev/null @@ -1,162 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example using Interrupts to send a message and then wait indefinitely for messages -# to be received. Interrupts are used only for receive. sending is done with polling. -# This example is for systems that support interrupts like the Raspberry Pi with "blinka" -# CircuitPython does not support interrupts so it will not work on Circutpython boards -# Author: Tony DiCola, Jerry Needell -import asyncio -import time - -import board -import busio -import digitalio -import neopixel - -from adafruit_rfm import rfm9x - -# On CircuitPlayground Express, and boards with built in status NeoPixel -> board.NEOPIXEL -# Otherwise choose an open pin connected to the Data In of the NeoPixel strip, i.e. board.D1 -pixel_pin = board.D5 - -# On a Raspberry pi, use this instead, not all pins are supported -# pixel_pin = board.D18 - -# The number of NeoPixels -num_pixels = 32 - -# The order of the pixel colors - RGB or GRB. Some NeoPixels have red and green reversed! -# For RGBW NeoPixels, simply change the ORDER to RGBW or GRBW. -ORDER = neopixel.GRB - -pixels = neopixel.NeoPixel( - pixel_pin, num_pixels, brightness=0.2, auto_write=False, pixel_order=ORDER -) - - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.D10) -RESET = digitalio.DigitalInOut(board.D11) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) - -# set node addresses -rfm9x.node = 2 -rfm9x.destination = 100 -# send startup message from my_node -# rfm9x.send_with_ack(bytes("startup message from node {}".format(rfm9x.node), "UTF-8")) -rfm9x.listen() -# rfm9x.xmit_timeout = 5. -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer - - -# pylint: disable=too-few-public-methods -class Packet: - """Simple class to hold an value. Use .value to to read or write.""" - - def __init__(self): - self.received = False - - -def wheel(pos): - # Input a value 0 to 255 to get a color value. - # The colours are a transition r - g - b - back to r. - if pos < 0 or pos > 255: - r = g = b = 0 - elif pos < 85: - r = int(pos * 3) - g = int(255 - pos * 3) - b = 0 - elif pos < 170: - pos -= 85 - r = int(255 - pos * 3) - g = 0 - b = int(pos * 3) - else: - pos -= 170 - r = 0 - g = int(pos * 3) - b = int(255 - pos * 3) - return (r, g, b) if ORDER in (neopixel.RGB, neopixel.GRB) else (r, g, b, 0) - - -async def rainbow_cycle(wait, lock): - while True: - if not lock.locked(): - for j in range(255): - for i in range(num_pixels): - pixel_index = (i * 256 // num_pixels) + j - pixels[i] = wheel(pixel_index & 255) - pixels.show() - await asyncio.sleep(wait) - - -async def wait_for_packets(packet_status, lock): - while True: - if rfm9x.payload_ready(): - if lock.locked(): - print("locked waiting for receive") - async with lock: - packet = await rfm9x.asyncio_receive_with_ack(with_header=True, timeout=None) - if packet is not None: - packet_status.received = True - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - print([hex(x) for x in packet]) - print(f"RSSI: {rfm9x.last_rssi}") - await asyncio.sleep(0.001) - - -async def send_packets(packet_status, lock): - # initialize counter - counter = 0 - ack_failed_counter = 0 - counter = 0 - transmit_interval = 5 - time_now = time.monotonic() - while True: - # If no packet was received during the timeout then None is returned. - if packet_status.received: - packet_status.received = False - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - counter += 1 - # send a mesage to destination_node from my_node - if lock.locked(): - print("locked waiting for send") - async with lock: - if not await rfm9x.asyncio_send_with_ack( - bytes( - f"message from node {rfm9x.node} {counter} {ack_failed_counter}", - "UTF-8", - ) - ): - ack_failed_counter += 1 - print(" No Ack: ", counter, ack_failed_counter) - await asyncio.sleep(0.1) - - -async def main(): - packet_status = Packet() - lock = asyncio.Lock() - task1 = asyncio.create_task(wait_for_packets(packet_status, lock)) - task2 = asyncio.create_task(send_packets(packet_status, lock)) - task3 = asyncio.create_task(rainbow_cycle(0.001, lock)) # rainbow cycle with 1ms delay per step - - await asyncio.gather(task1, task2, task3) # Don't forget "await"! - - -asyncio.run(main()) diff --git a/examples/rfm9x_rh_header.py b/examples/rfm9x_rh_header.py deleted file mode 100644 index b62feb5..0000000 --- a/examples/rfm9x_rh_header.py +++ /dev/null @@ -1,40 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to display raw packets including header -# Author: Jerry Needell -# -import board -import busio -import digitalio - -from adafruit_rfm import rfm9x - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -while True: - # Look for a new packet: only accept if addresses to my_node - packet = rfm9x.receive(with_header=True) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print("Received (raw header):", [hex(x) for x in packet[0:]]) - print(f"Received (raw payload): {packet[4:]}") - print(f"RSSI: {rfm9x.last_rssi}") - # send reading after any packet received diff --git a/examples/rfm9x_rh_transmit.py b/examples/rfm9x_rh_transmit.py deleted file mode 100644 index 9b7fa9f..0000000 --- a/examples/rfm9x_rh_transmit.py +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically -# Author: Jerry Needell -# -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9x - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9x = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) - - -# set the time interval (seconds) for sending packets -transmit_interval = 5 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm9x.tx_power = 23 - - -# initialize counter -counter = 0 -# send a broadcast mesage -rfm9x.send(bytes(f"message number {counter}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -send_reading = False -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm9x.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - # clear flag to send data - send_reading = False - counter = counter + 1 - rfm9x.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xfsk_node1.py b/examples/rfm9xfsk_node1.py deleted file mode 100644 index 868b768..0000000 --- a/examples/rfm9xfsk_node1.py +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically -# Author: Jerry Needell -# -import random -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) -node = 1 -destination = 2 - -rfm9xfsk.radiohead = False -rfm9xfsk.enable_address_filter = True -rfm9xfsk.fsk_node_address = node -rfm9xfsk.fsk_broadcast_address = 0xFF -# set the time interval (seconds) for sending packets -transmit_interval = 2 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -# rfm9xfsk.tx_power = 23 - - -# initialize counter -counter = 0 -# send a startup mesage -rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm9xfsk.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}", rfm9xfsk.last_rssi) - # clear flag to send data - time.sleep(random.random()) - counter = counter + 1 - rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) diff --git a/examples/rfm9xfsk_node2.py b/examples/rfm9xfsk_node2.py deleted file mode 100644 index d32d85c..0000000 --- a/examples/rfm9xfsk_node2.py +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically -# Author: Jerry Needell -# -import random -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) -node = 2 -destination = 1 - -rfm9xfsk.radiohead = False -rfm9xfsk.enable_address_filter = True -rfm9xfsk.fsk_node_address = node -rfm9xfsk.fsk_broadcast_address = 0xFF -# set the time interval (seconds) for sending packets -transmit_interval = 2 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -# rfm9xfsk.tx_power = 23 - - -# initialize counter -counter = 0 -# send a startup mesage -rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm9xfsk.receive(timeout=0.5) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}", rfm9xfsk.last_rssi) - if time.monotonic() - time_now > transmit_interval + random.random(): - counter = counter + 1 - rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) - time_now = time.monotonic() diff --git a/examples/rfm9xfsk_transmit.py b/examples/rfm9xfsk_transmit.py deleted file mode 100644 index 4fa0220..0000000 --- a/examples/rfm9xfsk_transmit.py +++ /dev/null @@ -1,80 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically -# Author: Jerry Needell -# -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - -print( - rfm9xfsk.fsk_node_address, - rfm9xfsk.fsk_broadcast_address, - rfm9xfsk.enable_address_filter, -) - -rfm9xfsk.radiohead = False -# rfm9xfsk.enable_address_filter=True -# rfm9xfsk.fsk_node_address=0x2 -# rfm9xfsk.fsk_broadcast_address=0xff -print( - rfm9xfsk.fsk_node_address, - rfm9xfsk.fsk_broadcast_address, - rfm9xfsk.enable_address_filter, -) -# set the time interval (seconds) for sending packets -transmit_interval = 5 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm9xfsk.tx_power = 23 - - -# initialize counter -counter = 0 -# send a broadcast mesage -rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -send_reading = False -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm9xfsk.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - # clear flag to send data - send_reading = False - counter = counter + 1 - rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm9xook_transmit.py b/examples/rfm9xook_transmit.py deleted file mode 100644 index e904aad..0000000 --- a/examples/rfm9xook_transmit.py +++ /dev/null @@ -1,83 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# SPDX-License-Identifier: MIT - -# Example to send a packet periodically -# Author: Jerry Needell -# -import time - -import board -import busio -import digitalio - -from adafruit_rfm import rfm9xfsk - -# Define radio parameters. -RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your -# module! Can be a value like 915.0, 433.0, etc. - -# Define pins connected to the chip, use these if wiring up the breakout according to the guide: -CS = digitalio.DigitalInOut(board.CE1) -RESET = digitalio.DigitalInOut(board.D25) - -# Initialize SPI bus. -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialze RFM radio -rfm9xfsk = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) - -print( - rfm9xfsk.fsk_node_address, - rfm9xfsk.fsk_broadcast_address, - rfm9xfsk.enable_address_filter, -) - -rfm9xfsk.radiohead = False -print(rfm9xfsk.modulation_type) -rfm9xfsk.modulation_type = 1 -print(rfm9xfsk.modulation_type) -# rfm9xfsk.enable_address_filter=True -# rfm9xfsk.fsk_node_address=0x2 -# rfm9xfsk.fsk_broadcast_address=0xff -print( - rfm9xfsk.fsk_node_address, - rfm9xfsk.fsk_broadcast_address, - rfm9xfsk.enable_address_filter, -) -# set the time interval (seconds) for sending packets -transmit_interval = 5 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm9xfsk.tx_power = 23 - - -# initialize counter -counter = 0 -# send a broadcast mesage -rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) - -# Wait to receive packets. -print("Waiting for packets...") -# initialize flag and timer -send_reading = False -time_now = time.monotonic() -while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm9xfsk.receive(timeout=2.0) - # If no packet was received during the timeout then None is returned. - if packet is not None: - # Received a packet! - # Print out the raw bytes of the packet: - print(f"Received (raw bytes): {packet}") - # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: - # reset timeer - time_now = time.monotonic() - # clear flag to send data - send_reading = False - counter = counter + 1 - rfm9xfsk.send(bytes(f"message number {counter}", "UTF-8")) diff --git a/examples/rfm69_node1.py b/examples/rfm_fsk_node1.py similarity index 63% rename from examples/rfm69_node1.py rename to examples/rfm_fsk_node1.py index 7e540e4..6152023 100644 --- a/examples/rfm69_node1.py +++ b/examples/rfm_fsk_node1.py @@ -9,8 +9,6 @@ import busio import digitalio -from adafruit_rfm import rfm69 - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -23,26 +21,47 @@ spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +# from adafruit_rfm import rfm9x + +# rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +from adafruit_rfm import rfm9xfsk + +rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 + +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Disable the RadioHead Header +rfm.radiohead = False # set the time interval (seconds) for sending packets transmit_interval = 5 node = 1 destination = 2 -rfm69.radiohead = False -rfm69.enable_address_filter = True -rfm69.fsk_node_address = node -rfm69.fsk_broadcast_address = 0xFF +rfm.enable_address_filter = True +rfm.fsk_node_address = node +rfm.fsk_broadcast_address = 0xFF +# For RFM69 only: # Optionally set an encryption key (16 byte AES key). MUST match both # on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = None -# rfm69.encryption_key = ( +rfm.encryption_key = None +# rfm.encryption_key = ( # b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" # ) # initialize counter counter = 0 # send a broadcast mesage -rfm69.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) +rfm.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) # Wait to receive packets. print("Waiting for packets...") @@ -50,7 +69,7 @@ time_now = time.monotonic() while True: # Look for a new packet - wait up to 5 seconds: - packet = rfm69.receive(timeout=2.0) + packet = rfm.receive(timeout=2.0) # If no packet was received during the timeout then None is returned. if packet is not None: # Received a packet! @@ -61,4 +80,4 @@ # reset timeer time_now = time.monotonic() counter = counter + 1 - rfm69.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) + rfm.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) diff --git a/examples/rfm69_transmit.py b/examples/rfm_fsk_node2.py similarity index 57% rename from examples/rfm69_transmit.py rename to examples/rfm_fsk_node2.py index dc94c0d..4338136 100644 --- a/examples/rfm69_transmit.py +++ b/examples/rfm_fsk_node2.py @@ -1,17 +1,14 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-FileCopyrightText: 2020 Jerry Needell for Adafruit Industries # SPDX-License-Identifier: MIT # Example to send a packet periodically -# Author: Jerry Needell -# + import time import board import busio import digitalio -from adafruit_rfm import rfm69 - # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. @@ -23,44 +20,56 @@ # Initialize SPI bus. spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - # Initialze RFM radio -rfm69 = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +# from adafruit_rfm import rfm9x + +# rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK + +from adafruit_rfm import rfm9xfsk + +rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Use rfm69 for two RFM69 radios using FSK + +# from adafruit_rfm import rfm69 + +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Disable the RadioHead Header +rfm.radiohead = False # set the time interval (seconds) for sending packets -node = 1 -destination = 2 -rfm69.radiohead = False +transmit_interval = 5 +node = 2 +destination = 1 +rfm.enable_address_filter = True +rfm.fsk_node_address = node +rfm.fsk_broadcast_address = 0xFF +# For RFM69 only: # Optionally set an encryption key (16 byte AES key). MUST match both # on the transmitter and receiver (or be set to None to disable/the default). -rfm69.encryption_key = None -# rfm69.encryption_key = ( +rfm.encryption_key = None +# rfm.encryption_key = ( # b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" # ) -# set the time interval (seconds) for sending packets -transmit_interval = 5 - -# Note that the radio is configured in LoRa mode so you can't control sync -# word, encryption, frequency deviation, or other settings! - -# You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM69 can go up to 20 dB: -# rfm69.tx_power = 20 - - # initialize counter counter = 0 # send a broadcast mesage -rfm69.send(bytes(f"message number {counter}", "UTF-8")) +rfm.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) # Wait to receive packets. print("Waiting for packets...") # initialize flag and timer -send_reading = False time_now = time.monotonic() while True: - # Look for a new packet - wait up to 2 seconds: - packet = rfm69.receive(timeout=2.0) + # Look for a new packet - wait up to 5 seconds: + packet = rfm.receive(timeout=2.0) # If no packet was received during the timeout then None is returned. if packet is not None: # Received a packet! @@ -70,7 +79,5 @@ if time.monotonic() - time_now > transmit_interval: # reset timeer time_now = time.monotonic() - # clear flag to send data - send_reading = False counter = counter + 1 - rfm69.send(bytes(f"message number {counter}", "UTF-8")) + rfm.send(bytes(f"message number {counter}", "UTF-8"), destination=destination) From 13a393c50cd23f9dbb992604595f3796d82208db Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Thu, 1 Aug 2024 07:25:18 -0400 Subject: [PATCH 7/9] remove todos --- docs/index.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 95620a4..7e9b6d2 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,15 +24,9 @@ Table of Contents .. toctree:: :caption: Tutorials -.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave - the toctree above for use later. - .. toctree:: :caption: Related Products -.. todo:: Add any product links here. If there are none, then simply delete this todo and leave - the toctree above for use later. - .. toctree:: :caption: Other Links From 829d767d2408934ad5f0af0f07a993b6630ee49b Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Sun, 4 Aug 2024 09:27:23 -0400 Subject: [PATCH 8/9] add OOK and RFM69 encryption options to examples --- examples/rfm_fsk_node1.py | 10 ++++++++++ examples/rfm_fsk_node2.py | 10 ++++++++++ examples/rfm_raw.py | 12 +++++++++++- examples/rfm_rh_asyncio_listen.py | 9 +++++++++ examples/rfm_rh_asyncio_listen_ack.py | 10 ++++++++++ examples/rfm_rh_asyncio_node1.py | 9 +++++++++ examples/rfm_rh_base.py | 18 ++++++++++++++---- examples/rfm_rh_base_ack.py | 10 ++++++++++ examples/rfm_rh_node1.py | 10 ++++++++++ examples/rfm_rh_node1_ack.py | 18 ++++++++++++++---- examples/rfm_simpletest.py | 12 +++++++++++- examples/rfm_transmit.py | 10 ++++++++++ 12 files changed, 128 insertions(+), 10 deletions(-) diff --git a/examples/rfm_fsk_node1.py b/examples/rfm_fsk_node1.py index 6152023..3cf612f 100644 --- a/examples/rfm_fsk_node1.py +++ b/examples/rfm_fsk_node1.py @@ -41,6 +41,16 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 + # Disable the RadioHead Header rfm.radiohead = False # set the time interval (seconds) for sending packets diff --git a/examples/rfm_fsk_node2.py b/examples/rfm_fsk_node2.py index 4338136..95d3626 100644 --- a/examples/rfm_fsk_node2.py +++ b/examples/rfm_fsk_node2.py @@ -41,6 +41,16 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 + # Disable the RadioHead Header rfm.radiohead = False # set the time interval (seconds) for sending packets diff --git a/examples/rfm_raw.py b/examples/rfm_raw.py index defe180..ac5e24b 100644 --- a/examples/rfm_raw.py +++ b/examples/rfm_raw.py @@ -38,7 +38,17 @@ # from adafruit_rfm import rfm69 -# rfm = rfm9x.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 # Disable the RadioHead Header rfm.radiohead = False diff --git a/examples/rfm_rh_asyncio_listen.py b/examples/rfm_rh_asyncio_listen.py index 923bace..8293bcf 100644 --- a/examples/rfm_rh_asyncio_listen.py +++ b/examples/rfm_rh_asyncio_listen.py @@ -44,6 +44,15 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 # send startup message from my_node # rfm.send(bytes("startup message from node {}".format(rfm.node), "UTF-8")) diff --git a/examples/rfm_rh_asyncio_listen_ack.py b/examples/rfm_rh_asyncio_listen_ack.py index a7961ea..93f4eec 100644 --- a/examples/rfm_rh_asyncio_listen_ack.py +++ b/examples/rfm_rh_asyncio_listen_ack.py @@ -44,6 +44,16 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 + # send startup message from my_node # rfm.send(bytes("startup message from node {}".format(rfm.node), "UTF-8")) diff --git a/examples/rfm_rh_asyncio_node1.py b/examples/rfm_rh_asyncio_node1.py index e57448c..114ee69 100644 --- a/examples/rfm_rh_asyncio_node1.py +++ b/examples/rfm_rh_asyncio_node1.py @@ -40,6 +40,15 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 # set node addresses rfm.node = 1 diff --git a/examples/rfm_rh_base.py b/examples/rfm_rh_base.py index 9563892..a44b753 100644 --- a/examples/rfm_rh_base.py +++ b/examples/rfm_rh_base.py @@ -21,15 +21,15 @@ # Use rfm9x for two RFM9x radios using LoRa -# from adafruit_rfm import rfm9x +from adafruit_rfm import rfm9x -# rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) # Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK -from adafruit_rfm import rfm9xfsk +# from adafruit_rfm import rfm9xfsk -rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) +# rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) # Use rfm69 for two RFM69 radios using FSK @@ -37,6 +37,16 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 + # set node addresses rfm.node = 100 rfm.destination = 0xFF diff --git a/examples/rfm_rh_base_ack.py b/examples/rfm_rh_base_ack.py index 523d661..8d3f2c6 100644 --- a/examples/rfm_rh_base_ack.py +++ b/examples/rfm_rh_base_ack.py @@ -37,6 +37,16 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 + # set node addresses rfm.node = 100 rfm.destination = 0xFF diff --git a/examples/rfm_rh_node1.py b/examples/rfm_rh_node1.py index 4278e4b..e232034 100644 --- a/examples/rfm_rh_node1.py +++ b/examples/rfm_rh_node1.py @@ -42,6 +42,16 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 + # set the time interval (seconds) for sending packets transmit_interval = 2 diff --git a/examples/rfm_rh_node1_ack.py b/examples/rfm_rh_node1_ack.py index 9c31c90..baadfd3 100644 --- a/examples/rfm_rh_node1_ack.py +++ b/examples/rfm_rh_node1_ack.py @@ -26,15 +26,15 @@ # Use rfm9x for two RFM9x radios using LoRa -# from adafruit_rfm import rfm9x +from adafruit_rfm import rfm9x -# rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) # Use rfm9xfsk for two RFM9x radios or RFM9x to RFM69 using FSK -from adafruit_rfm import rfm9xfsk +# from adafruit_rfm import rfm9xfsk -rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) +# rfm = rfm9xfsk.RFM9xFSK(spi, CS, RESET, RADIO_FREQ_MHZ) # Use rfm69 for two RFM69 radios using FSK @@ -42,6 +42,16 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 + # set the time interval (seconds) for sending packets transmit_interval = 10 diff --git a/examples/rfm_simpletest.py b/examples/rfm_simpletest.py index 2e5107b..9ec4de1 100644 --- a/examples/rfm_simpletest.py +++ b/examples/rfm_simpletest.py @@ -38,7 +38,17 @@ # from adafruit_rfm import rfm69 -# rfm = rfm9x.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 # Send a packet. Note you can only send a packet containing up to 60 bytes for an RFM69 # and 252 bytes forn an RFM9x. diff --git a/examples/rfm_transmit.py b/examples/rfm_transmit.py index 7a15ae0..48e8b3a 100644 --- a/examples/rfm_transmit.py +++ b/examples/rfm_transmit.py @@ -42,6 +42,16 @@ # rfm = rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) +# For RFM69 only: Optionally set an encryption key (16 byte AES key). MUST match both +# on the transmitter and receiver (or be set to None to disable/the default). +# rfm.encryption_key = None +# rfm.encryption_key = ( +# b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +# ) + +# for OOK on RFM69 or RFM9xFSK +# rfm.modulation_type = 1 + # uncommnet to Disable the RadioHead Header # rfm.radiohead = False From 033ac446f33fa487be4f3edd6b0f8e103fb695d2 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Wed, 7 Aug 2024 08:21:33 -0400 Subject: [PATCH 9/9] fix docs generation, a few changes per review --- adafruit_rfm/rfm69.py | 7 ++++--- adafruit_rfm/rfm9x.py | 6 ++++-- adafruit_rfm/rfm9xfsk.py | 6 ++++-- adafruit_rfm/rfm_common.py | 16 ++++++++++++++-- docs/api.rst | 11 ++++++++++- 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/adafruit_rfm/rfm69.py b/adafruit_rfm/rfm69.py index e4c2108..4ff3c54 100644 --- a/adafruit_rfm/rfm69.py +++ b/adafruit_rfm/rfm69.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT """ -`adafruit_rfm69` +`adafruit_rfm.rfm69` ==================================================== CircuitPython RFM69 packet radio module. This supports sending and @@ -33,12 +33,12 @@ except ImportError: pass - try: from typing import Optional import busio import digitalio + from circuitpython_typing import ReadableBuffer except ImportError: pass @@ -567,6 +567,7 @@ def enable_crc(self, val: bool) -> None: else: self.crc_on = 0 + @property def crc_error(self) -> bool: """crc status""" return (self.read_u8(_RF69_REG_28_IRQ_FLAGS2) & 0x2) >> 1 @@ -629,7 +630,7 @@ def clear_interrupt(self) -> None: self.write_u8(_RF69_REG_27_IRQ_FLAGS1, 0xFF) self.write_u8(_RF69_REG_28_IRQ_FLAGS2, 0xFF) - def fill_fifo(self, payload: bytearray) -> None: + def fill_fifo(self, payload: ReadableBuffer) -> None: """Write the payload to the FIFO.""" complete_payload = bytearray(1) # prepend packet length to payload complete_payload[0] = len(payload) diff --git a/adafruit_rfm/rfm9x.py b/adafruit_rfm/rfm9x.py index 144c0f4..d89000a 100644 --- a/adafruit_rfm/rfm9x.py +++ b/adafruit_rfm/rfm9x.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT """ -`adafruit_rfm9x` +`adafruit_rfm.rfm9x` ==================================================== CircuitPython module for the RFM95/6/7/8 LoRa 433/915mhz radio modules. @@ -20,6 +20,7 @@ try: import busio import digitalio + from circuitpython_typing import ReadableBuffer try: from typing import Literal @@ -490,6 +491,7 @@ def enable_crc(self, val: bool) -> None: self.read_u8(_RF95_REG_1E_MODEM_CONFIG2) & 0xFB, ) + @property def crc_error(self) -> bool: """crc status""" return (self.read_u8(_RF95_REG_12_IRQ_FLAGS) & 0x20) >> 5 @@ -506,7 +508,7 @@ def clear_interrupt(self) -> None: """Clear Interrupt flags""" self.write_u8(_RF95_REG_12_IRQ_FLAGS, 0xFF) - def fill_fifo(self, payload: bytearray) -> None: + def fill_fifo(self, payload: ReadableBuffer) -> None: """len_data is not used but is here for compatibility with rfm69 Fill the FIFO with a packet to send""" self.write_u8(_RF95_REG_0D_FIFO_ADDR_PTR, 0x00) # FIFO starts at 0. diff --git a/adafruit_rfm/rfm9xfsk.py b/adafruit_rfm/rfm9xfsk.py index d456b86..4394cde 100644 --- a/adafruit_rfm/rfm9xfsk.py +++ b/adafruit_rfm/rfm9xfsk.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT """ -`adafruit_rfm9xFSK` +`adafruit_rfm.rfm9xfsk` ==================================================== CircuitPython module for the RFM95/6/7/8 FSK 433/915mhz radio modules. @@ -22,6 +22,7 @@ import busio import digitalio + from circuitpython_typing import ReadableBuffer try: from typing import Literal @@ -490,6 +491,7 @@ def enable_crc(self, val: bool) -> None: else: self.crc_on = 0 + @property def crc_error(self) -> bool: """crc status""" return (self.read_u8(_RF95_REG_3F_IRQ_FLAGS_2) & 0x2) >> 1 @@ -552,7 +554,7 @@ def clear_interrupt(self) -> None: self.write_u8(_RF95_REG_3E_IRQ_FLAGS_1, 0xFF) self.write_u8(_RF95_REG_3F_IRQ_FLAGS_2, 0xFF) - def fill_fifo(self, payload: bytearray) -> None: + def fill_fifo(self, payload: ReadableBuffer) -> None: """Write the payload to the FIFO.""" complete_payload = bytearray(1) # prepend packet length to payload complete_payload[0] = len(payload) diff --git a/adafruit_rfm/rfm_common.py b/adafruit_rfm/rfm_common.py index 04096db..ed5a55a 100644 --- a/adafruit_rfm/rfm_common.py +++ b/adafruit_rfm/rfm_common.py @@ -336,6 +336,9 @@ async def asyncio_send( # noqa: PLR0912 PLR0913 return not timed_out send = asyncio_to_blocking(asyncio_send) + """Non-asyncio wrapper to Send a string of data using the transmitter + using the same arguments and keywords as asyncio_send() + """ async def asyncio_send_with_ack(self, data: ReadableBuffer) -> bool: """Reliable Datagram mode: @@ -377,6 +380,9 @@ async def asyncio_send_with_ack(self, data: ReadableBuffer) -> bool: return got_ack send_with_ack = asyncio_to_blocking(asyncio_send_with_ack) + """Non-asyncio wrapper to Send a string of data using the transmitter + using the same arguments and keywords as asyncio_send_with_ack() + """ async def asyncio_receive( # noqa: PLR0912 self, @@ -420,7 +426,7 @@ async def asyncio_receive( # noqa: PLR0912 # Enter idle mode to stop receiving other packets. self.idle() if not timed_out: - if self.enable_crc and self.crc_error(): + if self.enable_crc and self.crc_error: self.crc_error_count += 1 else: packet = self.read_fifo() @@ -447,6 +453,9 @@ async def asyncio_receive( # noqa: PLR0912 return packet receive = asyncio_to_blocking(asyncio_receive) + """Non-asyncio wrapper to Receive a packet + using the same arguments and keywords as asyncio_receive() + """ async def asyncio_receive_with_ack( # noqa: PLR0912 self, @@ -490,7 +499,7 @@ async def asyncio_receive_with_ack( # noqa: PLR0912 # Enter idle mode to stop receiving other packets. self.idle() if not timed_out: - if self.enable_crc and self.crc_error(): + if self.enable_crc and self.crc_error: self.crc_error_count += 1 else: packet = self.read_fifo() @@ -543,3 +552,6 @@ async def asyncio_receive_with_ack( # noqa: PLR0912 return packet receive_with_ack = asyncio_to_blocking(asyncio_receive_with_ack) + """Non-asyncio wrapper to Receive a packet + using the same arguments and keywords as asyncio_receive_with_ack() + """ diff --git a/docs/api.rst b/docs/api.rst index 0d6d4f7..d82934e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -4,5 +4,14 @@ .. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py) .. use this format as the module name: "adafruit_foo.foo" -.. automodule:: adafruit_rfm +.. automodule:: adafruit_rfm.rfm_common + :members: + +.. automodule:: adafruit_rfm.rfm69 + :members: + +.. automodule:: adafruit_rfm.rfm9x + :members: + +.. automodule:: adafruit_rfm.rfm9xfsk :members: