Happy new years everyone! This Christmas I got an awesome light up laser cutout of my favorite Formula 1 track Spa-Francorchamps (home of the F1 Belgian Grand Prix) from my awesome girlfriend. I love the gift and thought I could really bring it to life by adding some “smarts” to the LED lights that power it, and since I’ve been working so much in rust, figured it was a great excuse to get into avr-rust.

I decided to get a clone of an arduino-uno (basically just an Atmega328p) as well as a relay board (to switch the power supply for the track). The track is powered via a USB 2.0 cable and instead of cutting it I ordered a USB extender and cut that. I didn’t have any of my previous equipment here in Paris so I had to buy a soldering kit with some tools.

Relay board, off-brand arduino uno, usb extender cable, jumper cables

I had purchased the relay board because I thought the LEDs powering the F1 track might draw too much current to drive it directly from the arduino digital output pin (my plan was to have the digital pin control the relay which would switch the external DC current). But using my USB multimeter, it turns out that the track only draws around 10mA.

So it looks like we’ll be able to directly power the track through the arduino board, which simplifies the wiring required.

Cutting up the adapter cable

Using a wire cutter I spliced the USB extender in half

I then used a wire stripper to remove the outer insulation plastic

USB 2.0 wires are fairly simple, only have four lines. Two for power (VCC and Ground) and two for data (Data+ and Data-).

Since the F1 track only uses the power lines for the LEDs, we can safely remove the exposed data lines.

Soldering the adapter cable

Now we need to solder these to arduino jumper cables so we can easily plug them in to the arduino breakout board to power directly.

I simply wrapped the exposed wires around each other and soldered them directly.

And since we don’t want the 5V VCC line to short with the ground one I used electrical tape to insulate the solder joints

The cable is now ready! Next up is software

Setting up avr-rust

Next I had to set up avr-rust on my local development machine. Using rustup it’s as simple as

rustup component add rust-src --toolchain nightly

Then using homebrew we need

brew tap osx-cross/avr
brew install avr-binutils avr-gcc avrdude

Rustyspa project

I created a very simple crate that uses PWM with a modulating duty cycle to fade in and out the lights on the F1 track. It uses avl-hal to interface with the arduino hardware and avrdude to flash it.

The full source code is all available here but the main source code is simple

#![no_std]
#![no_main]

extern crate panic_halt;
use arduino_uno::hal::pwm;
use arduino_uno::hal::pwm::Timer1Pwm;
use arduino_uno::prelude::*;

const STEP_SLEEP_MS: u16 = 10;
const ON_SLEEP_MS: u16 = 250;
const OFF_SLEEP_MS: u16 = 100;

#[arduino_uno::entry]
fn main() -> ! {
    let peripherals = arduino_uno::Peripherals::take().unwrap();
    let mut pins = arduino_uno::Pins::new(peripherals.PORTB, peripherals.PORTC, peripherals.PORTD);

    // create a PWM timer
    let mut timer = Timer1Pwm::new(peripherals.TC1, pwm::Prescaler::Prescale64);
    // get pin D9 in PWM output mode
    let mut led = pins.d9.into_output(&mut pins.ddr).into_pwm(&mut timer);

    // enable PWM cycle
    led.enable();

    let duty_range = 0..=led.get_max_duty();
    loop {
        for i in duty_range.clone() {
            led.set_duty(i);
            arduino_uno::delay_ms(STEP_SLEEP_MS);
        }
        arduino_uno::delay_ms(ON_SLEEP_MS);

        for i in duty_range.clone().rev() {
            led.set_duty(i);
            arduino_uno::delay_ms(STEP_SLEEP_MS);
        }
        arduino_uno::delay_ms(OFF_SLEEP_MS);
    }
}

Final product

For now this is the final product! A smoothly fading Spa-Francorchamps! Next up I will want to set up schedules for it to turn on and other special effects, maybe synchronize it to music.