servo_pwm.py
A micro-servo (SG90, MG90) on D5 sweeps 0 to 180°: 50 Hz PWM, 0.5-2.5 ms pulses computed on the pulseio 16-bit scale. The classic radar-sweep command, ready to accept any position.
Hardware
An Adafruit Feather nRF52840 Express (nRF52840 Cortex-M4F SoC, BLE 5 / 802.15.4) with on-board NeoPixel, red LED, 2 MB QSPI flash for the CircuitPython filesystem and a LiPo battery connector. A 5 V micro-servo; power it from VOUT (5 V on USB) or a separate battery — the board 3V3 regulator is too weak for the stall spike.
Wiring
Signal (orange/yellow wire) to D5, servo + to VOUT, servo ground to GND — common ground with the Feather is mandatory.
How it works
pulseio.PWMOut generates the hardware 50 Hz; the angle function maps 0-180° onto 2.5-12.5 % duty (0.5-2.5 ms in 20 ms). The for loops sweep in ~1.4 s per pass.
Running it
CircuitPython (feather_nrf52840_express build) installed via the UF2 bootloader (double reset); edit code.py on the CIRCUITPY drive, console in Mu/Thonny.
Variants
Drive the angle from BLE or a sensor, add a second servo for a two-axis arm, or smooth motion with interpolation.
servo_pwm.py
# Micro-servo sur D5 : balaie 0 -> 180 -> 0 degrés.
# Alimentation servo : broche VOUT (USB) ou batterie, pas le 3V3 seul.
import time
import board
import pulseio
servo = pulseio.PWMOut(board.D5, frequency=50, duty_cycle=0)
def angle(a):
# 20 ms de période : 0,5 ms = 0°, 2,5 ms = 180°
# duty_cycle 16 bits : 65535 = 20 ms
mini = int(65535 * 0.5 / 20)
maxi = int(65535 * 2.5 / 20)
servo.duty_cycle = mini + (maxi - mini) * a // 180
while True:
for a in range(0, 181, 5):
angle(a)
time.sleep(0.02)
for a in range(180, -1, -5):
angle(a)
time.sleep(0.02)