Zoe resurrected

I’ve only just boxed up Zoe having reached her limit, but now the Raspberry Pi Zero W has been released with WiFi and Bluetooth built in, freeing up her one micro-USB socket and thus opening up the possibility of adding GPS tracking to her too.  I’ve just ordered one from The PiHut to try it out.

This isn’t going to affect her maximum camera video size of 480 x 480 pixels, so she’s never going to work as well on gravel like Hermione, but it’s a useful upgrade nonetheless.

Crawler phase 1 complete.

Crawler is finally working:

The problem was the cwiid python library only recognises the 1st edition Wii controllers – those without Wii motionplus.  You can get these originals from ebay.  Here’s the code.

#!/usr/bin/env python

import RPi.GPIO as GPIO # Import the GPIO Library
import cwiid
import time
import os


# Set the GPIO modes
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

# Set variables for the GPIO motor pins
pinMotorAForwards = 10
pinMotorABackwards = 9
pinMotorBForwards = 8
pinMotorBBackwards = 7

# Set additional variables to support L293D bodge
pinMotorA1 = 4
pinMotorA2 = 27
pinMotorB2 = 22
pinMotorB1 = 23

# Set variables for the line detector GPIO pin
pinLineFollower = 25

# Define GPIO pins to use on the Pi
pinTrigger = 17
pinEcho = 18

# Set variable for the LED pin
pinLED1 = 5
pinLED2 = 6

# How many times to turn the pin on and off each second
Frequency = 20
# How long the pin stays on each cycle, as a percent
DutyCycleA = 100
DutyCycleB = 100
# Setting the duty cycle to 0 means the motors will not turn
Stop = 0

# Set the GPIO Pin mode to be Output
GPIO.setup(pinMotorAForwards, GPIO.OUT)
GPIO.setup(pinMotorABackwards, GPIO.OUT)
GPIO.setup(pinMotorBForwards, GPIO.OUT)
GPIO.setup(pinMotorBBackwards, GPIO.OUT)

GPIO.setup(pinMotorA1, GPIO.OUT)
GPIO.setup(pinMotorA2, GPIO.OUT)
GPIO.setup(pinMotorB1, GPIO.OUT)
GPIO.setup(pinMotorB2, GPIO.OUT)

GPIO.output(pinMotorA1, False)
GPIO.output(pinMotorA2, False)
GPIO.output(pinMotorB1, False)
GPIO.output(pinMotorB2, False)

# Set the pinLineFollower pin as an input so its value can be read
GPIO.setup(pinLineFollower, GPIO.IN)

def StopMotors():
    pwmMotorAForwards.ChangeDutyCycle(Stop)
    pwmMotorABackwards.ChangeDutyCycle(Stop)
    pwmMotorBForwards.ChangeDutyCycle(Stop)
    pwmMotorBBackwards.ChangeDutyCycle(Stop)
    GPIO.output(pinLED1, False)
    GPIO.output(pinLED2, False)

    GPIO.output(pinMotorA1, False)
    GPIO.output(pinMotorA2, False)
    GPIO.output(pinMotorB1, False)
    GPIO.output(pinMotorB2, False)

def Backwards():
    pwmMotorAForwards.ChangeDutyCycle(DutyCycleA)
    pwmMotorABackwards.ChangeDutyCycle(Stop)
    pwmMotorBForwards.ChangeDutyCycle(DutyCycleB)
    pwmMotorBBackwards.ChangeDutyCycle(Stop)
    GPIO.output(pinLED1, False)
    GPIO.output(pinLED2, False)

    GPIO.output(pinMotorA1, False)
    GPIO.output(pinMotorA2, True)
    GPIO.output(pinMotorB1, False)
    GPIO.output(pinMotorB2, True)

def Forwards():
    pwmMotorAForwards.ChangeDutyCycle(Stop)
    pwmMotorABackwards.ChangeDutyCycle(DutyCycleA)
    pwmMotorBForwards.ChangeDutyCycle(Stop)
    pwmMotorBBackwards.ChangeDutyCycle(DutyCycleB)
    GPIO.output(pinLED1, True)
    GPIO.output(pinLED2, True)

    GPIO.output(pinMotorA1, True)
    GPIO.output(pinMotorA2, False)
    GPIO.output(pinMotorB1, True)
    GPIO.output(pinMotorB2, False)

def Right():
    print("Right")
    pwmMotorAForwards.ChangeDutyCycle(Stop)
    pwmMotorABackwards.ChangeDutyCycle(DutyCycleA * 0.3)
    pwmMotorBForwards.ChangeDutyCycle(DutyCycleB * 0.3)
    pwmMotorBBackwards.ChangeDutyCycle(Stop)
    GPIO.output(pinLED1, True)
    GPIO.output(pinLED2, False)

    GPIO.output(pinMotorA1, True)
    GPIO.output(pinMotorA2, False)
    GPIO.output(pinMotorB1, False)
    GPIO.output(pinMotorB2, True)

def BLeft():
    print("Right")
    pwmMotorAForwards.ChangeDutyCycle(DutyCycleA * 0.3)
    pwmMotorABackwards.ChangeDutyCycle(Stop)
    pwmMotorBForwards.ChangeDutyCycle(DutyCycleB)
    pwmMotorBBackwards.ChangeDutyCycle(Stop)
    GPIO.output(pinLED1, True)

    GPIO.output(pinMotorA1, True)
    GPIO.output(pinMotorA2, False)
    GPIO.output(pinMotorB1, False)
    GPIO.output(pinMotorB2, True)

def FLeft():
    print("Right")
    pwmMotorAForwards.ChangeDutyCycle(Stop)
    pwmMotorABackwards.ChangeDutyCycle(DutyCycleA * 0.3)
    pwmMotorBForwards.ChangeDutyCycle(Stop)
    pwmMotorBBackwards.ChangeDutyCycle(DutyCycleB)
    GPIO.output(pinLED1, True)

    GPIO.output(pinMotorA1, True)
    GPIO.output(pinMotorA2, False)
    GPIO.output(pinMotorB1, False)
    GPIO.output(pinMotorB2, True)

def Left():
    print("Left")
    pwmMotorAForwards.ChangeDutyCycle(DutyCycleA * 0.3)
    pwmMotorABackwards.ChangeDutyCycle(Stop)
    pwmMotorBForwards.ChangeDutyCycle(Stop)
    pwmMotorBBackwards.ChangeDutyCycle(DutyCycleB * 0.3)
    GPIO.output(pinLED1, False)
    GPIO.output(pinLED2, True)

    GPIO.output(pinMotorA1, False)
    GPIO.output(pinMotorA2, True)
    GPIO.output(pinMotorB1, True)
    GPIO.output(pinMotorB2, False)

def BRight():
    print("Left")
    pwmMotorAForwards.ChangeDutyCycle(DutyCycleA)
    pwmMotorABackwards.ChangeDutyCycle(Stop)
    pwmMotorBForwards.ChangeDutyCycle(DutyCycleB * 0.3)
    pwmMotorBBackwards.ChangeDutyCycle(Stop)
    GPIO.output(pinLED1, False)

    GPIO.output(pinMotorA1, False)
    GPIO.output(pinMotorA2, True)
    GPIO.output(pinMotorB1, True)
    GPIO.output(pinMotorB2, False)

def FRight():
    print("Left")
    pwmMotorAForwards.ChangeDutyCycle(Stop)
    pwmMotorABackwards.ChangeDutyCycle(DutyCycleA)
    pwmMotorBForwards.ChangeDutyCycle(Stop)
    pwmMotorBBackwards.ChangeDutyCycle(DutyCycleB * 0.3)
    GPIO.output(pinLED1, False)

    GPIO.output(pinMotorA1, False)
    GPIO.output(pinMotorA2, True)
    GPIO.output(pinMotorB1, True)
    GPIO.output(pinMotorB2, False)


def do_WiiRemote():
    global speed, DutyCycleA, DutyCycleB

    #connecting to the wiimote. This allows several attempts
    # as first few often fail.
    print 'Press 1+2 on your Wiimote now...'
    wm = None
    while not wm:
        try:
            LED1.ChangeDutyCycle(50)
            wm=cwiid.Wiimote()

        except RuntimeError:
            continue

    LED1.ChangeDutyCycle(0)
    LED1.stop()

    #set wiimote to report button presses and accelerometer state
    wm.rpt_mode = cwiid.RPT_BTN | cwiid.RPT_ACC

    #turn on led to show connected
    wm.led = 1

    buttons = wm.state['buttons']
    speed = 100
    DutyCycleA = speed
    DutyCycleB = speed
    moving = False

    #--------------------------------------------------------------------------
    # There are official cwiid numbers for the following - I need to

    WII_BTN_2 = 1              # cwiid.BTN_2
    WII_BTN_1 = 2              # cwiid.BTN_1
    WII_BTN_B = 4              # cwiid.BTN_B
    WII_BTN_A = 8              # cwiid.BTN_A
    WII_BTN_MINUS = 16         # cwiid.BTN_MINUS
    WII_BTN_HOME = 128
    WII_BTN_LEFT = 256
    WII_BTN_RIGHT = 512
    WII_BTN_DOWN = 1024
    WII_BTN_UP = 2048
    WII_BTN_PLUS = 4096        # cwiid.BTN_PLUS
                               # cwiid.NUNCHUK_BTN_C
                               # cwiid.NUNCHUK_BTN_Z


    while not(buttons == WII_BTN_HOME + WII_BTN_A + WII_BTN_B):
        buttons = wm.state['buttons']
        print("Buttons: " + str(buttons))
        if (buttons == WII_BTN_A + WII_BTN_B + WII_BTN_2): # Halt
            wm.led = 2
            print ("Halting Raspberry Pi...")
            GPIO.cleanup()
            bashCommand = ("sudo halt")
            print "echo:"+bashCommand
            os.system(bashCommand)
        elif (buttons & cwiid.BTN_1):
            print("BTN_1")
        elif (buttons & cwiid.BTN_2):
            print("BTN_2")
        elif (buttons & cwiid.BTN_A): # Shoot!!! :o)
            print("BTN_A")
            StopMotors()
        elif (buttons & cwiid.BTN_B): # Fire Missile!!! :o)
            print("BTN_B")
        elif (buttons == WII_BTN_UP):
            print("BTN_UP")
            Forwards()
            moving = True
        elif (buttons == WII_BTN_DOWN):
            print("BTN_DOWN")
            Backwards()
            moving = True
        elif (buttons == WII_BTN_LEFT):
            print("BTN_LEFT")
            Left()
            moving = True
        elif (buttons == WII_BTN_RIGHT):
            print("BTN_RIGHT")
            Right()
            moving = True
        elif (buttons == WII_BTN_UP + WII_BTN_RIGHT):
            FRight()
            moving = True
        elif (buttons == WII_BTN_UP + WII_BTN_LEFT):
            FLeft()
            moving = True
        elif (buttons == WII_BTN_DOWN + WII_BTN_RIGHT):
            BRight()
            moving = True
        elif (buttons == WII_BTN_DOWN + WII_BTN_LEFT):
            BLeft()
            moving = True
        elif (buttons & cwiid.BTN_PLUS):
            print("BTN_PLUS")
            if speed <= 90: speed = speed + 10 else: speed = 100 print("Speed: " + str(speed)) DutyCycleA = speed DutyCycleB = speed elif (buttons & cwiid.BTN_MINUS): print("BTN_MINUS") if speed >= 10:
                speed = speed - 10
            else:
                speed = 0
            print("Speed: " + str(speed))
            DutyCycleA = speed
            DutyCycleB = speed
        elif (buttons & cwiid.NUNCHUK_BTN_C): #
            print("NUNCHUK_BTN_C")
        elif (buttons & cwiid.NUNCHUK_BTN_Z): #
            print("NUNCHUK_BTN_Z")
        else:
            print("Nothing...")
            if moving == True:
                moving = False
                StopMotors()

        time.sleep(0.2)

    GPIO.cleanup()



# Set the GPIO to software PWM at 'Frequency' Hertz
pwmMotorAForwards = GPIO.PWM(pinMotorAForwards, Frequency)
pwmMotorABackwards = GPIO.PWM(pinMotorABackwards, Frequency)
pwmMotorBForwards = GPIO.PWM(pinMotorBForwards, Frequency)
pwmMotorBBackwards = GPIO.PWM(pinMotorBBackwards, Frequency)

# Start the software PWM with a duty cycle of 0 (i.e. not moving)
pwmMotorAForwards.start(Stop)
pwmMotorABackwards.start(Stop)
pwmMotorBForwards.start(Stop)
pwmMotorBBackwards.start(Stop)

# Set pins as output and input
GPIO.setup(pinTrigger, GPIO.OUT) # Trigger
GPIO.setup(pinEcho, GPIO.IN) # Echo

# Set the LED Pin mode to be Output
GPIO.setup(pinLED1, GPIO.OUT)
GPIO.setup(pinLED2, GPIO.OUT)

LED1Frequency = 2
LED2Frequency = 2

LED1DutyCycle = 0
LED2DutyCycle = 0

LED1 = GPIO.PWM(pinLED1, LED1Frequency)
LED2 = GPIO.PWM(pinLED2, LED2Frequency)

LED1.start(LED1DutyCycle)
LED2.start(LED2DutyCycle)

# Set trigger to False (Low)
GPIO.output(pinTrigger, False)

# Allow module to settle
time.sleep(0.5)


try:
    do_WiiRemote()

# If you press CTRL+C, cleanup and stop
except KeyboardInterrupt:
    # Reset GPIO settings
    GPIO.cleanup()


So all that remained was to get it to start at boot time.  This set of instructions works perfectly for me.

See previous post for BOM etc

Next time I’m bored or frustrated, I’ll have a go at updating cwiid to recognise newer versions of the Wii controller.


Just spotted this made it onto Adafruit’s blog.

Crawler

A colleague who works at Astro Designs and who I know from the Cotswold Raspberry Jam sent me his beta version of a Pi-Zero motor controller.  Since it was raining all weekend, and I’ve run out of stuff to do with Chloe and Zoe until Scance Sweep from Kickstarter appears, I decided to build and play with it.  I think it’s really cute (sorry about the blurred photo, no idea why since the camera was on a tripod).

Crawler

Crawler

It runs a python script at boot time, which polls for a Wii NumChuck bluetooth controller using the cwiid python library which is included in the Raspian Jessie-lite distribution.  Once joined, the buttons on the NumChuck control the movement of the robot, and also provide a safe shutdown at the end.  Or in theory, that’s how it will work.

The frame and motors are from pimoroni.  It’s powered by 4 AA batteries, with a LDO 5V regulator powering the Pi Zero.

The only problem I have is getting the CWiiD python library to connect to  the Raspian bluetooth driver.  For some reason, bluetooth hates me – I’ve binned several sets of keyboards and mice over the years as they either fail to connect, or forever keep dropping out on my windows desktop.

And that hatred continue – the simple test code below which has worked for others, fails completely returning “Socket connect error (control connection)”.  Google reports lots of historic occurrences but no solutions.

#!/usr/bin/env python2.7

import RPi.GPIO as GPIO # Import the GPIO Library
import cwiid
import time
import os


# Set the GPIO modes
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

# Set variable for the LED pin
pinLED1 = 5
pinLED2 = 6

# Set the LED Pin mode to be Output
GPIO.setup(pinLED1, GPIO.OUT)
GPIO.setup(pinLED2, GPIO.OUT)

try:
   do_WiiRemote()
except KeyboardInterrupt:
   GPIO.cleanup()
         

def do_WiiRemote():
   #connecting to the wiimote. This allows several attempts
   # as first few often fail.
   print 'Press 1+2 on your Wiimote now...'
   wm = None
   i=1
   while not wm:
      try:
         for x in range(0,i):
            GPIO.output(pinLED1, True)
            time.sleep(0.5)
            GPIO.output(pinLED1, False)
            time.sleep(0.5)
         GPIO.output(pinLED1,True)
         print "Bluetooth pairing attempt " + str(i)

         wm=cwiid.Wiimote()
      except RuntimeError:
         if (i>10):
            print("cannot create connection")
            quit()
         print "Error opening wiimote connection"
         i +=1

   GPIO.output(pinLED1, False)

   #set wiimote to report button presses and accelerometer state
   wm.rpt_mode = cwiid.RPT_BTN | cwiid.RPT_ACC

   #turn on led to show connected
   wm.led = 1

   buttons = wm.state['buttons']
   speed = 100

   while not(buttons == 140):
      buttons = wm.state['buttons']
      print("Buttons: " + str(buttons))
      time.sleep(0.2)

   GPIO.cleanup()

I am seriously p’d off – this was meant to be a quick 1 day project for a diversion from the Quadcopter stuff, but now it’s become a PITA in its own right.  I suspect it’s the bluetooth daemon in Jessie that’s refusing the connection from cwiid, and that means there’s not a lot I can do about it.


I’m going to track my investigations here.

The cwiid error is triggered here in cwiid-master/libcwiid/connect.c

	/* Connect to Wiimote */
	/* Control Channel */
	memset(&remote_addr, 0, sizeof remote_addr);
	remote_addr.l2_family = AF_BLUETOOTH;
	remote_addr.l2_bdaddr = *bdaddr;
	remote_addr.l2_psm = htobs(CTL_PSM);
	if ((ctl_socket =
	  socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1) {
		cwiid_err(NULL, "Socket creation error (control socket)");
		goto ERR_HND;
	}
	if (connect(ctl_socket, (struct sockaddr *)&remote_addr,
		        sizeof remote_addr)) {
		cwiid_err(NULL, "Socket connect error (control socket)");
		goto ERR_HND;
	}

	/* Interrupt Channel */
	remote_addr.l2_psm = htobs(INT_PSM);
	if ((int_socket =
	  socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1) {
		cwiid_err(NULL, "Socket creation error (interrupt socket)");
		goto ERR_HND;
	}
	if (connect(int_socket, (struct sockaddr *)&remote_addr,
		        sizeof remote_addr)) {
		cwiid_err(NULL, "Socket connect error (interrupt socket)");
		goto ERR_HND;
	}

	if ((wiimote = cwiid_new(ctl_socket, int_socket, flags)) == NULL) {
		/* Raises its own error */
		goto ERR_HND;
	}

	return wiimote;

ERR_HND:
	/* Close Sockets */
	if (ctl_socket != -1) {
		if (close(ctl_socket)) {
			cwiid_err(NULL, "Socket close error (control socket)");
		}
	}
	if (int_socket != -1) {
		if (close(int_socket)) {
			cwiid_err(NULL, "Socket close error (interrupt socket)");
		}
	}
	return NULL;
}

I need to dig further as to why the socket to the bluetooth daemon / driver failed.


A more precise google search turned up a Bluetooth kernel driver problem – perhaps this is it?

B3 bluetooth keyboard

I bought an Anker bluetooth keyboard for my B3 piPad, and have spent 10 days periodically swearing at it trying both the CLI or GUI app.  Finally, I found a complete set of instructions on the RPi forum that worked.  Type following commands from a console:

  1. bluetoothctl
  2. pairable on
  3. scan on
    Now you have to wait, until your BT device (keyboard) is shown (don’t forget to activate your keyboard now! Press the so called “pairing button” of your BT keyboard, may be a combination of two keys, see the users manual!). Don’t forget to wait!!! You should see it’s address as “xx:xx:xx:xx:xx:xx”
  4. scan off
  5. agent on
  6. pair xx:xx:xx:xx:xx:xx
    Now you are requested to input a number (6 digits) using your BT keyboard, and then you have to quit it by pressing the “return” button of your BT keyboard! “pairing successful” should be displayed!
  7. trust xx:xx:xx:xx:xx:xx
  8. connect xx:xx:xx:xx:xx:xx
    “connection successful” should be displayed!
  9. info xx:xx:xx:xx:xx:xx
    … will show the success again (some Information are displayed).
  10. exit

Reboot your RPi now.