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?

Yes Sir, I can boogie!

I’ve been meaning for ages to get my turtle* project back up and running, and having put the quad siblings to bed for a while, I’ve finally had time.

Turtle

Turtle

By using a B+, I finally have enough GPIO pins to drive the stepper motors directly via 8 pins and mosfets rather than using the 74HC595 serial to parallel latch.  That’s simplified the circuitry and software a lot.  I’ve also turned the turtle into a WAP.  Finally, I’ve swapped batteries from unregulated 2S LiPo to a 2.4A phone charger bank.  This gives just about enough power – the 2.1V battery bank shown in the picture didn’t work – the motors drew enough current to trigger a safety switch in the battery.

I’m hoping to do a tutorial at the next Cotswold Jam, starting at a flashing LED and ending in a robot in 10 easy steps.  In the meantime, just watch her boogie!

Turtle boogie! from Andy Baker on Vimeo.

P.S. For a giggle, watch the original “Yes Sir, I can boobie” on youtube.


*A turtle should really have a pen controlled by a solenoid to leave a trail as it wanders. A more useful implementation for the modern era would be as the base for a laser cutter, or 3D printer: consider attaching the stepper motors to a fixed base at 90° to each other independently manoeuvring a suspended platform around underneath a fixed position laser or molten plastic feeder?  It’s only a different LEGO layout from what the turtles doing now.

In fact, my next project, piPlotter, has just been conceived, using the turtle motors to move a Raspberry Pi 7″ touch screen running a draw application with a stylus touching the screen to draw out the movement.  More of a LEGO project than a RPi project, and a lot more fun as a result.  Oh, and cheap too!

The end of a project…

The first project I did was a turtle, with a Lego frame, Raspberry Pi plus 74HC595 serial parallel latch, ULN2801A darlington ampplifiers powering two stepper motors, with a Wifi connection to another RPi application where you can type in commands.  It was all powered by a 7.4v  Lithium Ion battery via a LDO Switching regulator.

This was done very much step by step, using much of the same circuitry from 25 years ago when I did one at school driven by a BBC Micro, starting first with driving LEDs, then progressing to steppers and then finally seating it in a Lego Techic body.

My Turtle

My Turtle

The commands are sent over a TCP connection from the control machine to the turtle.  The turtle was powered by a LiPo battery.  If you want more details, click on the TurtlePi category on the right.

I ended up buying lots of little bits of Lego from ebay to get the best combination of gears etc to ensure the large torque needed by the wheels didn’t break the gears or their axles.  But one piece didn’t exist: a flat yellow flat dome to drag on the floor under the batteries.  At some point, I got hold of a clear yellow one which was ‘good enough’ for the moment but my OCD – every programmer must have OCD to some extent – wouldn’t let it go until I had the correct one in the matching opaque yellow as the rest of the Turtle.  One turned up on ebay the other day – I’d kept the watch running – and I couldn’t believe it!

Compromised and Correct Lego domes

Compromised and Correct Lego domes

So now I’m a very happy person, and can concentrate 100% on Phoebe!

P.S. I had intended to show a video here, but as I powered up the Turtle, the circuitry heated up, and I think I’ve just blown the 3 stacked ULN2801 darlington drivers – hopefully the 74HC595 survived .  Definitely the end of the project for the mo’ though! Oops

No Hesitation, Repetition or Deviation

Although neither the drone nor alarm pi projects are even phase 1 complete (phase 1 for the drone is safe automaton takeoff, hover and land, and for the alarm is independent alarm control), I’m strongly considering moving from the RPi.GPIO library to the RPIO library for a number of reasons:

  • RPIO interrupts can be used to wake socket.select() calls, whereas RPi.GPIO runs a separate thread which can only wake a socket.select() on the make thread by sending a SIGINT – functional but ugly
  • RPIO supports hardware PWM across any GPIO port whereas currently, I use I2C to connect to a PWN breakout chipset

While using the RPi.GPIO works fine, RPIO just feels better. So once phase 1 of both projects are complete, I’m going to add phase 1.5 which is the switchover to RPIO (or perhaps merge the best of both).

In passing, I’m also considering moving over to PiPi once it’s available in the Wheezy distro – the drone is running full speed currently with no sleeps. Moving to PiPi means I can start introducing time.sleep() as the precursor to TCP socket inputs via socket.select(). Currently the space in the CPU cycles from using interpreted Python is feeling a bit small to stably introduce remote control.

A Porky Pi

Ok, so I’ve been a bit light on the truth; the turtle has a problem.  Much of the time, the turtle reboots as soon as I run the turtlesvr python script.  Occasionally however, it works just fine.  After swearing alot, it became apparent that with a fully charged battery, supplying ~ 8.4v, all was fine.  However as the battery discharges to lower output (but still plentiful to power up the RPi via the 5V GPIO port) it rebooted.

Then I noticed (with the motors replaced by LEDs) that on booting the Turtle, all LEDs flashed together for a fraction of a second before going out again.  Some serious speculation has led me to this conclusion:  as part of the python code / electronic boot sequence, all outputs are set high (or are floating high) just for a fraction of a second on the 74HC595 chip, before settling – that’s a bug I’m in the process of solving still.  But why did that cause an RPi reboot?

My supposition is that, with the motors attached, the “flash of LEDs” is the equivalent of powering up all 8 stepper motor coils thus drawing up to 10A!!!!!!  With the battery charged, it could (just) cope because the switching regulator could convert the excess voltage above 5V  to extra available current; hence all was well.   However…

With the battery down by a few tenths of a volt, the extra current is being drawn directly from the battery rather than the regulator, and this leads to the battery protection circuitry kicking in (to prevent explosions and other unwanted symptoms), thus the power vanishes for a brief interval before shortly being restored, but rebooting of the RPi as a bi-product..

The more I think about this, the more I’m convinced this is the problem.  So all I need to do now is work out why all LEDs are engaged for a flash at turtlesvr boot up.  This could be python, electronics or just timing at heart, but with the LEDs proving to be great diagnostic tools yet not rebooting the TurtlePi, I at least stand a chance of solving the problem.

To cut a long storey short, it was the master reset pin on the 74HC595 causing the problem.  I’d pulled it high with a reisitor thus not causing a master reset on power up; in turn that meant that when the initialization of the chip happens, any old junk inside would be used to operate the motors – this turned out to be all bits set, leading to the 10A draw, and immediate battery cut-out.

So, the fix involves another GPIO output pin wired to the 74HC595 MR pin, and a 10k pull-down resistor.  The resistor means the master reset happens on power up, and the GPIO pin can then clear the master reset once the turtlesvr code is running.  Hence another tweak to the turtlesvr code to drive that new GPIO pin.  Code to follow shortly, and I’ll try to put together a circuit diagram.

 

Wrong again: the problem is that is wasn’t possible to preset the value of a GPIO port prior to setting it to an output.  So the default was 0, and on the /OE pin, this meant that motors coils were engaged prior to feeding the desired values over the serial interface for those motors to take.  By bad luck, it engaged all coils and tripped the safety cutout in the battery.  So the Python could be fixed in my case as I could keep /OE high through a pull-up resistor until I’d fed the motors data into the data registers.  Only then could I safely set /OE as an output and set it LOW. There was no need for the /MR pin to keep the chip reset.

Having said that,  the problem wouldn’t have occurred were it possible to preset the pin output values prior to setting them to be outputs.  Hopefully a fix will come for the GPIO Python library, but for now, it’s not a necessity for me and my Turtle.

 

Turtle Pi Math(s)

So I’ve got a Turtle under my command, but the commands are all in steps of the motor – such as “move fwd 100” or “turn acw 37”.  I’d prefer the units to be meaningful approximations to reasonal units like millimeters or degrees, so that needs some basic maths.

The distance between the wheels is 160mm – let’s call it D.  The diameter of the wheel is 82mm – let’s call it d.  The motors performs 1 full rotation (360 degrees) in 200 steps.

So forward and backward are relatively simple math(s): distance travelled in millimeters, m = (n x π x d) / 200.  So to travel m millimeters, we need n = m x 200 / (π x d) steps.

Turning on it’s central axis is slightly trickier: n steps travel a distance of (n x π x d) / 200 as above.  But now this represents a partial arc of the circle the turtle is turning in – lets call that angle θ (theta).  So (θ x π x D) / 360 = (n  x π x d) / 200.  Cancelling the π’s leads to (θ x D) / 360 = (n x d) / 200.  Or for a given angle to turn, the number of steps is (θ x D x 200 / (d x 360)).

Finally, I also have a “sweep” action where one wheel is locked, and only the other moves. This effectively doubles D as now the radius of the circle is D rather than the diameter.  So the steps swept to move through an angle theta is now (θ x 2 x D x 200 / (d x 360)).

Having got the number of steps, all that’s left to do is to convert them from floating point to integers allowing for rounding errors.  I’ll post the updated code containing these changes, and changes related to the next post…

 

TurtlePi code

As promised several times, here’s the python code for my turtle command line interface (turtlecli) and turtle server (turtlesvr). I’ve saved them as text files so your OS won’t moan so you’ll need to rename them as .py or no suffix and “chmod +x” them.

To run the turtle code, just type “sudo ./turtlesvr”.  The Turtle serve code just sits passively on the turtle waiting for a command from the CLI.

The turtle command line interface (CLI) runs on another Pi, and accepts type commands, which it passes to the Turtle to enact.  Here’s a few syntax examples:

  • move fwd 100
  • move bck 38
  • turn cw 189
  • sweep acw 97
Simples!
P.S. This is beginners python – please comment if you see bugs.
P.P.S The units in the commands above are steps of the motor.  How that corresponds to millimeters or degrees depends on the radius of the wheels and their separation. My by luck gives me about 90 degree turn for 100 steps.

 

To infinity and beyond!

But where exactly to go next, now that the TurtlePi is essentially rolling on command? I’ve got some lego tinkering and some polishing of the python to do, but that’s it. Or is it?…

  • I’m starting to build up a order with my favourite electronic suppliers to assemble the electronics necessary for home grown voice / speech recognition, so I can boss the turtle around verbally
  • I’d considered Quadracopters as my Pi in the Sky project, but that’s living up to its name requiring cashflow.
  • In the meantime, I seem to have acquired an excess of RPis so I’m waiting to see the Pi Screen for use as a portable media player; the other as yet has no role to play.

For the moment though, it’s voice recognition using a 12 bit ADC, via the SPI bus to the Pi, and then all I have to do is to invent my own version of voice / speech recognition algorithms. Voice first (who is talking?), and then speech (what are they saying?). Certainly the latter will likely be dictionary based upon teaching / caching the Turtle commands for everyone who wants to use it. But first, I want to get the electronics and SPI bus working – that’s the stuff I love most.

Finally, when I finally finish fine tuning, I’ll see if I can get a video up here.

TTFN!