VanSpoof - Prototype 2 - Echo Firmware

In part 1 and part 2 of building my first VanSpoof prototype, I managed to flash the microcontroller on the PCB with a blinky demo. This time round, let's see about sending and receiving some serial data.

In this prototype, we'll read every byte that comes in to our USART, then echo it straight back out. We can adjust our code's forever loop to wait until it's read() a byte from the USART, then write!() it out again.

    // Forever more...
    loop {
        // Read a byte from the USART, waiting until we've got one.
        let byte = block!(usart.read()).expect("Failed to read from USART2.");
        // Write it straight back out again.
        write!(usart, "{}", byte).expect("Failed to write to USART2.");
    }

If we hook up a Glasgow Digital Interface Explorer to our PCB's RX & TX pins, we can send our laptop's key-presses to the VanSpoof and see what we get back. Typing the traditional "Hello, world!" message gives us the following output.

That's not exactly what I was expecting to see. Those are the ASCII character codes for the letters in the string. To check there's nothing wrong with our firmware or how we've set up the Glasgow, we can run a small test program locally on my laptop.

    let hello_world_bytes:Vec<u8> = "Hello, world!".as_bytes().to_vec();
    for i in hello_world_bytes.iter() {
        print!("{}",i);
    }

That snippet of code shows the same behaviour as we observed with the VanSpoof and Glasgow combination.

A stream of bytes, sent via write!() or print!(), appear as the string-ified representation of their ASCII codes. I think this is because I've only minimally changed our blinky code, and we're still getting our write! macro from the core::fmt::Write trait. Its documentation describes it as,

A trait for writing or formatting into Unicode-accepting buffers or streams.

A u8 isn't actually a "byte of ASCII", but an 8-bit number, so it makes sense the formatter would shape it to fit and string-ify the value. The core::io::Write trait is probably more appropriate for us once we start moving to byte-streams such as Modbus frames. It's described as,

A trait for objects which are byte-oriented sinks.

Just now, casting the byte to a char before writing it out will do, but we will have to remember to come back to this later.

    // Forever more...
    loop {
        // Read a byte from the USART, waiting until we've got one.
        let byte = block!(usart.read()).expect("Failed to read from USART2.");
        // Write it straight back out again.
        write!(usart, "{}", byte as char).expect("Failed to write to USART2.");
    }

That small change gives us the correct output when we couple our PCB back up to the Glasgow.

If we pop a Spy! Break! Inject! between the Glasgow and the VanSpoof, we can hook up our oscilloscope probes and see the byte and its echo being transmitted.

We can even use this to see the difference between 'A' (0b01000001) and 'a' (0b01100001).

The surpising thing is that the bytes are in the opposite direction to what I'd expect to see - the extra bit to shift to lower-case is towards the end of the bitstreams rather than the start. It turns out this because we normally visualise binary numbers MSB first, but USARTs send and receive LSB first.

Most serial communications designs send the data bits within each byte least significant bit first.

Now I've proven we can send and receive data over our USART pins, it's time to knuckle down and code up a Modbus frame assembly routine. We can then try to understand and respond to the bike's Modbus messages.

2025-02-17

Leave a comment