Search This Blog

Monday, 31 March 2014

Controlling a Raspberry Pi / Arduino Bot from the Internet Part 2 of 3

This is part two of a three part Series to explain how I control my BotTwo semi-Autonomous Robot from a webpage on the Internet. 

In Part One, we talked about the Interaction and Communications between the end user device (laptop/tablet/phone), the Web Server presenting the control panel, and the socket service listening for commands on the Robot itself.

This series will allow you to operate your Robot like an RC Car via the Internet.



This is the mode described here, with code examples for each programming platform.  As part of my learning process, I chose a very simple DIY approach, as opposed to a framework like node.js 


Raspberry Pi Arduino I2C
Raspberry Pi / Arduino I2C



In this posting, we will discuss how to use python on the Raspberry Pi to initialise a tcp socket listener for incoming commands from the Web Server.

We will take the incoming message, and repeat it via I2C to the Arduino that is managing the DC motors and wheel encoders.

        Note:  typically, there would be a validation step between receiving the command, and issuing it                      to the motor controllers.

You would want to ensure that the command made sense, but also ensure that it was not going to put the robot into danger.  This would include proximity / obstacle detection, as well as "cliff detection". In more advanced robots, you may also have environmental sensors that could ensure that the path chosen was safe to travel.

Ok... so... in my implementation, I rely heavily on the Adafruit Raspberry Pi python library for I2C communications both for existing I2C sensors, as well as for communicating with my Arduino's.  I fully admit to replicating an existing I2C sensor library, and then making it work with my motor controller Arduino.

Adafruit_I2C.py  provides a number of methods for sending and receiving data via I2C, in 8bit bytes, 16bit integers, or character array (python list) form.

I'm not going to explain how to prepare your Pi for I2C... 


 I had looked at Quick2Wire, and WiringPi, but their implementations is based on python3, whereas it appears that more I2C connectivity libraries exist for python 2.x via Adafruit.

Here is my stripped down "library" for sending a command string to the Arduino Motor Controller via I2C.

Motion.py 
#!/usr/bin/python

# Python library for Arduino as I2C Motor Controller.

# Copyright 2014 Michael Ball  unix_guru at hotmail dot com

from Adafruit_I2C import Adafruit_I2C

class Motion(Adafruit_I2C):

    # Minimal constants carried over from Arduino library

    MOTION_ADDRESS          = 0x33 # I2C Device Address
    MOTION_DATA             = 0x00 # Sensor data 


    def __init__(self, busnum=-1, debug=False):
        self.move = Adafruit_I2C(self.MOTION_ADDRESS, busnum, debug)


    def getMotionData(self):
        return self.move.readU8(self.MOTION_DATA) # & 0x0F


    # Read the Motion sensors
    def read_sensors(self):
        raw = self.move.readList(self.MOTION_DATA, 8)
        res = []
        for i in range(0, 8, 2):
            g = (raw[i] << 8) | raw[i+1] 
            res.append(g)
        return res

    # Send Commands to Motion Controller
    def write_command(self, command):
err = self.move.writeList(self.MOTION_ADDRESS, map(ord,command))
return err

# Simple example prints wheel encoder data and then sends a 
# "Forward 200mm" command every 2 seconds:
# Please note that sending a string via I2C bus is not the most 
# efficient use of this bus.  
# Data register method will be shown in an upcoming article

if __name__ == '__main__':

    from time import sleep

    motion = Motion()

    print '[Motion lpulse/rpulse]'
    while True:
        print motion.getMotionData()

        print motion.read_sensors()
sleep(.5)
print motion.write_command("f, 200 \n\r");
        sleep(2)  

With no further ado... here is my python "command repeater"   aka tcp socket listener-I2C Master.
#! /usr/bin/python
#
#  Process_Socket_Commands.py   Michael Ball Feb 2014
#  Manage command/response between Arduino Motor/Sensor controller, 
#  and Raspberry Pi intelligent controller.
#  Using tcp socket listener to receive commands from end user, 
#  and I2C to communicate with Arduino

import socket               # Import socket module
import time
import thread

from Motion import Motion



####################################################################
# Global Variables
cmd_received = 0
rcvdCommand = ""


####################################################################
# Declare functions

# Retrieve commands from tcp socket if available, and send to I2C
def get_next_command():       

  while True:
    # Inside processing loop we wait for a connection
    client_socket, address = server_socket.accept()
    print 'Got connection from', address
    client_socket.send('Thank you for connecting')
    rcvdCommand = client_socket.recv(1024)

    # rcvdCommand is a comma separated string from the Webserver
    # that consists of a single character Command, plus a Parameter
    # Move forward 200mm  would be represented as "f,200\n\r"
    print rcvdCommand

    # Normally, you would insert validation checking here, 
    # like proximty / obstacle avoidance, or cliff checking


    #send command to arduino
    motion.write_command(rcvdCommand)
    rcvdCommand = "" # Clear the command 
    client_socket.close()               # Close the connection


def get_motor_status():

   while True:
    # Read Wheel Encoder info from arduino 
    print "Motion, LCount, RCount, Speed"
    print motion.read_sensors()
    time.sleep(1)


            
##########################################################################
#
#This is where all the good stuff starts...
#
print 'Arduino Bot Command Processor - Feb 2014'

#  Create and open the socket will be listening on
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("192.168.0.105", 5000))
server_socket.listen(5)

print "Running Socket Server"

# initialize I2C Arduino motor controller - 
# Note there are no condition checks here
# a robust implementation would ensure that 
# the motor controller was actually online
motion = Motion()        

print "Arduino I2C Motor Controller Initialized"


try:
    running = True
    # Start up a Command tcp socket listener thread
    thread.start_new_thread(get_next_command, ()) 

    # Start up a thread to receive feedback from the Arduino
    thread.start_new_thread(get_motor_status, ())
    while 1: # Continuous loop
       pass

  


except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
    print "\nKilling Thread..."
    # db.close()
print "Done.\nExiting."

Please read the comments.  This is a bare-bones implementation that takes a command string, and forwards it to The arduino via I2C for processing.   

You will need to add your proximity and safety testing to this.  As well, sending a string via I2C is not the most efficient use of the bus.   I simply did this so that I had both options available on the Arduino.
A proper Data Register method will be shown in an upcoming article.

As you will see in the next segment of this series,  I share the Command Interpreter / processor between the serial port and the I2C.  Commands can be received via either.




References:

http://dsscircuits.com/index.php/articles/78-arduino-i2c-slave-guide
http://gammon.com.au/i2c
Adafruit: Configuring the Pi for I2C
http://blog.oscarliang.net/raspberry-pi-arduino-connected-i2c/
https://wiki.python.org/moin/TcpCommunication
https://docs.python.org/3/howto/sockets.html
https://docs.python.org/2/library/socketserver.html
http://www.lucidtronix.com/tutorials/16
http://www.instructables.com/id/How-To-Make-an-Obstacle-Avoiding-Arduino-Robot/
https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code
http://en.wikipedia.org/wiki/I%C2%B2C
http://www.robot-electronics.co.uk/acatalog/I2C_Tutorial.html
http://www.penguintutor.com/linux/raspberrypi-webserver
http://www.instructables.com/id/Raspberry-Pi-I2C-Python/?ALLSTEPS
http://raspberrypi4dummies.wordpress.com/2013/07/18/raspberry-pi-master-controls-arduino-uno-slaves-via-i2c/

Also read:

http://quick2wire.com/category/python/
https://github.com/quick2wire/trackbot
http://blog.chris.tylers.info/index.php?/archives/274-New-Pidora-Package-quick2wire-python-api.html
http://think-bowl.com/raspberry-pi/installing-the-think-bowl-i2c-libraries-for-python/
https://projects.drogon.net/raspberry-pi/wiringpi/i2c-library/