updated all to micropython

master
Jake 2026-07-25 23:06:47 +08:00
parent 1850bed768
commit 0ded6c003b
4 changed files with 343 additions and 172 deletions

View File

@ -0,0 +1,8 @@
{
"hash": "6f652a7d",
"configHash": "aba780af",
"lockfileHash": "e3b0c442",
"browserHash": "e137aec8",
"optimized": {},
"chunks": {}
}

3
.vite/deps/package.json Normal file
View File

@ -0,0 +1,3 @@
{
"type": "module"
}

164
files/ssd1306.py Normal file
View File

@ -0,0 +1,164 @@
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
from micropython import const
import framebuf
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xA6)
SET_DISP = const(0xAE)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_IREF_SELECT = const(0xAD)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xDA)
SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
SET_CHARGE_PUMP = const(0x8D)
# Subclassing FrameBuffer provides support for graphics primitives
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
self.init_display()
def init_display(self):
for cmd in (
SET_DISP, # display off
# address setting
SET_MEM_ADDR,
0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE, # start at line 0
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO,
self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET,
0x00,
SET_COM_PIN_CFG,
0x02 if self.width > 2 * self.height else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV,
0x80,
SET_PRECHARGE,
0x22 if self.external_vcc else 0xF1,
SET_VCOM_DESEL,
0x30, # 0.83*Vcc
# display
SET_CONTRAST,
0xFF, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
SET_IREF_SELECT,
0x30, # enable internal IREF during display on
# charge pump
SET_CHARGE_PUMP,
0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01, # display on
): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP)
def poweron(self):
self.write_cmd(SET_DISP | 0x01)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def rotate(self, rotate):
self.write_cmd(SET_COM_OUT_DIR | ((rotate & 1) << 3))
self.write_cmd(SET_SEG_REMAP | (rotate & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width != 128:
# narrow displays use centred columns
col_offset = (128 - self.width) // 2
x0 += col_offset
x1 += col_offset
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_data(self.buffer)
class SSD1306_I2C(SSD1306):
def __init__(self, i2c, addr=0x3C, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.write_list = [b"\x40", None] # Co=0, D/C#=1
super().__init__(128, 64, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)
def write_data(self, buf):
self.write_list[1] = buf
self.i2c.writevto(self.addr, self.write_list)
class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
import time
self.res(1)
time.sleep_ms(1)
self.res(0)
time.sleep_ms(10)
self.res(1)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)
def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(buf)
self.cs(1)

View File

@ -24,7 +24,7 @@
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson2">Uploading Code</button> <button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson2">Uploading Code</button>
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson3">Motors</button> <button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson3">Motors</button>
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson4">Color Sensors</button> <button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson4">Color Sensors</button>
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson5">I2C Multiplexing</button> <button class="tab-btn text-gray-600 hover:text-blue-600 hidden" data-target="lesson5">I2C Multiplexing</button>
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson6">OLED Display</button> <button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson6">OLED Display</button>
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson7">RGB LED(Neopixel)</button> <button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson7">RGB LED(Neopixel)</button>
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson8">Sonar</button> <button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson8">Sonar</button>
@ -226,6 +226,7 @@ motorIN2.duty_u16(0) </code></pre>
<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">
from machine import Pin, PWM from machine import Pin, PWM
import time
# Initialize motor PWM pins # Initialize motor PWM pins
motorIN1 = PWM(Pin(8)) motorIN1 = PWM(Pin(8))
@ -235,7 +236,7 @@ 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
if power > 100: if power > 100:
power = 100 power = 100
elif power < -100: elif power &lt; -100:
power = -100 power = -100
# Convert 0-100 value, to 0 to 65535 # Convert 0-100 value, to 0 to 65535
@ -245,7 +246,7 @@ def motor(power):
if power > 0: if power > 0:
motorIN1.duty_u16(duty) motorIN1.duty_u16(duty)
motorIN2.duty_u16(0) motorIN2.duty_u16(0)
elif power < 0: elif power &lt; 0:
motorIN1.duty_u16(0) motorIN1.duty_u16(0)
motorIN2.duty_u16(duty) motorIN2.duty_u16(duty)
else: else:
@ -273,7 +274,7 @@ for i in range(-100, 100):
motor code into a new module.</p> motor code into a new module.</p>
</br> </br>
<p>We make a second file called <code>motor.py</code> which we'll import and call from our main <p>We make a second file called <code>motor.py</code> which we'll import and call from our main
<code>code.py</code> file. <code>main.py</code> file.
</p> </p>
<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>
@ -293,7 +294,7 @@ for i in range(-100, 100):
<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 # main.py
import time import time
import motor import motor
@ -312,6 +313,8 @@ for i in range(-100, 100):
print(i) print(i)
time.sleep(0.1) time.sleep(0.1)
</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">
@ -334,7 +337,7 @@ class Motor:
# Constrain power to -100 to 100 # Constrain power to -100 to 100
if power > 100: if power > 100:
power = 100 power = 100
elif power < -100: elif power &lt; -100:
power = -100 power = -100
# Scale to duty cycle (065535 for RP2040) # Scale to duty cycle (065535 for RP2040)
@ -343,7 +346,7 @@ class Motor:
if power > 0: if power > 0:
self.in1.duty_u16(duty) self.in1.duty_u16(duty)
self.in2.duty_u16(0) self.in2.duty_u16(0)
elif power < 0: elif power &lt; 0:
self.in1.duty_u16(0) self.in1.duty_u16(0)
self.in2.duty_u16(duty) self.in2.duty_u16(duty)
else: else:
@ -410,51 +413,41 @@ class Motor:
<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">
# color.py # color.py
import struct
import time class Color:
def __init__(self, bus, address=0x29):
self._bus = bus
self._i2c_address = address
self._bus.writeto(self._i2c_address, b'\x80\x03')
self._bus.writeto(self._i2c_address, b'\x81\x2b')
self._bus.writeto(self._i2c_address, b'\x81\xFF') # Integration time 0xFF=2.4ms 0xF0=38.4ms 0x00=614.4ms
self._bus.writeto(self._i2c_address, b'\x8F\x02') # Gain 0x00=1x, 0x01=4x 0.02=16x, 0x03=60x
TCS_ADDR = 0x29 def scaled(self):
COMMAND_BIT = 0x80 crgb = self.raw()
if crgb[0] > 0:
return tuple(float(x) / crgb[0] for x in crgb[1:])
def i2c_locked(i2c, func, *args, **kwargs): return (0,0,0)
"""Wraps I2C operations with lock acquisition and release."""
while not i2c.try_lock():
pass
try:
return func(*args, **kwargs)
finally:
i2c.unlock()
def write_register(i2c, addr, reg, value): def rgb(self):
"""Writes a byte to a register.""" return tuple(int(x * 255) for x in self.scaled())
i2c_locked(i2c, i2c.writeto, addr, bytes([COMMAND_BIT | reg, value]))
def read_register(i2c, addr, reg, length): def light(self):
"""Reads multiple bytes from a register.""" return self.raw()[0]
result = bytearray(length)
def transfer(): def brightness(self, level=65.535):
i2c.writeto(addr, bytes([COMMAND_BIT | reg])) return int((self.light() / level))
i2c.readfrom_into(addr, result)
return result
return i2c_locked(i2c, transfer) def valid(self):
self._bus.writeto(self._i2c_address, b'\x93')
return self._bus.readfrom(self._i2c_address, 1)[0] & 1
def init_sensor(i2c): def raw(self):
time.sleep(0.01) self._bus.writeto(self._i2c_address, b'\xb4')
write_register(i2c, TCS_ADDR, 0x00, 0x01) # Enable (POWER ON) return struct.unpack("&lt;HHHH", self._bus.readfrom(self._i2c_address, 8))
time.sleep(0.01)
write_register(i2c, TCS_ADDR, 0x00, 0x03) # Enable (COLOR SENSING ON)
write_register(i2c, TCS_ADDR, 0x01, 0xFF) # Integration time 0xFF=2.4ms 0xF0=38.4ms 0x00=614.4ms
write_register(i2c, TCS_ADDR, 0x0F, 0x03) # Gain 0x00=1x, 0x01=4x 0.02=16x, 0x03=60x
def read_rgbc(i2c):
data = read_register(i2c, TCS_ADDR, 0x14, 8)
c = data[1] << 8 | data[0]
r = data[3] << 8 | data[2]
g = data[5] << 8 | data[4]
b = data[7] << 8 | data[6]
return r, g, b, c
</code></pre> </code></pre>
@ -462,73 +455,111 @@ def read_rgbc(i2c):
<!-- Step 3 --> <!-- Step 3 -->
<div class="prose"> <div class="prose">
<h2>Step 3: code.py</h2> <h2>Step 3: main.py</h2>
<p>Finally, this is how we call our color.py code from our main code, initialise and read the <p>Finally, this is how we call our color.py code from our main code, initialise and read the
sensor.</p> sensor.</p>
</br> </br>
<p>We use the <code>busio</code> library to handle our i2c. We initialise the connection by telling <p>We use <code>I2C and Pin</code> from the <code>machine></code>library to handle our i2c. We initialise the connection by telling
it which pins we're using for SCL and SDA, and how fast to communicate.</p> it which pins we're using for SCL and SDA, and how fast to communicate.</p>
</br> </br>
<p>We then pass on that connection to initialize the sensor using <p>We then pass on that connection to initialize the sensor using
<code>color.init_sensor(i2c)</code> <code>color.Color(i2c)</code>
</p> </p>
</br> </br>
<p>The whenever we read <code>color.read_rgbc(i2c)</code> it will give us an array with the <p>The whenever we read <code>color.rgb()</code> it will give us an array with the
<code>[red, green, blue, color]</code>, with the color being the total light reflected, rather <code>[red, green, blue]</code>.
than just a single color.
</p> </p>
<p>If we want to read the transparent sensor and get the total brightness, we call <code>color.light()</code></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">
# code.py # main.py
import busio
import time import time
import board from machine import Pin, I2C
import color import color
i2c = busio.I2C(scl=board.GP1, sda=board.GP0, frequency=1_000_000) i2c = I2C(0, scl=Pin(1), sda=Pin(0))
color.init_sensor(i2c) rc = color.Color(i2c)
while True: while True:
value = color.read_rgbc(i2c) rgb = rc.rgb()
print(value) brightness = rc.light()
time.sleep(0.1) </code></pre> print(rgb, brightness)
time.sleep(0.1)
</code></pre>
</div> </div>
<!-- Step 3 --> <!-- Step 4 -->
<div class="prose"> <div class="prose">
<h2>Step 4: How to use it?</h2> <h2>Step 4: How to use it?</h2>
<p>To take these numbers and use them effectively, we need to break out the different colour <p>To take these numbers and use them effectively, it depends on what we want to do.</p>
channels.</p> <p>TO follow a black line on a white background we only need the total brightness so we use the color.light() function.</p>
</br> </br>ke the total colour from the result, and then use it
<p>To the right is an example of how we might take the total colour from the result, and then use it
to decide whether to turn or not.</p> to decide whether to turn or not.</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">
value = color.read_rgbc(i2c) # Read the sensor [r,g,b,c] value = rc.light() # Read the sensor
# Extract each of the color values from the array
r = value[0] # red
g = value[1] # green
b = value[2] # blue
c = value[3] # all colors
if c > 30: # If the c value is greater than 30
if value > 700: # If the value is greater than 700
# Turn Right # Turn Right
left_motor.move(50) left_motor.move(50)
left_motor.move(-50) right_motor.move(-50)
else: else:
# Go Straight # Go Straight
left_motor.move(50) left_motor.move(50)
left_motor.move(50) right_motor.move(50)
</code></pre> </code></pre>
</div> </div>
<!-- Step 5 -->
<div class="prose">
<h2>Step 5: Adding a second sensor</h2>
<p>One sensor can follow an edge, but for a proper line follower we want a <code>left</code> and a
<code>right</code> sensor.
</p>
</br>
<p>There's a catch: every TCS3472 colour sensor uses the same I2C address (<code>0x29</code>). If you
wire both straight to the same SDA and SCL pins, they'll both answer every message and the
readings will be garbage.</p>
</br>
<p>The fix is to have a second I2C bus, luckily the RP2040 supports this.</p>
</br>
<p>We need to go back to our i2c declaration and add a channel number to it, <code>i2c = I2C(0, scl=Pin(1), sda=Pin(0))</code></p>
</br>
<p>That extra "0" specifically tells the RP2040 to use the first I2C bus. Then we can add a second bus by running <code>i2c2 = I2C(1, scl=Pin(3), sda=Pin(2))</code></p>
<p>Once we've initialized our right colour sensor with <code>rc = color.Color(i2c2)</code> then we're good to go!</code></p>
</div> </div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
# main.py
from machine import Pin, I2C
import time
import color
i2c = I2C(0, scl=Pin(1), sda=Pin(0)) # Initialize the first I2C bus
i2c2 = I2C(1, scl=Pin(3), sda=Pin(2)) # Initialize the second I2C bus
lc = color.Color(i2c2) # Initialize the left colour sensor
rc = color.Color(i2c) # Initialize the right colour sensor
while True:
left_color = lc.light()
right_color = rc.light()
print(left_color, right_color)
time.sleep(0.1)
</code></pre>
</div>
</div>
</section> </section>
<!-- Lesson 5 (hidden initially) --> <!-- Lesson 5 (hidden initially) -->
@ -595,9 +626,9 @@ def i2c_locked(i2c, func, *args, **kwargs):
def select_channel(i2c, channel): def select_channel(i2c, channel):
"""Selects the TCA9548A multiplexer channel.""" """Selects the TCA9548A multiplexer channel."""
if not 0 <= channel <= 7: if not 0 &lt;= channel &lt;= 7:
raise ValueError("Channel must be 0-7") raise ValueError("Channel must be 0-7")
i2c_locked(i2c, i2c.writeto, TCA_ADDR, bytes([1 << channel])) i2c_locked(i2c, i2c.writeto, TCA_ADDR, bytes([1 &lt;&lt; channel]))
</code></pre> </code></pre>
@ -605,7 +636,7 @@ def select_channel(i2c, channel):
<!-- Step 3 --> <!-- Step 3 -->
<div class="prose"> <div class="prose">
<h2>Step 3: code.py</h2> <h2>Step 3: main.py</h2>
<p>So in our main code we just import the muxer module, and make sure we switch to the correct <p>So in our main code we just import the muxer module, and make sure we switch to the correct
channel before sending any messages.</p> channel before sending any messages.</p>
</br> </br>
@ -614,7 +645,7 @@ def select_channel(i2c, channel):
</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">
# code.py # main.py
import busio import busio
import time import time
@ -683,51 +714,47 @@ while True:
<div class="prose"> <div class="prose">
<h2>Step 2: Coding</h2> <h2>Step 2: Coding</h2>
<p>The code for the OLED is a little more complicated that what we want to code for ourselves, so <p>The code for the OLED is a little more complicated that what we want to code for ourselves, so
we'll download a library and place it in out CIRCUITPY/lib folder.</p> we'll upload this file to our device, or copy->paste the contents into a new file called ssd1306.py.</p>
</br> </br>
<p><a href="files/adafruit_ssd1306.mpy">adafruit_ssd1306.mpy</a></p> <p><a href="files/ssd1306.py">ssd1306.py</a></p>
<p><a href="files/adafruit_framebuf.py">adafruit_framebuf.py</a></p>
</br> </br>
<p>We initialize the display with <code>display = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c)</code>. <p>We initialize the display with <code>disp = ssd1306.SSD1306_I2C(i2c)</code>.
</p> </p>
</br> </br>
<p>We colour the entire screen either black (off) or blue/white (on) with <p>We colour the entire screen either black (off) or blue/white (on) with
<code>display.fill(0)</code> for off, or 1 for on. <code>disp.fill(0)</code> for off, or 1 for on.
</p> </p>
</br> </br>
<p>We write text with <code>display.text(text, x, y, color)</code>.</p> <p>We write text with <code>disp.text(text, x, y, color)</code>.</p>
</br> </br>
<p>Finally, before anythin will actually appear on the screen, we send it using <p>Finally, before anythin will actually appear on the screen, we send it using
<code>display.show()</code>. <code>disp.show()</code>.
</p> </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 from machine import Pin, I2C
import time import time
import busio import ssd1306
import adafruit_ssd1306
i2c = busio.I2C(scl=board.GP1, sda=board.GP0, frequency=1_000_000) i2c = I2C(0, scl=Pin(1), sda=Pin(0))
display = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c) disp = ssd1306.SSD1306_I2C(i2c)
disp.fill(0) # Fill the screen with BLACK
display.fill(0) # Fill the screen with BLACK
# Print Hello world at the top left in COLOR # Print Hello world at the top left in COLOR
display.text("Hello World", 0, 0, 1) disp.text("Hello World", 0, 0, 1)
display.show() # Send the update to the screen disp.show() # Send the update to the screen
time.sleep(2) time.sleep(2)
disp.fill(1)# Fille the screen with COLOR
display.fill(1)# Fille the screen with COLOR disp.show() # Send the update to the screen
display.show() # Send the update to the screen
time.sleep(2) time.sleep(2)
# Print Hello world at x:10, y:20 in BLACK # Print Hello world at x:10, y:20 in BLACK
display.text("Hello World", 10, 20, 0) disp.text("Hello World", 10, 20, 0)
display.show() # Send the update to the screen disp.show() # Send the update to the screen
</code></pre> </code></pre>
@ -748,35 +775,29 @@ display.show() # Send the update to the screen
</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, I2C
import time import time
import busio import ssd1306
import adafruit_ssd1306
import color import color
import muxer
i2c = busio.I2C(scl=board.GP1, sda=board.GP0, frequency=1_000_000) i2c = I2C(0, scl=Pin(1), sda=Pin(0)) # Initialize the first I2C bus
display = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c) i2c2 = I2C(1, scl=Pin(3), sda=Pin(2)) # Initialize the second I2C bus
disp = ssd1306.SSD1306_I2C(i2c)
# initialize two colour sensors # initialize two colour sensors
muxer.select_channel(i2c, 0) lc = color.Color(i2c2)
color.init_sensor(i2c) rc = color.Color(i2c)
muxer.select_channel(i2c, 1)
color.init_sensor(i2c)
while True: while True:
# Read the colour channel from both colour sensors left_color = lc.light()
muxer.select_channel(i2c, 0) right_color = rc.light()
value0 = color.read_rgbc(i2c)[3]
muxer.select_channel(i2c, 1)
value1 = color.read_rgbc(i2c)[3]
# Print the results on the display # Print the results on the display
display.fill(0) disp.fill(0)
display.text("L: " + str(value0), 0, 0, 1) disp.text("L: " + str(left_color), 0, 0, 1)
display.text("R: " + str(value1), 0, 10, 1) disp.text("R: " + str(right_color), 0, 10, 1)
display.show() disp.show()
time.sleep(0.1) time.sleep(0.1)
</code></pre> </code></pre>
</div> </div>
@ -816,16 +837,13 @@ while True:
<p>Once again we'll be using a library to handle all the bits and bytes under the hood, so we only <p>Once again we'll be using a library to handle all the bits and bytes under the hood, so we only
have to worry about setting colours.</p> have to worry about setting colours.</p>
</br> </br>
<p><a href="files/neopixel.py">neopixel.py</a></p>
</br>
<p>We only need to initialize the pixel with <p>We only need to initialize the pixel with
<code>pixel = neopixel.NeoPixel(board.GP29, 1, brightness=0.2)</code>. <code>pixel = neopixel.NeoPixel(Pin(16), 1)</code>.
</p> </p>
<p>The "1" is the number of pixels we have, and we CAN set the brightness up to 1.0 but it's quite <p>The Pin is the RP2040 pin that the LED is connected to.The "1" is the number of pixels we have.</p>
bright.</p>
</br> </br>
<p>Then we just need to tell the pixel what colour to be, we talk to <p>Then we just need to tell the pixel what colour to be, we talk to
<code>pixel[0] = (red,green,blue)</code> because <code>pixel[0] = (green,red,blue)</code> because
it's the only one we have. The numbers for each colour go from minimum 0, to maximum 255. it's the only one we have. The numbers for each colour go from minimum 0, to maximum 255.
</p> </p>
@ -833,58 +851,31 @@ while True:
</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
import time import time
import neopixel import neopixel
pixel = neopixel.NeoPixel(board.GP29, 1, brightness=0.2) pixel = neopixel.NeoPixel(Pin(16), 1)
pixel[0] = (20, 0, 0)
pixel.write()
while True: while True:
pixel[0] = (255, 0, 0) # Red pixel[0] = (255, 0, 0) # Green
pixel.write()
time.sleep(1) time.sleep(1)
pixel[0] = (0, 255, 0) # Green pixel[0] = (0, 255, 0) # Red
pixel.write()
time.sleep(1) time.sleep(1)
pixel[0] = (0, 0, 255) # Blue pixel[0] = (0, 0, 255) # Blue
pixel.write()
time.sleep(1) time.sleep(1)
</code></pre> </code></pre>
</div> </div>
<!-- Step 3 -->
<div class="prose">
<h2>Step 3: Make some pretty effects</h2>
<p>This example has three loops, each one fades from one primary colour to the next.</p>
</br>
<p>We increase one LEDs brightness by assigning it <code>i</code>, which counts from 0-255</p>
</br>
<p>We decrease another LEDs brightness by making it <code>255-i</code> so it begins at max, and
counts
down to zero as i becomes higher.</p>
</div> </div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
import board
import time
import neopixel
pixel = neopixel.NeoPixel(board.GP29, 1, brightness=0.2)
while True:
# Fade Red->Green
for i in range(256):
pixel[0] = (255-i, i, 0)
# Fade Green->Blue
for i in range(256):
pixel[0] = (0, 255-i, i)
# Fade Blue->Red
for i in range(256):
pixel[0] = (i, 0, 255-i)
</code></pre>
</div>
</section> </section>
@ -959,9 +950,12 @@ class Sonar:
# Calculate duration # Calculate duration
duration = time.ticks_diff(end, start) duration = time.ticks_diff(end, start)
# Convert to distance (speed of sound ~343 m/s) # Convert to distance in millimeters
distance = (duration / 2) * 0.0343 # Speed of sound ~343 m/s = 0.343 mm/µs
return int(distance) distance_mm = (duration / 2) * 0.343
return int(distance_mm)
</code></pre> </code></pre>
</div> </div>
@ -983,8 +977,8 @@ sonar = sonar.Sonar(trigger_pin=26, echo_pin=28)
while True: while True:
dist = sonar.distance() dist = sonar.distance()
print("Distance:", dist, "cm") print("Distance:", dist, "mm")
time.sleep(1) time.sleep(0.1)
</code></pre> </code></pre>
</div> </div>
@ -1053,7 +1047,7 @@ class ThumbInput:
return None return None
def _map_thumbstick(self, x, min_val, mid_val, max_val): def _map_thumbstick(self, x, min_val, mid_val, max_val):
if x < mid_val: if x &lt; mid_val:
return (x - mid_val) / (mid_val - min_val) * 100 return (x - mid_val) / (mid_val - min_val) * 100
else: else:
return (x - mid_val) / (max_val - mid_val) * 100 return (x - mid_val) / (max_val - mid_val) * 100
@ -1190,7 +1184,8 @@ print(filtered)
<div class="prose"> <div class="prose">
<h2>Step 4: Take the median</h2> <h2>Step 4: Take the median</h2>
<p>That's better, but those large peaks are still throwing our average off quite a bit. We can improve this by taking the MEDIAN of the buffer rather than the average. <p>That's better, but those large peaks are still throwing our average off quite a bit. We can
improve this by taking the MEDIAN of the buffer rather than the average.
</p> </p>
<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">
def median_filter(values): def median_filter(values):
@ -1213,7 +1208,8 @@ print(filtered)
<div class="prose"> <div class="prose">
<h2>Step 5: Fine tuning</h2> <h2>Step 5: Fine tuning</h2>
<p>Before moving on, play with the buffer size to see how it effects the results AND the responsiveness to changes.</p> <p>Before moving on, play with the buffer size to see how it effects the results AND the
responsiveness to changes.</p>
<p>Try and find a good balance with the time.sleep() value as well.</p> <p>Try and find a good balance with the time.sleep() value as well.</p>
</div> </div>
@ -1284,7 +1280,7 @@ class ThumbInput:
return None return None
def _map_thumbstick(self, x, min_val, mid_val, max_val): def _map_thumbstick(self, x, min_val, mid_val, max_val):
if x < mid_val: if x &lt; mid_val:
return (x - mid_val) / (mid_val - min_val) * 100 return (x - mid_val) / (mid_val - min_val) * 100
else: else:
return (x - mid_val) / (max_val - mid_val) * 100 return (x - mid_val) / (max_val - mid_val) * 100
@ -1442,7 +1438,7 @@ class PIDController:
</code></pre> </code></pre>
</br> </br>
<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">
# code.py # main.py
import pid import pid