micropython conversion

master
Jake 2026-04-25 21:29:04 +08:00
parent 3bc8093a18
commit 5f0ff276e9
3 changed files with 113 additions and 132 deletions

BIN
images/thonny_code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
images/thonny_firmware.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@ -41,26 +41,23 @@
<!-- Lesson 1 -->
<section id="lesson1" class="lesson-content">
<h1 class="text-3xl font-bold mt=0 mb-6">Setting up our Circuitpython toolchain</h1>
<h1 class="text-3xl font-bold mt=0 mb-6">Setting up our MicroPython toolchain</h1>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Step 1 -->
<div class="prose">
<h2>Step 1: Setting up the IDE</h2>
<p>There are many IDEs that we can use to program CircuitPython robot, we can even just use a text
editor.</p>
<p>However we'll be using one called "Mu Editor". So let's begin by downloading it.</p>
<p>There are many IDEs that we can use to program MicroPython robot, we'll be using one called "Thonny". So let's begin by downloading it.</p>
<h3>Download</h3>
<ul class="ml-6 list-disc">
<li><a href="files/MuEditor-win64-1.2.0.msi" class="text-blue-600 hover:underline">Windows</a>
<li><a href="https://github.com/thonny/thonny/releases/download/v4.1.7/thonny-4.1.7.exe" class="text-blue-600 hover:underline">Windows</a>
</li>
<li><a href="files/MuEditor-Linux-1.2.0-x86_64.tar"
class="text-blue-600 hover:underline">Linux</a></li>
<li><a href="files/MuEditor-OSX-1.2.0.dmg" class="text-blue-600 hover:underline">Mac</a></li>
<li><a href="https://github.com/thonny/thonny/releases/download/v4.1.7/thonny-4.1.7.pkg" class="text-blue-600 hover:underline">Mac</a></li>
</ul>
<p>You can also get it from the Mu website, <a
href="https://codewith.mu/en/">https://codewith.mu/en/</a></p>
<p>You can also get it from the Thonny website, <a
href="https://thonny.org/">https://thonny.org/</a></p>
</div>
<div>
@ -68,21 +65,23 @@
<!-- Step 2: Image -->
<div class="prose">
<h2>Step 2: Install Circuitpython on board</h2>
<h2>Step 2: Install MicroPython firmware on board</h2>
<ol class="list-decimal ml-6">
<li>Download the CircuitPython firmware UF2 file for the RP2040 Zero <a
href="files/adafruit-circuitpython-waveshare_rp2040_zero-en_GB-9.2.8.uf2">here</a>.</li>
<li>Connect your RP2040 Zero to your computer using a USB cable.</li>
<li>Hold down the BOOT button, press and release the RST button.</li>
<li>A new USB drive appears in your file explorer called <b>RPI-RP2</b>.</li>
<li>Drag the downloaded CircuitPython firmware file into the <b>RPI-RP2</b> drive.</li>
<li>The firmware will install, and after a moment, the drive should be called <b>CIRCUITPY</b>.
</li>
<li>Open the drive and examine the different files in there. The "code.py" file is our main
file, but we won't open it here.</li>
<li>Open the MU Editor, and we can start coding!</li>
<li>In Thonny, click on the Run->Configure Interpreter buttons at the top of the window.</li>
<li>Choose "MicroPython (Raspberry Pi Pico) from the first dropdown menu.</li>
<li>Click the "<u>Install or update MicroPython</u>" button down the bottom-right of the window.</li>
<li>You need to put your device in FLASH mode, hold down the BOOT button, press and release the RESET button.</li>
<li>You should now find a drive called "RPI-RP2". in the Target volume dropdown, shoose it.</li>
<li>Choose the variant "Raspberry Pi Pico - Pico / Pico H"</li>
<li>Press Install button to install the firmware.</li>
<li>Reset the device again with the RESET button.</li>
<li>Press the cancel button to close the firmware update panel, then press OK to officially begin coding.</li>
</ol>
</div>
<div>
<img src="images/thonny_firmware.png" alt="Robot moving further"
class="rounded shadow w-full max-w-xs md:max-w-full" />
</div>
<div>
<img src="images/rp2040zero.png" alt="Robot moving further"
class="rounded shadow w-full max-w-xs md:max-w-full" />
@ -94,15 +93,15 @@
<!-- Lesson 2 (hidden initially) -->
<section id="lesson2" class="lesson-content hidden">
<h1 class="text-3xl font-bold mb-6">The MU IDE</h1>
<h1 class="text-3xl font-bold mb-6">The Thonny IDE</h1>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Step 1 -->
<div class="prose">
<h2>Step 1: Open the CIRCUITPY/code.py file</h2>
<p>When your device is plugged into your computer, you should be able to find the CIRCUITPY drive in
your list of drives. Open the code.py file on that drive using the "Load" button.</p>
<h2>Step 1: Open/Create the main.py file</h2>
<p>When your device is plugged into your computer, you should be able to press the File->Load button, and choose "Raspberry Pi Pico" to load from.</p>
<p>If no such file exists we need to make it, press File->New, and then save it to the Raspberry Pi Pico as "main.py"</p>
</div>
<div>
@ -111,15 +110,16 @@
<!-- Step 2: Image -->
<div class="prose">
<h2>Step 2: Open the Serial Monitor</h2>
<p>To get messages back from your device, and to read errors, press the "Serial" button nd make sure
you have the "CircuitPython REPL" window open at the bottom.</p>
<p>To get messages back from your device, and to read errors, press the view->shellbutton and make sure
you have the shell window open at the bottom.</p>
</br>
<h2>Step 3: Hello World</h2>
<p>Your code is only updated on your device when you press "Save". If you write the code
<p>Your code is only updated on your device when you press "Run". If you write the code
<code>print("Hello World")</code> and then save, yuou should see the message appear in the
Serial monitor.
</p>
<p>Note that the code won't persist on the robot after a reset unless you also SAVE it to the device, RUN just runs it once</p>
</br>
<h2>Step 4: Code all the things!</h2>
@ -129,22 +129,10 @@
</p>
</div>
<div>
<img src="images/mu_editor.jpg" alt="Robot turning right"
<img src="images/thonny_code.png" alt="Robot turning right"
class="rounded shadow w-full max-w-xs md:max-w-full" />
</div>
<div class="prose">
<h2>Note: Common Problems</h2>
<p>If you see the ">>>" at the bottom of your serial monitor, and your code isn't running, that's
because you've accidentally put the monitor into a special mode that lets you write code
directly into serial. In this case your saved code won't run. To escape this mode, select the
serial monitor, press "enter/return" a few times, and press CTRL-D.</p>
</div>
<div>
<img src="images/mu_editor_serialmode.jpg" alt="Robot turning right"
class="rounded shadow w-full max-w-xs md:max-w-full" />
</div>
</div>
</section>
@ -196,27 +184,25 @@
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
import board
import pwmio
from machine import Pin, PWM
import time
motorIN1 = pwmio.PWMOut(board.GP8, frequency=1000, duty_cycle=0)
motorIN2 = pwmio.PWMOut(board.GP9, frequency=1000, duty_cycle=0)
motorIN1 = PWM(Pin(8))
motorIN2 = PWM(Pin(9))
# DRIVE FORWARD
motorIN1.duty_cycle = 65535 # This is the maximum value
motorIN2.duty_cycle = 0 # This is the minimum value
motorIN1.duty_u16(65535) # This is the maximum value
motorIN2.duty_u16(0) # This is the minimum value
time.sleep(1)
# DRIVE BACKWARD
motorIN1.duty_cycle = 0
motorIN2.duty_cycle = 65535
motorIN1.duty_u16(0)
motorIN2.duty_u16(65535)
time.sleep(1)
# STOP
motorIN1.duty_cycle = 0
motorIN2.duty_cycle = 0 </code></pre>
motorIN1.duty_u16(0)
motorIN2.duty_u16(0) </code></pre>
</div>
<!-- Step 3 -->
@ -228,13 +214,11 @@ motorIN2.duty_cycle = 0 </code></pre>
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
import board
import pwmio
import time
from machine import Pin, PWM
# Initialize motor PWM pins
motorIN1 = pwmio.PWMOut(board.GP8, frequency=1000, duty_cycle=0)
motorIN2 = pwmio.PWMOut(board.GP9, frequency=1000, duty_cycle=0)
motorIN1 = PWM(Pin(8))
motorIN2 = PWM(Pin(9))
def motor(power):
# Make sure power is never greater than 100 or less than -100
@ -248,14 +232,14 @@ def motor(power):
# Apply power to the correct motor pin
if power > 0:
motorIN1.duty_cycle = duty
motorIN2.duty_cycle = 0
motorIN1.duty_u16(duty)
motorIN2.duty_u16(0)
elif power < 0:
motorIN1.duty_cycle = 0
motorIN2.duty_cycle = duty
motorIN1.duty_u16(0)
motorIN2.duty_u16(duty)
else:
motorIN1.duty_cycle = 0
motorIN2.duty_cycle = 0
motorIN1.duty_u16(0)
motorIN2.duty_u16(0)
# TESTS
motor(100) # FULL FORWARD
@ -283,27 +267,26 @@ for i in range(-100, 100):
<p>Now all we need to do from the main code is:</p>
</br>
<p>Import the module</p>
<p><code>import motors</code></p>
<p><code>from motor import Motor</code></p>
</br>
<p>Initialize a motor object</p>
<p><code>left_motor = motor.Motor(board.GP8, board.GP9)</code></p>
<p><code>left_motor = Motor(8, 9)</code></p>
</br>
<p>Set the power</p>
<p><code>left_motor.move(100)</code></p>
</br>
<p>You can also add a second, third, or 20th motor at any time just by initializing another
motor.Motor object.</p>
<p><code>right_motor = motor.Motor(board.GP10, board.GP11)</code></p>
Motor object.</p>
<p><code>right_motor = Motor(10, 11)</code></p>
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm">
<code class="language-python">
# code.py
import board
import time
import motor
left_motor = motor.Motor(board.GP8, board.GP9)
left_motor = motor.Motor(9,8)
# TESTS
left_motor.move(100) # FULL FORWARD
@ -321,15 +304,20 @@ for i in range(-100, 100):
</code></pre>
</br>
<pre class="bg-gray-100 p-4 rounded shadow text-sm">
<code class="language-python"></code>
<code class="language-python">
# motor.py
import pwmio
from machine import Pin, PWM
class Motor:
def __init__(self, in1, in2, frequency=1000):
# Set up PWM outputs on the specified pins
self.in1 = pwmio.PWMOut(in1, frequency=frequency, duty_cycle=0)
self.in2 = pwmio.PWMOut(in2, frequency=frequency, duty_cycle=0)
self.in1 = PWM(Pin(in1))
self.in2 = PWM(Pin(in2))
self.in1.freq(frequency)
self.in2.freq(frequency)
# Initialize duty to 0
self.in1.duty_u16(0)
self.in2.duty_u16(0)
def move(self, power):
# Constrain power to -100 to 100
@ -338,18 +326,18 @@ class Motor:
elif power < -100:
power = -100
# Scale to duty cycle
# Scale to duty cycle (065535 for RP2040)
duty = abs(power) * 65535 // 100
if power > 0:
self.in1.duty_cycle = duty
self.in2.duty_cycle = 0
self.in1.duty_u16(duty)
self.in2.duty_u16(0)
elif power < 0:
self.in1.duty_cycle = 0
self.in2.duty_cycle = duty
self.in1.duty_u16(0)
self.in2.duty_u16(duty)
else:
self.in1.duty_cycle = 0
self.in2.duty_cycle = 0
self.in1.duty_u16(0)
self.in2.duty_u16(0)
</code></pre>
</div>
@ -920,75 +908,68 @@ while True:
<!-- Step 2 -->
<div class="prose">
<h2>Step 2: Coding</h2>
<p>We'll use a library to handle sending and listening for the pulses. Put this module in your
CIRCUITPY/lib folder.</p>
<p>We'll use a library to handle sending and listening for the pulses. Put this module on your device.</p>
</br>
<p><a href="files/adafruit_hcsr04.py">adafruit_hcsr04.py</a></p>
</br>
<p>Note the <code>timeout=0.005</code>, this is important as it tells the code how long to wait for
an ECHO. If we don't have that, our code could be blocked for quite a lot of time if the only
obstacles are a long way away.</p>
<p>When the "distance()" function is called, it sends a short pulse on the trigger pin, then waits for a response on the echo pin. The time it takes for the echo to return is used to calculate the distance.</p>
</br>
<p>Even though all we really need is to get the distance with <code>sonar.distance</code>, when no
echo is received in time an error will occur that will stop your code from running.</p>
</br>
<p>With our <code>try-except</code> block, any errors will just run whatever is in the
<code>except</code> part. In this case, nothing.
</p>
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
import board
import adafruit_hcsr04
# sonar.py
import machine
import time
# Initialize the sonar device
sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.GP26, echo_pin=board.GP28, timeout=0.005)
class Sonar:
def __init__(self, trigger_pin, echo_pin):
self.trigger = machine.Pin(trigger_pin, machine.Pin.OUT)
self.echo = machine.Pin(echo_pin, machine.Pin.IN)
while True:
distance = 99
def distance(self):
# Send a 10µs pulse to trigger
self.trigger.off()
time.sleep_us(2)
self.trigger.on()
time.sleep_us(10)
self.trigger.off()
# The try-except block catches errors that
# occur when no signal returns in time
try:
distance = sonar.distance
print(distance)
except RuntimeError as e:
pass
# Wait for echo start
while self.echo.value() == 0:
pass
start = time.ticks_us()
# Wait for echo end
while self.echo.value() == 1:
pass
end = time.ticks_us()
# Calculate duration
duration = time.ticks_diff(end, start)
# Convert to distance (speed of sound ~343 m/s)
distance = (duration / 2) * 0.0343
return int(distance)
</code></pre>
</div>
<!-- Step 3 -->
<div class="prose">
<h2>Step 3: Clean it up a bit</h2>
<p>To tidy up our main loop, we can move all that to a function so the main loop just needs
<code>get_distance()</code>.
</p>
</br>
<p>If your code is getting quite long, you might even take functions like this and put them in your
own module to keep your main code easy to read.</p>
<h2>Step 3: Call it in the main.py</h2>
<p>Now all we need to do is create the object and call the distance function in the main.py file.</p>
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
import board
# main.py
import time
import adafruit_hcsr04
import sonar
# Initialize the sonar device
sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.GP26, echo_pin=board.GP28, timeout=0.005)
def get_distance():
distance = 99
try:
distance = sonar.distance
except RuntimeError as e:
pass
return distance
# Create sonar object with trigger on GP3 and echo on GP2
sonar = sonar.Sonar(trigger_pin=26, echo_pin=28)
while True:
print(get_distance())
time.sleep(0.1)
dist = sonar.distance()
print("Distance:", dist, "cm")
time.sleep(1)
</code></pre>
</div>