Skip to content

Commit 799a1db

Browse files
Merge pull request #18 from pimoroni/experimental/wireless
Add support for RM2 Wireless Module
2 parents 869b4c0 + 90683fe commit 799a1db

File tree

24 files changed

+710
-10
lines changed

24 files changed

+710
-10
lines changed

.github/workflows/micropython.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,17 @@ jobs:
6464
- name: Yukon
6565
shortname: yukon
6666
board: PIMORONI_YUKON
67+
- name: Yukon W
68+
shortname: yukon+wireless
69+
board: PIMORONI_YUKON_W
6770

6871
env:
6972
RELEASE_FILE: pimoroni-${{matrix.shortname}}-${{github.event.release.tag_name || github.sha}}-micropython
7073
FIRMWARE_DIR: "$GITHUB_WORKSPACE/yukon/firmware"
7174
BOARD_DIR: "$GITHUB_WORKSPACE/yukon/firmware/${{matrix.board}}"
7275
FILESYSTEM_DIR: "$GITHUB_WORKSPACE/yukon/lib"
7376
FILESYSTEM_SUFFIX: "with-filesystem"
74-
BOARD: "PIMORONI_YUKON"
77+
BOARD: ${{matrix.board}}
7578

7679
steps:
7780
- name: Compiler Cache
@@ -135,6 +138,12 @@ jobs:
135138
working-directory: micropython
136139
run: git apply "${{env.FIRMWARE_DIR}}/yukon_expander.patch"
137140

141+
- name: "HACK: Yukon Wireless Patch"
142+
if: matrix.shortname == 'yukon_w'
143+
shell: bash
144+
working-directory: micropython/lib/pico-sdk
145+
run: git apply "${{env.FIRMWARE_DIR}}/yukon_wireless.patch"
146+
138147
- name: Install Arm GNU Toolchain (arm-none-eabi-gcc)
139148
uses: carlosperate/arm-none-eabi-gcc-action@v1
140149
with:

.github/workflows/python-linting.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ jobs:
2222
- name: Lint Yukon Python Libraries
2323
shell: bash
2424
run: |
25-
python3 -m flake8 --show-source --ignore E501,E201,E241,E222,E116,E266 lib/
25+
python3 -m flake8 --show-source --ignore E501,E201,E241,E222,E116,E266,F401 lib/

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ To take Yukon further, the full API for the library is described in the followin
106106
* [Docs: LED Strip Module](/docs/modules/led_strip.md)
107107
* [Docs: Quad Servo Direct Module](/docs/modules/quad_servo_direct.md)
108108
* [Docs: Quad Servo Regulated Module](/docs/modules/quad_servo_reg.md)
109+
* [Docs: RM2 Wireless Module](/docs/modules/rm2_wireless.md)
109110
* [Docs: Serial Bus Servo Module](/docs/modules/serial_servo.md)
110111
* [Docs: Yukon Module](/docs/modules/yukon_module.md)
111112
* [Docs: Custom Module](/docs/modules/custom_module.md)

docs/module_detection.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,11 @@ Here is the current address list for Yukon modules produced by Pimoroni.
253253
<tr style="border-top: 2px solid; background-color:rgba(128, 128, 128, 0.25);">
254254
<td>LOW</td><td>LOW</td><td>1</td><td>0</td><td>1</td><td>Reserved</td><td></td>
255255
</tr>
256-
<tr style="background-color:rgba(128, 128, 128, 0.25);">
257-
<td>LOW</td><td>FLOAT</td><td>1</td><td>0</td><td>1</td><td>Reserved</td><td></td>
256+
<!-- RM2 Wireless -->
257+
<tr style="background-color:rgba(244, 204, 204, 0.25);">
258+
<td>LOW</td><td>FLOAT</td><td>1</td><td>0</td><td>1</td><td>RM2 Wireless</td><td></td>
258259
</tr>
260+
<!-- Reserved -->
259261
<tr style="background-color:rgba(128, 128, 128, 0.25);">
260262
<td>LOW</td><td>HIGH</td><td>1</td><td>0</td><td>1</td><td>Reserved</td><td></td>
261263
</tr>

docs/modules/rm2_wireless.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# RM2 Wireless Module - Library Reference <!-- omit in toc -->
2+
3+
This is the library reference for the [RM2 Wireless Module for Yukon](https://pimoroni.com/yukon).
4+
5+
- [Getting Started](#getting-started)
6+
- [Initialising the Module](#initialising-the-module)
7+
- [Reference](#reference)
8+
- [Constants](#constants)
9+
- [Functions](#functions)
10+
11+
12+
**: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.**
13+
14+
## Getting Started
15+
16+
To start using a RM2 Wireless Module, you first need to import the class from `pimoroni_yukon.modules`.
17+
18+
```python
19+
from pimoroni_yukon.modules import RM2WirelessModule
20+
```
21+
22+
Then create an instance of `RM2WirelessModule`. This will also confirm that you have a wireless capable build flashed to your Yukon.
23+
24+
```python
25+
module = RM2WirelessModule()
26+
```
27+
28+
29+
## Initialising the Module
30+
31+
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.
32+
33+
```python
34+
from pimoroni_yukon import SLOT5 as SLOT # Only SLOT5 supports the RM2 Wireless Module at present
35+
36+
# Import and set up Yukon and RM2WirelessModule instances
37+
38+
yukon.register_with_slot(module, SLOT)
39+
```
40+
41+
Then `Yukon` can verify and initialise its modules.
42+
43+
```python
44+
yukon.verify_and_initialise()
45+
```
46+
47+
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.
48+
49+
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).
50+
51+
From here you can optionally provide power to all your other modules by calling.
52+
```python
53+
yukon.enable_main_output()
54+
```
55+
56+
57+
## Reference
58+
59+
### Constants
60+
61+
```python
62+
NAME = "RM2 Wireless"
63+
```
64+
65+
66+
### Functions
67+
68+
```python
69+
# Address Checking
70+
@staticmethod
71+
is_module(adc1_level: int, adc2_level: int, slow1: bool, slow2: bool, slow3: bool) -> bool
72+
73+
# Initialisation
74+
RM2WirelessModule()
75+
initialise(slot: SLOT, adc1_func: Callable, adc2_func: Callable) -> None
76+
```
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# RM2 Wireless Module - Micropython Examples <!-- omit in toc -->
2+
3+
<img src="https://shop.pimoroni.com/cdn/shop/files/wireless-1_1500x1500_crop_center.jpg" width="500">
4+
5+
These are micropython examples for the [RM2 Wireless Module for Yukon](https://shop.pimoroni.com/products/rm2-wireless-module-for-yukon).
6+
7+
- [Examples](#examples)
8+
- [Detect Module](#detect-module)
9+
- [WiFi Scan](#wifi-scan)
10+
- [Cheerlights](#cheerlights)
11+
12+
13+
## Examples
14+
15+
### Detect Module
16+
[detect_module.py](detect_module.py)
17+
18+
A boilerplate example showing how to detect if the RM2 Wireless Module is attached to Yukon prior to performing any wireless operations.
19+
20+
21+
### WiFi Scan
22+
[wifi_scan.py](wifi_scan.py)
23+
24+
Periodically scan for available WiFi networks using a RM2 Wireless Module connected to Slot 5, and print out their details.
25+
26+
27+
### Cheerlights
28+
29+
[cheerlights.py](cheerlights.py)
30+
31+
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/
32+
33+
This example requires a secrets.py file to be on your board's file system with the credentials of your WiFi network.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import network
2+
import requests
3+
from pimoroni_yukon import Yukon
4+
from pimoroni_yukon import SLOT2 as STRIP_SLOT
5+
from pimoroni_yukon import SLOT5 as RM2_SLOT
6+
from pimoroni_yukon.modules import LEDStripModule, RM2WirelessModule
7+
8+
9+
"""
10+
Obtain the current CheerLights colour from the internet and show it on an LED Strip connected to Yukon.
11+
For more information about CheerLights, visit: https://cheerlights.com/
12+
13+
This example requires a secrets.py file to be on your board's file system with the credentials of your WiFi network.
14+
15+
Hold "Boot" to exit the program (can take up to 5 seconds).
16+
"""
17+
18+
try:
19+
from secrets import WIFI_SSID, WIFI_PASSWORD
20+
if len(WIFI_SSID) == 0:
21+
raise ValueError("no WiFi network set. Open the 'secrets.py' file on your device to add your WiFi credentials")
22+
except ImportError:
23+
raise ImportError("no module named 'secrets'. Create a 'secrets.py' file on your device with your WiFi credentials")
24+
25+
26+
# Constants
27+
COLOUR_NAMES = ("R", "G", "B")
28+
CONNECTION_INTERVAL = 1.0 # The time to sleep between each connection check
29+
REQUEST_INTERVAL = 5.0 # The time to sleep between each internet request
30+
STRIP_TYPE = LEDStripModule.NEOPIXEL # Change to LEDStripModule.DOTSTAR for APA102 style strips
31+
# Two Neopixel strips can be driven too, by using LEDStripModule.DUAL_NEOPIXEL
32+
STRIP_PIO = 0 # The PIO system to use (0 or 1) to drive the strip(s)
33+
STRIP_SM = 0 # The State Machines (SM) to use to drive the strip(s)
34+
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
35+
BRIGHTNESS = 1.0 # The max brightness of the LEDs (only supported by APA102s)
36+
37+
# Variables
38+
yukon = Yukon() # Create a new Yukon object
39+
leds = LEDStripModule(STRIP_TYPE, # Create a LEDStripModule object, with the details of the attached strip(s)
40+
STRIP_PIO,
41+
STRIP_SM,
42+
LEDS_PER_STRIP,
43+
BRIGHTNESS)
44+
wireless = RM2WirelessModule() # Create a RM2WirelessModule object
45+
46+
47+
# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
48+
try:
49+
yukon.register_with_slot(leds, STRIP_SLOT) # Register the LEDStripModule object with the slot
50+
yukon.register_with_slot(wireless, RM2_SLOT) # Register the RM2WirelessModule object with the slot
51+
yukon.verify_and_initialise() # Verify that the modules are attached to Yukon, and initialise them
52+
53+
wlan = network.WLAN(network.STA_IF) # Create a new network object for interacting with WiFi
54+
wlan.active(True) # Turn on WLAN communications
55+
56+
# Connect to WLAN
57+
print(f"Connecting to network '{WIFI_SSID}'")
58+
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
59+
60+
# Wait until the connection is established
61+
while not wlan.isconnected():
62+
print('Waiting for connection...')
63+
yukon.monitored_sleep(CONNECTION_INTERVAL)
64+
65+
# Print out our IP address
66+
print(f'Connected on {wlan.ifconfig()[0]}')
67+
68+
# Turn on power to the module slots and the LED strip
69+
yukon.enable_main_output()
70+
leds.enable()
71+
72+
# Loop until the BOOT/USER button is pressed
73+
while not yukon.is_boot_pressed():
74+
75+
# Get the current CheerLights colour from the internet
76+
req = requests.get("http://api.thingspeak.com/channels/1417/field/2/last.json")
77+
json = req.json()
78+
req.close()
79+
80+
# Use the second to get the colour components for the RGB output
81+
colour = tuple(int(json['field2'][i:i + 2], 16) for i in (1, 3, 5))
82+
83+
# Print out the Cheerlights colour
84+
for i in range(len(colour)):
85+
print(f"{COLOUR_NAMES[i]} = {colour[i]}", end=", ")
86+
print()
87+
88+
# Apply the colour to all the LEDs and send them to the strip
89+
for led in range(leds.strip.num_leds()):
90+
leds.strip.set_rgb(led, *colour)
91+
leds.strip.update()
92+
93+
# Monitor sensors for a number of seconds, recording the min, max, and average for each
94+
yukon.monitored_sleep(REQUEST_INTERVAL)
95+
96+
97+
finally:
98+
# Put the board back into a safe state, regardless of how the program may have ended
99+
yukon.reset()
100+
101+
# Attempt to disconnect from WiFi
102+
try:
103+
wlan.disconnect()
104+
except Exception:
105+
pass
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from pimoroni_yukon import Yukon
2+
from pimoroni_yukon import SLOT5 as SLOT
3+
from pimoroni_yukon.modules import RM2WirelessModule
4+
5+
"""
6+
A boilerplate example showing how to detect if the RM2 Wireless Module
7+
is attached to Yukon prior to performing any wireless operations.
8+
"""
9+
10+
# Variables
11+
yukon = Yukon() # Create a new Yukon object, with a lower voltage limit set
12+
module = RM2WirelessModule() # Create a RM2WirelessModule object
13+
14+
# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
15+
try:
16+
yukon.register_with_slot(module, SLOT) # Register the RM2WirelessModule object with the slot
17+
yukon.verify_and_initialise() # Verify that a RM2WirelessModule is attached to Yukon, and initialise it
18+
19+
# Do wireless things here
20+
21+
finally:
22+
# Put the board back into a safe state, regardless of how the program may have ended
23+
yukon.reset()
24+
25+
# Disconnect from wireless here
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import network
2+
import binascii
3+
from pimoroni_yukon import Yukon
4+
from pimoroni_yukon import SLOT5 as SLOT
5+
from pimoroni_yukon.modules import RM2WirelessModule
6+
7+
"""
8+
Periodically scan for available WiFi networks using a RM2 Wireless Module connected to Slot 5,
9+
and print out their details.
10+
11+
Hold "Boot" to exit the program (can take up to 5 seconds).
12+
"""
13+
14+
# Constants
15+
SCAN_INTERVAL = 5.0 # The time to sleep between each network scan
16+
17+
# Variables
18+
yukon = Yukon() # Create a new Yukon object, with a lower voltage limit set
19+
module = RM2WirelessModule() # Create a RM2WirelessModule object
20+
21+
22+
# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
23+
try:
24+
yukon.register_with_slot(module, SLOT) # Register the RM2WirelessModule object with the slot
25+
yukon.verify_and_initialise() # Verify that a RM2WirelessModule is attached to Yukon, and initialise it
26+
27+
wlan = network.WLAN(network.STA_IF) # Create a new network object for interacting with WiFi
28+
wlan.active(True) # Turn on WLAN communications
29+
30+
while not yukon.is_boot_pressed():
31+
# Scan for nearby networks and print them out
32+
networks = wlan.scan() # Returns a list of tuples with 6 fields: ssid, bssid, channel, RSSI, security, hidden
33+
for i, w in enumerate(networks):
34+
print(i, w[0].decode(), binascii.hexlify(w[1]).decode(),
35+
w[2], w[3], w[4], w[5])
36+
print()
37+
38+
# Monitor sensors for a number of seconds, recording the min, max, and average for each
39+
yukon.monitored_sleep(SCAN_INTERVAL)
40+
41+
finally:
42+
# Put the board back into a safe state, regardless of how the program may have ended
43+
yukon.reset()
44+
45+
# Attempt to disconnect from WiFi
46+
try:
47+
wlan.disconnect()
48+
except Exception:
49+
pass

firmware/PIMORONI_YUKON/board.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"images": [
99
],
1010
"mcu": "rp2040",
11-
"product": "Pimoroni Yukon",
11+
"product": "Pimoroni Yukon (no Wireless)",
1212
"thumbnail": "",
1313
"url": "https://shop.pimoroni.com/products/yukon",
1414
"vendor": "Pimoroni"

0 commit comments

Comments
 (0)