adabox 002 feather bluetooth le mini robot, part 1

These are my notes for building and programming the Adafruit ADABOX 002 Feature Bluetooth LE Mini Robot.

The ADABOX 002 was the second in the series when Adafruit first started its Ada Box quarterly subscription. It’s a neat concept, where you get a box of goodies every three months that can be used to build up quite a number of interesting projects. ADABOX 002 comes with Adafruit’s Mini 3-Layer Round Robot Chassis Kit 2WD with DC Motors (PRODUCT ID: 3244), a Feather 32u4 Bluefruit LE with Feather Stacking Headers already soldered on, a DC Motor & Stepper FeatherWing Add-on with its pins pre-soldered, and a bread board. All you need to do is assemble everything, stacking the DC Motor FeatherWing on top of the Feather 32u4, and then attaching that to the bread board. It’s all physical assembly with no soldering required.

What I intend to cover, starting with this post, are

  • Notes concerning the mechanical construction that differ from Adafruit’s
  • Notes concerning the installation of the Arduino IDE and supporting drivers
  • Notes concerning the software for controlling the robot

Assembly

You can find the full original assembly instructions here: https://learn.adafruit.com/adabox002/assembling-your-robot. What follows are mods to those instructions.

Before you assemble the lower and middle decks to hold the motors, assemble the standoffs on the middle deck so that there are three in the back and one in the right front, unlike the way illustrated by Adafruit. If you wait to add the standoffs after attaching the lower and middle decks, you’ll discover that the angle you need to make between the supplied screwdriver and the screws will be difficult to deal with.

As you’ll note above the three standoffs are across the back. If you look at the mechanical only kit, that’s the way it’s built. Why someone decided to build it differently for this project is a mystery. Once the middle deck is attached to the lower and the motors are properly in place, then attach the upper deck.

You can see from this angle how the forth standoff is off to the right (right is looking down from the top with the big wheels to the back). That’s so you can move the AA battery box into and out of the space between the middle and upper deck pieces, so that you can change the batteries without taking the robot apart. If those batteries are dead that robot won’t move. One more piece of advice with regards to the battery box. Only put two of the rubber feet onto the back of the battery box. This allows the battery box to be held in place with friction, but makes it easy to slide the box in and out. It also keeps the small slide switch built into the box from being hit as you move it in and out of the middle of the robot.

Now we come to the second change to building the robot. The original directions had the breadboard mounted long axis front-to-back with the back excess breadboard hanging over the back edge. I mounted it 90 degrees, from side-to-side. The way the deck metal is cut, it would seem that’s what the original designer of the hardware had in mind.

The motors are connected to the motor controller board with four 4″ breadboard jumper wires, and tied down with a single miniature nylon cable tie. This is different than from what Adafruit provided, four pins that forced the motor controller board to be at the front of the bread board. With these four wires, a much better layout (to me, at least) is built, and all for an additional $2 worth of parts (if that much).

One other item which is called out on the Adafruit site and is attached here is a 3.7V 500mAh battery, connected to the microcontroller board. You need this battery if you want to run the robot without it having to be attached to the USB cable for power. I only paid an additional $8 for that item.

What it means is that the BOX 002 robot is about 95% there as far as having something easy to build. That extra 5% are the wires and the battery, costing another $10. Why Adafruit doesn’t include the battery with the kit (and raise the price accordingly) is another one of those unanswered mysteries. The fact all of this is easy to fix means it’s not going to stop you, or anyone else, from getting to a very nice looking and working little robot.

 

golang on the raspberry pi, part 3

sweep from William Beebe on Vimeo.

This is a test of the Adafruit 0.8″ Red Matrix FeatherWing (https://www.adafruit.com/product/3155) that’s also driven by the HT16K33 I2C display driver kit. In this example, I’ve gotten a bit fancier, if for no other reason than to create a more interesting display test. The application, sweep, does just that; it sweeps a triangle wave across the display five times, testing each LED in both arrays. The code follows.

/*
Copyright (c) 2018 William H. Beebe, Jr.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
    "fmt"
    "time"
    "os"
    "os/signal"
    "syscall"

    "gobot.io/x/gobot/drivers/i2c"
    "gobot.io/x/gobot/platforms/raspi"
)

const DEFAULT_ADDRESS int = 0x70

// Commands to send the HT16K33
//
const (
    HT16K33_SYSTEM_SETUP byte = 0x20
    HT16K33_OSCILLATOR_ON byte = 0x01
    HT16K33_DISPLAY_SETUP byte = 0x80
    HT16K33_DISPLAY_ON byte = 0x01
    HT16K33_BLINK_OFF byte = 0x00
    HT16K33_BLINK_2HZ byte = 0x02
    HT16K33_BLINK_1HZ byte = 0x04
    HT16K33_BLINK_HALFHZ byte = 0x06
    HT16K33_CMD_BRIGHTNESS byte = 0xE0
)

var con i2c.Connection

func initialize() i2c.Connection {
    adapter := raspi.NewAdaptor()
    adapter.Connect()
    bus := adapter.GetDefaultBus()
    fmt.Printf("bus %d\n", bus)
    con, _ = adapter.GetConnection(DEFAULT_ADDRESS, bus)
    // Turn on chip's internal oscillator.
    con.WriteByte(HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR_ON)
    // Turn on the display. YOU HAVE TO SEND THIS.
    con.WriteByte(HT16K33_DISPLAY_SETUP | HT16K33_DISPLAY_ON)
    // Set for maximum LED brightness.
    con.WriteByte(HT16K33_CMD_BRIGHTNESS | 0x0f)
    return con
}

// A very simple test for the Adafruit 0.8" 8x16 LED Matrix
// FeatherWing Display.
// "Bounces" a row of lit LEDs from top to bottom and back to the top, left to right,
// leaving a single straight line of lit LEDs across the top of the display.
//
func lightAll() {
    block := make([]byte, 16)
    upDirection := make([]bool, 16)
    altIndex := []int{0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
    for i := range block {
        block[i] = 0x80
    }

    for i := 0 ; i < 2 * len(block) ; i++ {
        con.WriteBlockData(0, block)
        if i > 0 {
            time.Sleep(25 * time.Millisecond)
        }
        for j := i ; j >= 0 ; j-- {
            if j < len(block) && block[altIndex[j]] > 1 && ! upDirection[altIndex[j]] {
                block[altIndex[j]] >>= 1
            } else if j < len(block) && block[altIndex[j]] == 1 {
                upDirection[altIndex[j]] = true
            }
            if j < len(block) && block[altIndex[j]] < 0x80 && upDirection[altIndex[j]] {
                block[altIndex[j]] <<= 1
            }
        }
    }
}

// Just turns every lit LED off.
//
func darkenAll() {
    // Turn off every bit on the displays.
    //
    block := make([]byte, 16)
    con.WriteBlockData(0, block)
}

func main() {
    // Hook the various system abort calls for us to use or ignore as we
    // see fit. In particular hook SIGINT, or CTRL+C for below.
    //
    signal_chan := make(chan os.Signal, 1)
    signal.Notify(signal_chan,
        syscall.SIGHUP,
        syscall.SIGINT,
        syscall.SIGTERM,
        syscall.SIGQUIT)

    con := initialize()

    // We want to capture CTRL+C to first clear the display and then exit.
    // We don't want to leave the display lit on an abort.
    //
    go func() {
        for {
            signal := <-signal_chan
            switch signal {
            case syscall.SIGINT:
                // CTRL+C
                fmt.Println()
                darkenAll()
                con.Close()
                os.Exit(0)
            default:
            }
        }
    }()

    darkenAll()
    for i := 0 ; i < 6 ; i++ {
        lightAll()
    }
    time.Sleep(30 * time.Millisecond)
    darkenAll()
    con.Close()
}

The interesting part is function lightAll, starting at line 68. That’s what’s doing all the fancy management of the bit bouncing, or sweeping.