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 --> <!-- Lesson 1 -->
<section id="lesson1" class="lesson-content"> <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"> <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Step 1 --> <!-- Step 1 -->
<div class="prose"> <div class="prose">
<h2>Step 1: Setting up the IDE</h2> <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 <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>
editor.</p>
<p>However we'll be using one called "Mu Editor". So let's begin by downloading it.</p>
<h3>Download</h3> <h3>Download</h3>
<ul class="ml-6 list-disc"> <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>
<li><a href="files/MuEditor-Linux-1.2.0-x86_64.tar"
class="text-blue-600 hover:underline">Linux</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>
<li><a href="files/MuEditor-OSX-1.2.0.dmg" class="text-blue-600 hover:underline">Mac</a></li>
</ul> </ul>
<p>You can also get it from the Mu website, <a <p>You can also get it from the Thonny website, <a
href="https://codewith.mu/en/">https://codewith.mu/en/</a></p> href="https://thonny.org/">https://thonny.org/</a></p>
</div> </div>
<div> <div>
@ -68,21 +65,23 @@
<!-- Step 2: Image --> <!-- Step 2: Image -->
<div class="prose"> <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"> <ol class="list-decimal ml-6">
<li>Download the CircuitPython firmware UF2 file for the RP2040 Zero <a <li>In Thonny, click on the Run->Configure Interpreter buttons at the top of the window.</li>
href="files/adafruit-circuitpython-waveshare_rp2040_zero-en_GB-9.2.8.uf2">here</a>.</li> <li>Choose "MicroPython (Raspberry Pi Pico) from the first dropdown menu.</li>
<li>Connect your RP2040 Zero to your computer using a USB cable.</li> <li>Click the "<u>Install or update MicroPython</u>" button down the bottom-right of the window.</li>
<li>Hold down the BOOT button, press and release the RST button.</li> <li>You need to put your device in FLASH mode, hold down the BOOT button, press and release the RESET button.</li>
<li>A new USB drive appears in your file explorer called <b>RPI-RP2</b>.</li> <li>You should now find a drive called "RPI-RP2". in the Target volume dropdown, shoose it.</li>
<li>Drag the downloaded CircuitPython firmware file into the <b>RPI-RP2</b> drive.</li> <li>Choose the variant "Raspberry Pi Pico - Pico / Pico H"</li>
<li>The firmware will install, and after a moment, the drive should be called <b>CIRCUITPY</b>. <li>Press Install button to install the firmware.</li>
</li> <li>Reset the device again with the RESET button.</li>
<li>Open the drive and examine the different files in there. The "code.py" file is our main <li>Press the cancel button to close the firmware update panel, then press OK to officially begin coding.</li>
file, but we won't open it here.</li>
<li>Open the MU Editor, and we can start coding!</li>
</ol> </ol>
</div> </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> <div>
<img src="images/rp2040zero.png" alt="Robot moving further" <img src="images/rp2040zero.png" alt="Robot moving further"
class="rounded shadow w-full max-w-xs md:max-w-full" /> class="rounded shadow w-full max-w-xs md:max-w-full" />
@ -94,15 +93,15 @@
<!-- Lesson 2 (hidden initially) --> <!-- Lesson 2 (hidden initially) -->
<section id="lesson2" class="lesson-content hidden"> <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"> <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Step 1 --> <!-- Step 1 -->
<div class="prose"> <div class="prose">
<h2>Step 1: Open the CIRCUITPY/code.py file</h2> <h2>Step 1: Open/Create the main.py file</h2>
<p>When your device is plugged into your computer, you should be able to find the CIRCUITPY drive in <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>
your list of drives. Open the code.py file on that drive using the "Load" button.</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>
<div> <div>
@ -111,15 +110,16 @@
<!-- Step 2: Image --> <!-- Step 2: Image -->
<div class="prose"> <div class="prose">
<h2>Step 2: Open the Serial Monitor</h2> <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 <p>To get messages back from your device, and to read errors, press the view->shellbutton and make sure
you have the "CircuitPython REPL" window open at the bottom.</p> you have the shell window open at the bottom.</p>
</br> </br>
<h2>Step 3: Hello World</h2> <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 <code>print("Hello World")</code> and then save, yuou should see the message appear in the
Serial monitor. Serial monitor.
</p> </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> </br>
<h2>Step 4: Code all the things!</h2> <h2>Step 4: Code all the things!</h2>
@ -129,22 +129,10 @@
</p> </p>
</div> </div>
<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" /> class="rounded shadow w-full max-w-xs md:max-w-full" />
</div> </div>
</div> </div>
</section> </section>
@ -196,27 +184,25 @@
</div> </div>
<div> <div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python"> <pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
import board from machine import Pin, PWM
import pwmio
import time import time
motorIN1 = pwmio.PWMOut(board.GP8, frequency=1000, duty_cycle=0) motorIN1 = PWM(Pin(8))
motorIN2 = pwmio.PWMOut(board.GP9, frequency=1000, duty_cycle=0) motorIN2 = PWM(Pin(9))
# DRIVE FORWARD # DRIVE FORWARD
motorIN1.duty_cycle = 65535 # This is the maximum value motorIN1.duty_u16(65535) # This is the maximum value
motorIN2.duty_cycle = 0 # This is the minimum value motorIN2.duty_u16(0) # This is the minimum value
time.sleep(1) time.sleep(1)
# DRIVE BACKWARD # DRIVE BACKWARD
motorIN1.duty_cycle = 0 motorIN1.duty_u16(0)
motorIN2.duty_cycle = 65535 motorIN2.duty_u16(65535)
time.sleep(1) time.sleep(1)
# STOP # STOP
motorIN1.duty_cycle = 0 motorIN1.duty_u16(0)
motorIN2.duty_cycle = 0 </code></pre> motorIN2.duty_u16(0) </code></pre>
</div> </div>
<!-- Step 3 --> <!-- Step 3 -->
@ -228,13 +214,11 @@ motorIN2.duty_cycle = 0 </code></pre>
</div> </div>
<div> <div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python"> <pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
import board from machine import Pin, PWM
import pwmio
import time
# Initialize motor PWM pins # Initialize motor PWM pins
motorIN1 = pwmio.PWMOut(board.GP8, frequency=1000, duty_cycle=0) motorIN1 = PWM(Pin(8))
motorIN2 = pwmio.PWMOut(board.GP9, frequency=1000, duty_cycle=0) motorIN2 = PWM(Pin(9))
def motor(power): def motor(power):
# Make sure power is never greater than 100 or less than -100 # 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 # Apply power to the correct motor pin
if power > 0: if power > 0:
motorIN1.duty_cycle = duty motorIN1.duty_u16(duty)
motorIN2.duty_cycle = 0 motorIN2.duty_u16(0)
elif power < 0: elif power < 0:
motorIN1.duty_cycle = 0 motorIN1.duty_u16(0)
motorIN2.duty_cycle = duty motorIN2.duty_u16(duty)
else: else:
motorIN1.duty_cycle = 0 motorIN1.duty_u16(0)
motorIN2.duty_cycle = 0 motorIN2.duty_u16(0)
# TESTS # TESTS
motor(100) # FULL FORWARD 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> <p>Now all we need to do from the main code is:</p>
</br> </br>
<p>Import the module</p> <p>Import the module</p>
<p><code>import motors</code></p> <p><code>from motor import Motor</code></p>
</br> </br>
<p>Initialize a motor object</p> <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> </br>
<p>Set the power</p> <p>Set the power</p>
<p><code>left_motor.move(100)</code></p> <p><code>left_motor.move(100)</code></p>
</br> </br>
<p>You can also add a second, third, or 20th motor at any time just by initializing another <p>You can also add a second, third, or 20th motor at any time just by initializing another
motor.Motor object.</p> Motor object.</p>
<p><code>right_motor = motor.Motor(board.GP10, board.GP11)</code></p> <p><code>right_motor = Motor(10, 11)</code></p>
</div> </div>
<div> <div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"> <pre class="bg-gray-100 p-4 rounded shadow text-sm">
<code class="language-python"> <code class="language-python">
# code.py # code.py
import board
import time import time
import motor import motor
left_motor = motor.Motor(board.GP8, board.GP9) left_motor = motor.Motor(9,8)
# TESTS # TESTS
left_motor.move(100) # FULL FORWARD left_motor.move(100) # FULL FORWARD
@ -321,15 +304,20 @@ for i in range(-100, 100):
</code></pre> </code></pre>
</br> </br>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"> <pre class="bg-gray-100 p-4 rounded shadow text-sm">
<code class="language-python"></code> <code class="language-python">
# motor.py # motor.py
import pwmio from machine import Pin, PWM
class Motor: class Motor:
def __init__(self, in1, in2, frequency=1000): def __init__(self, in1, in2, frequency=1000):
# Set up PWM outputs on the specified pins # Set up PWM outputs on the specified pins
self.in1 = pwmio.PWMOut(in1, frequency=frequency, duty_cycle=0) self.in1 = PWM(Pin(in1))
self.in2 = pwmio.PWMOut(in2, frequency=frequency, duty_cycle=0) 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): def move(self, power):
# Constrain power to -100 to 100 # Constrain power to -100 to 100
@ -338,18 +326,18 @@ class Motor:
elif power < -100: elif power < -100:
power = -100 power = -100
# Scale to duty cycle # Scale to duty cycle (065535 for RP2040)
duty = abs(power) * 65535 // 100 duty = abs(power) * 65535 // 100
if power > 0: if power > 0:
self.in1.duty_cycle = duty self.in1.duty_u16(duty)
self.in2.duty_cycle = 0 self.in2.duty_u16(0)
elif power < 0: elif power < 0:
self.in1.duty_cycle = 0 self.in1.duty_u16(0)
self.in2.duty_cycle = duty self.in2.duty_u16(duty)
else: else:
self.in1.duty_cycle = 0 self.in1.duty_u16(0)
self.in2.duty_cycle = 0 self.in2.duty_u16(0)
</code></pre> </code></pre>
</div> </div>
@ -920,75 +908,68 @@ while True:
<!-- Step 2 --> <!-- Step 2 -->
<div class="prose"> <div class="prose">
<h2>Step 2: Coding</h2> <h2>Step 2: Coding</h2>
<p>We'll use a library to handle sending and listening for the pulses. Put this module in your <p>We'll use a library to handle sending and listening for the pulses. Put this module on your device.</p>
CIRCUITPY/lib folder.</p>
</br> </br>
<p><a href="files/adafruit_hcsr04.py">adafruit_hcsr04.py</a></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>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>
</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>
<div> <div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python"> <pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
import board # sonar.py
import adafruit_hcsr04 import machine
import time
# Initialize the sonar device class Sonar:
sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.GP26, echo_pin=board.GP28, timeout=0.005) 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: def distance(self):
distance = 99 # Send a 10µs pulse to trigger
self.trigger.off()
# The try-except block catches errors that time.sleep_us(2)
# occur when no signal returns in time self.trigger.on()
try: time.sleep_us(10)
distance = sonar.distance self.trigger.off()
print(distance)
except RuntimeError as e: # Wait for echo start
pass 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> </code></pre>
</div> </div>
<!-- Step 3 --> <!-- Step 3 -->
<div class="prose"> <div class="prose">
<h2>Step 3: Clean it up a bit</h2> <h2>Step 3: Call it in the main.py</h2>
<p>To tidy up our main loop, we can move all that to a function so the main loop just needs <p>Now all we need to do is create the object and call the distance function in the main.py file.</p>
<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>
</div> </div>
<div> <div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python"> <pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
import board # main.py
import time import time
import adafruit_hcsr04 import sonar
# Initialize the sonar device # Create sonar object with trigger on GP3 and echo on GP2
sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.GP26, echo_pin=board.GP28, timeout=0.005) sonar = sonar.Sonar(trigger_pin=26, echo_pin=28)
def get_distance():
distance = 99
try:
distance = sonar.distance
except RuntimeError as e:
pass
return distance
while True: while True:
print(get_distance()) dist = sonar.distance()
time.sleep(0.1) print("Distance:", dist, "cm")
time.sleep(1)
</code></pre> </code></pre>
</div> </div>