Skip to content

Commit

Permalink
Merge pull request #18 from pimoroni/experimental/wireless
Browse files Browse the repository at this point in the history
Add support for RM2 Wireless Module
  • Loading branch information
ZodiusInfuser authored Dec 17, 2024
2 parents 869b4c0 + 90683fe commit 799a1db
Show file tree
Hide file tree
Showing 24 changed files with 710 additions and 10 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/micropython.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,17 @@ jobs:
- name: Yukon
shortname: yukon
board: PIMORONI_YUKON
- name: Yukon W
shortname: yukon+wireless
board: PIMORONI_YUKON_W

env:
RELEASE_FILE: pimoroni-${{matrix.shortname}}-${{github.event.release.tag_name || github.sha}}-micropython
FIRMWARE_DIR: "$GITHUB_WORKSPACE/yukon/firmware"
BOARD_DIR: "$GITHUB_WORKSPACE/yukon/firmware/${{matrix.board}}"
FILESYSTEM_DIR: "$GITHUB_WORKSPACE/yukon/lib"
FILESYSTEM_SUFFIX: "with-filesystem"
BOARD: "PIMORONI_YUKON"
BOARD: ${{matrix.board}}

steps:
- name: Compiler Cache
Expand Down Expand Up @@ -135,6 +138,12 @@ jobs:
working-directory: micropython
run: git apply "${{env.FIRMWARE_DIR}}/yukon_expander.patch"

- name: "HACK: Yukon Wireless Patch"
if: matrix.shortname == 'yukon_w'
shell: bash
working-directory: micropython/lib/pico-sdk
run: git apply "${{env.FIRMWARE_DIR}}/yukon_wireless.patch"

- name: Install Arm GNU Toolchain (arm-none-eabi-gcc)
uses: carlosperate/arm-none-eabi-gcc-action@v1
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ jobs:
- name: Lint Yukon Python Libraries
shell: bash
run: |
python3 -m flake8 --show-source --ignore E501,E201,E241,E222,E116,E266 lib/
python3 -m flake8 --show-source --ignore E501,E201,E241,E222,E116,E266,F401 lib/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ To take Yukon further, the full API for the library is described in the followin
* [Docs: LED Strip Module](/docs/modules/led_strip.md)
* [Docs: Quad Servo Direct Module](/docs/modules/quad_servo_direct.md)
* [Docs: Quad Servo Regulated Module](/docs/modules/quad_servo_reg.md)
* [Docs: RM2 Wireless Module](/docs/modules/rm2_wireless.md)
* [Docs: Serial Bus Servo Module](/docs/modules/serial_servo.md)
* [Docs: Yukon Module](/docs/modules/yukon_module.md)
* [Docs: Custom Module](/docs/modules/custom_module.md)
Expand Down
6 changes: 4 additions & 2 deletions docs/module_detection.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,11 @@ Here is the current address list for Yukon modules produced by Pimoroni.
<tr style="border-top: 2px solid; background-color:rgba(128, 128, 128, 0.25);">
<td>LOW</td><td>LOW</td><td>1</td><td>0</td><td>1</td><td>Reserved</td><td></td>
</tr>
<tr style="background-color:rgba(128, 128, 128, 0.25);">
<td>LOW</td><td>FLOAT</td><td>1</td><td>0</td><td>1</td><td>Reserved</td><td></td>
<!-- RM2 Wireless -->
<tr style="background-color:rgba(244, 204, 204, 0.25);">
<td>LOW</td><td>FLOAT</td><td>1</td><td>0</td><td>1</td><td>RM2 Wireless</td><td></td>
</tr>
<!-- Reserved -->
<tr style="background-color:rgba(128, 128, 128, 0.25);">
<td>LOW</td><td>HIGH</td><td>1</td><td>0</td><td>1</td><td>Reserved</td><td></td>
</tr>
Expand Down
76 changes: 76 additions & 0 deletions docs/modules/rm2_wireless.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# RM2 Wireless Module - Library Reference <!-- omit in toc -->

This is the library reference for the [RM2 Wireless Module for Yukon](https://pimoroni.com/yukon).

- [Getting Started](#getting-started)
- [Initialising the Module](#initialising-the-module)
- [Reference](#reference)
- [Constants](#constants)
- [Functions](#functions)


**:information-source: Wireless is a baked-in feature of MicroPython, so the normal import and initialisation steps for Yukon modules are not strictly required to get your project online. There are still some advantages to doing these though, so the steps are explained below.**

## Getting Started

To start using a RM2 Wireless Module, you first need to import the class from `pimoroni_yukon.modules`.

```python
from pimoroni_yukon.modules import RM2WirelessModule
```

Then create an instance of `RM2WirelessModule`. This will also confirm that you have a wireless capable build flashed to your Yukon.

```python
module = RM2WirelessModule()
```


## Initialising the Module

As with all Yukon modules, `RM2WirelessModule` must be initialised before it can be used. This is achieved by first registering the module with the `Yukon` class, with the slot it is attached to.

```python
from pimoroni_yukon import SLOT5 as SLOT # Only SLOT5 supports the RM2 Wireless Module at present

# Import and set up Yukon and RM2WirelessModule instances

yukon.register_with_slot(module, SLOT)
```

Then `Yukon` can verify and initialise its modules.

```python
yukon.verify_and_initialise()
```

This checks each slot on the board to see if the modules expected by your program are physically attached to the board. Only if they match will the `RM2WirelessModule` be initialised.

The RM2 Wireless Module is now ready to use. It can be interacted with using Micropython's `network` libraries, see Raspberry Pi's [Pico W tutorial](https://projects.raspberrypi.org/en/projects/get-started-pico-w/2).

From here you can optionally provide power to all your other modules by calling.
```python
yukon.enable_main_output()
```


## Reference

### Constants

```python
NAME = "RM2 Wireless"
```


### Functions

```python
# Address Checking
@staticmethod
is_module(adc1_level: int, adc2_level: int, slow1: bool, slow2: bool, slow3: bool) -> bool

# Initialisation
RM2WirelessModule()
initialise(slot: SLOT, adc1_func: Callable, adc2_func: Callable) -> None
```
33 changes: 33 additions & 0 deletions examples/modules/rm2_wireless/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# RM2 Wireless Module - Micropython Examples <!-- omit in toc -->

<img src="https://shop.pimoroni.com/cdn/shop/files/wireless-1_1500x1500_crop_center.jpg" width="500">

These are micropython examples for the [RM2 Wireless Module for Yukon](https://shop.pimoroni.com/products/rm2-wireless-module-for-yukon).

- [Examples](#examples)
- [Detect Module](#detect-module)
- [WiFi Scan](#wifi-scan)
- [Cheerlights](#cheerlights)


## Examples

### Detect Module
[detect_module.py](detect_module.py)

A boilerplate example showing how to detect if the RM2 Wireless Module is attached to Yukon prior to performing any wireless operations.


### WiFi Scan
[wifi_scan.py](wifi_scan.py)

Periodically scan for available WiFi networks using a RM2 Wireless Module connected to Slot 5, and print out their details.


### Cheerlights

[cheerlights.py](cheerlights.py)

Obtain the current CheerLights colour from the internet and show it on an LED Strip connected to Yukon. For more information about CheerLights, visit: https://cheerlights.com/

This example requires a secrets.py file to be on your board's file system with the credentials of your WiFi network.
105 changes: 105 additions & 0 deletions examples/modules/rm2_wireless/cheerlights.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import network
import requests
from pimoroni_yukon import Yukon
from pimoroni_yukon import SLOT2 as STRIP_SLOT
from pimoroni_yukon import SLOT5 as RM2_SLOT
from pimoroni_yukon.modules import LEDStripModule, RM2WirelessModule


"""
Obtain the current CheerLights colour from the internet and show it on an LED Strip connected to Yukon.
For more information about CheerLights, visit: https://cheerlights.com/
This example requires a secrets.py file to be on your board's file system with the credentials of your WiFi network.
Hold "Boot" to exit the program (can take up to 5 seconds).
"""

try:
from secrets import WIFI_SSID, WIFI_PASSWORD
if len(WIFI_SSID) == 0:
raise ValueError("no WiFi network set. Open the 'secrets.py' file on your device to add your WiFi credentials")
except ImportError:
raise ImportError("no module named 'secrets'. Create a 'secrets.py' file on your device with your WiFi credentials")


# Constants
COLOUR_NAMES = ("R", "G", "B")
CONNECTION_INTERVAL = 1.0 # The time to sleep between each connection check
REQUEST_INTERVAL = 5.0 # The time to sleep between each internet request
STRIP_TYPE = LEDStripModule.NEOPIXEL # Change to LEDStripModule.DOTSTAR for APA102 style strips
# Two Neopixel strips can be driven too, by using LEDStripModule.DUAL_NEOPIXEL
STRIP_PIO = 0 # The PIO system to use (0 or 1) to drive the strip(s)
STRIP_SM = 0 # The State Machines (SM) to use to drive the strip(s)
LEDS_PER_STRIP = 60 # How many LEDs are on the strip. If using DUAL_NEOPIXEL this can be a single value or a list or tuple
BRIGHTNESS = 1.0 # The max brightness of the LEDs (only supported by APA102s)

# Variables
yukon = Yukon() # Create a new Yukon object
leds = LEDStripModule(STRIP_TYPE, # Create a LEDStripModule object, with the details of the attached strip(s)
STRIP_PIO,
STRIP_SM,
LEDS_PER_STRIP,
BRIGHTNESS)
wireless = RM2WirelessModule() # Create a RM2WirelessModule object


# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
try:
yukon.register_with_slot(leds, STRIP_SLOT) # Register the LEDStripModule object with the slot
yukon.register_with_slot(wireless, RM2_SLOT) # Register the RM2WirelessModule object with the slot
yukon.verify_and_initialise() # Verify that the modules are attached to Yukon, and initialise them

wlan = network.WLAN(network.STA_IF) # Create a new network object for interacting with WiFi
wlan.active(True) # Turn on WLAN communications

# Connect to WLAN
print(f"Connecting to network '{WIFI_SSID}'")
wlan.connect(WIFI_SSID, WIFI_PASSWORD)

# Wait until the connection is established
while not wlan.isconnected():
print('Waiting for connection...')
yukon.monitored_sleep(CONNECTION_INTERVAL)

# Print out our IP address
print(f'Connected on {wlan.ifconfig()[0]}')

# Turn on power to the module slots and the LED strip
yukon.enable_main_output()
leds.enable()

# Loop until the BOOT/USER button is pressed
while not yukon.is_boot_pressed():

# Get the current CheerLights colour from the internet
req = requests.get("http://api.thingspeak.com/channels/1417/field/2/last.json")
json = req.json()
req.close()

# Use the second to get the colour components for the RGB output
colour = tuple(int(json['field2'][i:i + 2], 16) for i in (1, 3, 5))

# Print out the Cheerlights colour
for i in range(len(colour)):
print(f"{COLOUR_NAMES[i]} = {colour[i]}", end=", ")
print()

# Apply the colour to all the LEDs and send them to the strip
for led in range(leds.strip.num_leds()):
leds.strip.set_rgb(led, *colour)
leds.strip.update()

# Monitor sensors for a number of seconds, recording the min, max, and average for each
yukon.monitored_sleep(REQUEST_INTERVAL)


finally:
# Put the board back into a safe state, regardless of how the program may have ended
yukon.reset()

# Attempt to disconnect from WiFi
try:
wlan.disconnect()
except Exception:
pass
25 changes: 25 additions & 0 deletions examples/modules/rm2_wireless/detect_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from pimoroni_yukon import Yukon
from pimoroni_yukon import SLOT5 as SLOT
from pimoroni_yukon.modules import RM2WirelessModule

"""
A boilerplate example showing how to detect if the RM2 Wireless Module
is attached to Yukon prior to performing any wireless operations.
"""

# Variables
yukon = Yukon() # Create a new Yukon object, with a lower voltage limit set
module = RM2WirelessModule() # Create a RM2WirelessModule object

# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
try:
yukon.register_with_slot(module, SLOT) # Register the RM2WirelessModule object with the slot
yukon.verify_and_initialise() # Verify that a RM2WirelessModule is attached to Yukon, and initialise it

# Do wireless things here

finally:
# Put the board back into a safe state, regardless of how the program may have ended
yukon.reset()

# Disconnect from wireless here
49 changes: 49 additions & 0 deletions examples/modules/rm2_wireless/wifi_scan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import network
import binascii
from pimoroni_yukon import Yukon
from pimoroni_yukon import SLOT5 as SLOT
from pimoroni_yukon.modules import RM2WirelessModule

"""
Periodically scan for available WiFi networks using a RM2 Wireless Module connected to Slot 5,
and print out their details.
Hold "Boot" to exit the program (can take up to 5 seconds).
"""

# Constants
SCAN_INTERVAL = 5.0 # The time to sleep between each network scan

# Variables
yukon = Yukon() # Create a new Yukon object, with a lower voltage limit set
module = RM2WirelessModule() # Create a RM2WirelessModule object


# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
try:
yukon.register_with_slot(module, SLOT) # Register the RM2WirelessModule object with the slot
yukon.verify_and_initialise() # Verify that a RM2WirelessModule is attached to Yukon, and initialise it

wlan = network.WLAN(network.STA_IF) # Create a new network object for interacting with WiFi
wlan.active(True) # Turn on WLAN communications

while not yukon.is_boot_pressed():
# Scan for nearby networks and print them out
networks = wlan.scan() # Returns a list of tuples with 6 fields: ssid, bssid, channel, RSSI, security, hidden
for i, w in enumerate(networks):
print(i, w[0].decode(), binascii.hexlify(w[1]).decode(),
w[2], w[3], w[4], w[5])
print()

# Monitor sensors for a number of seconds, recording the min, max, and average for each
yukon.monitored_sleep(SCAN_INTERVAL)

finally:
# Put the board back into a safe state, regardless of how the program may have ended
yukon.reset()

# Attempt to disconnect from WiFi
try:
wlan.disconnect()
except Exception:
pass
2 changes: 1 addition & 1 deletion firmware/PIMORONI_YUKON/board.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"images": [
],
"mcu": "rp2040",
"product": "Pimoroni Yukon",
"product": "Pimoroni Yukon (no Wireless)",
"thumbnail": "",
"url": "https://shop.pimoroni.com/products/yukon",
"vendor": "Pimoroni"
Expand Down
4 changes: 2 additions & 2 deletions firmware/PIMORONI_YUKON/mpconfigboard.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This is a hack! Need to replace with upstream board definition.
#define MICROPY_HW_BOARD_NAME "Pimoroni Yukon"
#define MICROPY_HW_FLASH_STORAGE_BYTES (15 * 1024 * 1024)
#define MICROPY_HW_BOARD_NAME "Pimoroni Yukon (no Wireless)"
#define MICROPY_HW_FLASH_STORAGE_BYTES (14160 * 1024)

#define MICROPY_HW_USB_VID (0x2E8A)
#define MICROPY_HW_USB_PID (0x105B)
Expand Down
Loading

0 comments on commit 799a1db

Please sign in to comment.