tiny kite-borne sensor node, collaboration w/ chucho ocampo
Board renders, schematics, interactive bom, and fabrication files on this page were automatically generated by clef + Nix.
ocularium is a tiny sensor platform I designed to be flown on kites. I'm working with Chucho Ocampo on this project, who I met on SEI's trip to Lanzarote.
The design is finished, and as of writing this post I'm just sending it out to be fabricated.
Warning: these production files have not been validated — I am in the process of converting to kikit (which generates them and puts them here) but spun the boards using Fabrication Toolkit.
design
The board is 30x30mm with a 25x25mm M2 hole pattern. It's powered by a lithium cell via a battery controller, and uses an SD card for data storage. The sensors onboard are a six-axis accelerometer/gyro, a BME680 gas sensor (temperature, humidity, pressure, air quality), a lux sensor, and a MEMS microphone.
Design goals:
- Small and light, to minimize impact on the kite's flight -- Chucho's previous solution was bulky/heavy and would pull the kite
- Useful / interesting sensor payload
- ~4-hour endurance -- long enough to be forgiving about state of charge
- Low-cost
software / firmware
I'm writing the firmware in Rust using the embassy family of embedded libraries.
I am currently planning on writing a host-side UI in iced, which I've had luck with in the past (I'm using it for an in-progress mission UI for antrelay).
future work
I will be tying this in with interstice as a networking test platform.
bringup log
2024-08-22
depaneled board, front
Boards came in today. Production went well — they look great. I'm looking at a power issue: the BQ25185 battery charger IC has a regulated output that I was using to supply 3.3V. Problem is that it's actually 4.5V — didn't catch this at design time. In retrospect, I think I had it as a placeholder part that I planned to replace (or augment with a DCDC), but it slipped through the cracks.
Also have an ADC problem — I have VBAT, VUSB, and 3.3V connected to ADC lines, but I didn't add voltage dividers, so they're overvolting the ADCs and I suspect leaking through the ESD protection, if not damaging the silicon. I still have 2 untouched assembled boards if I already managed to burn one out.
Going to have to respin, but provisionally powering 3.3V directly off of my bench supply to enable bringup on this rev and find any more issues.
Tried to connect over SWD with an st-link clone, but it appears they don't support multidrop, which seems to be required for rp2040. Tried flashing black magic probe, which notionally addresses this, but it couldn't see the core. Ordered a few picos (I seem to have run out) to flash picoprobe onto, which I know will work.
2024-08-26
she blinks
Got my picos set up as picoprobes,
but no dice. Hooked up to my logic analyzer and saw well-formed SWD traffic. I
don't know the protocol so couldn't tell if there was any response data, but I
assumed not as probe-rs
complained that the device wasn't
responding:
My priority is get the rp2040 powering on and be able to flash code, so I moved
on to triggering BOOTSEL mode. Shorted QSPI_SS
to GND
and plugged in via
USB, but measured voltage on my power supply shot up to > 3.5V and it wasn't
sourcing any current, implying that the circuit was powering itself off of USB,
even with the switch between VSYS
(battery charger output, 4.5V —
discussed in last entry) and 3.3V turned off. I don't know how — the only
place VUSB goes other than the charger IC are the ADC pins. Could be something
fried and bridged the nets, but in this whole process I've neither seen magic
smoke nor noticed comonents getting warm — that's no more than
speculation at the moment.
On my last untouched board, I cut the VUSB traces right at the USB connector and verified open circuit to the regulator. Again shorted QSPI_SS to ground, powered on with my bench supply, and connected to USB.
Linux tries to connect, but can't talk to the device:
Seems like progress, but I logic analyzed this too, and the rp2040 isn't even
trying to respond — the traffic on the bus is just the host sending
SETUP
and DATA0 -> GET_DESCRIPTOR
packets until it gives up.
There is something happening on the board — when I power it, I see a reproducible pattern of power usage stabilizing at single-digit milliamps on my bench supply, but there's no way for me to tell at present whether that's from the RP2040 or any of the other ICs.
rescue port
Did some digging in the hardware design guide and saw this section about the rescue debug port:
The rescue debug port (DP) on RP2040 can be used to reset the chip into a known state if the user has programmed some bad code into the flash. For example some code that turned off the system clock would stop the processor debug ports being accessed, but the rescue DP would still work because it is clocked from the SWCLK of the SWD interface.
This seems like exactly what I want, because the whole contents of my flash are known to be garbage, as I've never flashed anything to the board before. The question of why I haven't been able to enter BOOTSEL mode is still open, but hopefully this will let me bypass it.
It requires you to use the Raspberry Pi Foundation's own independent fork of openocd (the practice of forking off vendor-specific tooling continues to be a pain in the ass):
)
)
(openocd
output substantially cleaned for readability).
I connected with gdb
, but this failed initially because it was trying to
identify my SPI flash and QSPI_SS
(= ~BOOTSEL
) was still shorted to GND
:
)
Fixed by opening this short:
)
And then flashed blinky firmware:
() )
)
() )
() )
() )
() )
() )
And now probe-rs run
(via cargo run
) works:
)
)
)
probe-rs info
works, but it locks up the core and probe-rs reset
doesn't reset it:
This last note about multidrop/TARGETSEL could be the solution, but there isn't
a flag to set TARGETSEL for probe-rs reset
. Fine in the end — I can manually
reset with the power supply.
2024-08-27
Working on USB, seeing the same issue as before: host sees the pull-up and tries to contact the device a few times before giving up:
I'm testing with firmware that I've flashed to a XIAO RP2040, which successfully establishes a high-speed link and opens a CDC-ACM port which is recognized by the host computer. Expected output:
Probed voltage on D+ and I'm seeing 3.0V with a cable plugged in rather than expected 3.3V for LS operation. D- is at ground potential. Possibly an issue with drive strength on the USB peripheral on the RP2040? But apparently 2.8V is the specified signaling minimum, so perhaps this shouldn't be surprising. Tried a few different USB cables that have been known to work and different ports on the host, no dice.
Probed D+ with USB cable disconnected and I'm seeing 3.20V, which for me counts as effectively nominal for a specified 3.3V.
Tried shorting the 27.4Ω impedance-matching resistors just for something to try — no change.
cc2 / vconn
Found an issue on my schematic: CC2 wasn't terminated — I left it floating. Another thing I intended to get back to at the time, but didn't, and then by the time I went to ship the board, I assumed past-me had validated it.
I'm testing with a USB A-to-C cable, which I suppose must internally have orientation detection logic, so this seems a likely culprit — I definitely need to fix it for the next rev. However, now I'm thinking back to the fact that I got a signal that looked fine on a scope and decoded as valid host-to-device control packets with a logic analyzer, probed right at the impedance-matching resistors next to the rp2040 pins.
I have kept coming back to this idea that maybe my data lines are backwards, but I've checked several times, and they weren't. Then again, I've only been checking by looking at the voltage at D+, the pull-up for which is device-supplied, so possibly that's not a reliable test. And when I scoped/ logic-analyzed the traffic, I was looking at the BOOTSEL behavior, so there could be another confounding issue.
Next thing I guess is to properly terminate CC2 and see if that makes a difference.
2024-09-22
Been almost a month since my last update — haven't gotten around to writing progress up. Mostly been working on molybdos as a basis for the firmware, converting chunks of old projects (e.g. janus, hexrx) to portable library code, and porting third-party sensor libraries to async (bme680, lsm6dsm, veml7700_async).
The problem with USB was swapped polarity on the data lines — I was able to rework it and verify the fix. I thought it was a footprint issue initially, but I actually just swapped them in the schematic at the decoupling resistors.
Also discovered that I got my I2C lines wrong — they're attached to different MCU peripherals. There doesn't seem to be a good software or PIO I2C implementation in Rust yet — I started writing a PIO one based on the example in the RP2040 C SDK, but decided the verification overhead wasn't worth it right at this moment. Going to test by breaking out the bus and connecting with my Bus Pirate or a Xiao board.
2024-09-24
Got a PIO I2S driver semi-working — enough to get working PDM audio back.
Scaling the bits to i16::MAX
and i16::MIN
is enough to get recognizable but
choppy sound out of it.
The issue I'm dealing with is that the DMA shots from the PIO FIFOs to memory are taking way longer (40x) than they should. I'm not sure yet whether I'm miscalculating my clock, am missing something about PIO functionality, or if it's something with the host MCU (backpressure or scheduling). I'm running the read loop in the regular thread-mode executor, so it could just be a scheduling issue (though there shouldn't be any other futures running concurrently, so that's kind of dubious). Next thing I'll try is a dedicated higher-priority interrupt executor just because it's easy.
That said, not going to bother for now. I can hear audio, so I know it's working enough to proceed with the next rev.
Confirmed that the BME is reachable over its SPI bus by reading its chip id. Not going to bother trying to read data off of it, just made sure I can talk to it.
2024-09-25
Took a look at the SD card today — it wasn't working right off the bat. Looks like I flipped the MOSI and MISO lines (guessing I feel victim to the SDO/SDI terminology mismatch: CMD is "data in" but "master out" / DAT0 is "data out" but "master in", and I matched on the wrong role). Reworking this would be a pain in the ass because the pins are tiny, so I'm going to just blindly respin it.
Also relevant: last week or so, I broke out my
fork of embedded_sdmmc
from the
hexrx firmare as a standalone repo.
2024-09-26
Had to find my bus pirate yesterday — ran I2C sanity checks today:
bus scan
I2C>(1)
Searching I2C address space. Found devices at:
0x20(0x10 W) 0x21(0x10 R) 0xD6(0x6B W) 0xD7(0x6B R)
Correctly found the VEML7700 at 0x10 and the LSM6DSM at 0x6B.
lsm6dsm
Find WHOAMI
reg (0x0f):
I2C>[0xd6 0x0f [0xd7 r]
I2C START BIT
WRITE: 0xD6 ACK
WRITE: 0x0F ACK
I2C START BIT
WRITE: 0xD7 ACK
READ: 0x6A
NACK
I2C STOP BIT
Sees correct value 0x6a.
veml7700
chipid reg at 0x7:
I2C>[0x20 0x7 [0x21 rr]
I2C START BIT
WRITE: 0x20 ACK
WRITE: 0x07 ACK
I2C START BIT
WRITE: 0x21 ACK
READ: 0x81
READ: ACK 0xC4
NACK
I2C STOP BIT
16 bit registers, default configuration is 0xC4 with address 0x81 — checks out.
respin todo
- 3V3 regulator
- fix cc termination
- adc
- voltage dividers
- voltage reference
- purple LED -> change to blue (too dim)
-
esd protection
- VBUS, D+/D-
- SWCLK, SWDIO
- usb data polarity
- i2c pin fix
- sd card fixes
- fix mosi/miso flip
- (optional) card detect
- connect sensor hub to i2c (solder jumpers, default-open)
- connect bme to i2c
- switch to proper i2s part