Computing stuff tied to the physical world

Decoding incoming data in Go

To receive data from our imaginary yet-to-be-created I2C bridge on a Raspberry Pi, we’ll need a way to talk to I2C which matches how the slave is set up.

Let’s invent a little protocol to talk to the LPC810, and keep it as simple as possible:

  • the slave is listening for requests on I2C address 0x70
  • it listens for three different requests, by checking the first byte of a write
  • this first byte in each write is interpreted by the slave as a “register” address:
    • write to reg #0: (nodeid, group, freq) = configure radio
    • read from reg #1: 1 byte, number of data available in next packet
    • read N bytes from reg #2: read the next packet, N obtained from reg #1
  • so to read register N, we first write N as 1 byte, and then read the register contents
  • note that we’re creating a polling mechanism, the master (RPi) is asking for data
  • we’ll poll once every 100 ms, i.e. ten times per second

Here is the “decode” app, also available on GitHub, written in Go:

package main

import (
    "fmt"
    "os"
    "syscall"
    "time"
)

const (
    dev      = "/dev/i2c-1"
    addr     = 0x70
    I2CSLAVE = 0x0703
)

const (
    RF_CONFIG = iota
    RF_STATUS
    RF_PACKET
)

func main() {
    file, err := os.OpenFile(dev, os.O_RDWR, os.ModeExclusive)
    if err != nil {
        panic(err)
    }
    syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), I2CSLAVE, addr)

    file.Write([]byte{RF_CONFIG, 1, 42, 8})

    for {
        file.Write([]byte{RF_STATUS})
        nbuf := make([]byte, 1)
        n, _ := file.Read(nbuf)
        if n > 0 && nbuf[0] > 0 {
            file.Write([]byte{RF_PACKET})
            buf := make([]byte, n)
            m, _ := file.Read(buf)

            if m == n {
                fmt.Printf("%02X\n", buf)
            } else {
                fmt.Println("n?", n, m)
            }
        } else {
            time.Sleep(100 * time.Millisecond)
        }
    }
}

That’s all there is to it. You should be able to recognise the logic outlined earlier in the above code. The only complication is the way the I2C bus 1 is being accessed: in a very Unix-like manner, there is a device called /dev/i2c-1, which we open and send a special “IOCTL” request for specifying the I2C address. After that, it’s all quite simple: write some bytes, read some bytes – everything will pass through the I2C bus.

Note also how the “registers” are defined as constants 0, 1, and 2 with names RF_CONFIG, RF_STATUS, and RF_PACKET, respectively. The “iota” is a Go idiom for auto-incrementing.

[Back to article index]