Search This Blog

Monday 31 March 2014

Speeding up the I2C bus on Raspberry Pi and Arduino

From Wikipedia

The I²C reference design has a 7-bit or a 10-bit (depending on the device used) address space.[3] Common I²C bus speeds are the 100 kbit/s standard mode and the 10 kbit/s low-speed mode, but arbitrarily low clock frequencies are also allowed. Recent revisions of I²C can host more nodes and run at faster speeds (400 kbit/s Fast mode, 1 Mbit/sFast mode plus or Fm+, and 3.4 Mbit/s High Speed mode). These speeds are more widely used on embedded systems than on PCs.
So by default the I2C bus is running at 100kHz. Not bad, but no blazing speed demon either. 

Most I2C devices can readily run at 400khz, if you keep the lines short.

On the Raspberry Pi, the bcm2708 chip manages I2C (smbus) 

#define I2C_TIMEOUT_MS 150
#define DRV_NAME "bcm2708_i2c"
static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE;
module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(baudrate, "The I2C baudrate");
To increase the speed on the Raspberry Pi, you can pass an optional baudrate parameter via modprobe to the I2C module
                                                                         sudo modprobe -r i2c_bcm2708 && sudo modprobe i2c_bcm2708 baudrate=400000  
To make that change permanent, you have to create a new i2c.conf file:
1
2
3
4
5
sudo nano /etc/modprobe.d/i2c.conf
    
type    options i2c_bcm2708 baudrate=400000
                                                             ctrl-x to save (answer yes)
This will force your Raspberry Pi to initiate communications at 400k baud.





On the Arduino side, you have to locate the file twi.h 

sudo nano /usr/share/arduino/libraries/Wire/utility/twi.h
find the string “#define TWI_FREQ 100000L” and change it to:
1
#define TWI_FREQ 400000L (was 100000L)

This should speed up your I2C communications sufficiently, without breaking anything.



References:

http://en.wikipedia.org/wiki/I%C2%B2C
http://www.raspberrypi.org/phpBB3/viewtopic.php?t=18852
http://neophob.com/2013/04/i2c-communication-between-a-rpi-and-a-arduino/
http://raspberrypi.znix.com/hipidocs/topic_i2cdev.htm
https://github.com/raspberrypi/linux/blob/rpi-3.6.y/drivers/i2c/busses/i2c-bcm2708.c#L73

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/






Sunday 30 March 2014

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

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

BotTwo has a few modes of operation.  

  • In one of his Autonomous Modes, he can wander about aimlessly, simply avoiding objects, all the while building or refining a map of his surroundings for future use.  
  • A second function in his Autonomous Modes allows you to select a location from his floorplan map, and he will find the shortest path to it, using A* algorithm. 
  • In Manual Mode, you can drive him like an RC Car from a webpage. 



This is the mode that I will describe 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 




This example starts with a HTML page that uses javascript to pass a Command variable and associated Parameter variable to a php script.
The php script opens a simple Web Socket to the Raspberry Pi on the Robot, and passes the command to it.  

This is just one of many ways to do this, but it works well for me, and may be a starting place for you.
I chose to run my webserver (Apache/Php/MySQL)  on a dedicated computer, as my Raspberry Pi is up and down as I'm working on it, or as the battery dies.   You may chose to run the webserver on your Pi.   

Here will present the HTML page, along with it's associated javascript, and php scripts that enable sending a command in real-time from a Button Control panel on your laptop/tablet/phone to your robot.





This HTML document simply sets up an imagemap of a button console, and attaches javascript actions to the area shapes that comprise the buttons:   

<area shape="circle" 
    coords="43,43,32"      
    onClick="sendCommand(4,0);"
    onMouseOver="showButtons('Look Left');" 
    onMouseOut="showButtons('');"/>

A slider Control or  HTML5 <input type= "range"> is used to send speed control messages using the same methods described below.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en">
<head>
    <title>Control Testing for Raspberry Pi/Arduino Autonomous Platform</title>
    <meta http-equiv="Content-Type"
    content="text/html; charset=iso-8859-1" />
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" type="text/javascript"></script>
    <script src='autonomous.js'></script>

</head>

<?PHP include("insert_commands.php"); ?>

<body  onload='init();' >    
<center> 
     <H1> Control Panel for Web Guided Robot </H1>
     <BR> <HR> <BR>

   <center><b>Navigation Controls</b></center>
   <div> 
  <center>
<style>
.help {
   background-color: #FFFF73;
   border-radius: 10px;
   display: none;
   opacity: 0.9;
   padding: 10px;
   z-index: 100;
}

.help_link:hover + span {
   display: inline;
}
</style>


<form name="myform">
  <center><b>Action:</b> <input type="text" name="stage" size="10" />
      <b>Param:</b>   <input type="text" name="param" alt="Param" id="param" size="3" value="100"/> 
      <br><a href="#" class="help_link">Help</a> 
<span class="help">Distance you want to travel in mm.</span><br />
      <b>Speed:</b>  
<input type="range" id="speed"  value="50" min="0" max="100"  onchange="changeSpeed();"/>
      <div id="uno">&nbsp;</div>
  </center>
</form>

<!-- Create  Mappings -->
<img src="button_box_new.png" alt="Navigational Buttons" 
border="0" usemap="#buttons"/>

<map name="buttons">

  <area shape="circle" 
   coords="43,43,32"
    
   onClick="sendCommand(4,0);"
   onMouseOver="showButtons('Look Left');" 
   onMouseOut="showButtons('');"/>
 
  <area shape="circle" 
   coords="130,43,32"
   onClick="sendCommand(6,0);"
   onMouseOver="showButtons('Look Ahead');" 
   onMouseOut="showButtons('');"/>
 
  <area shape="circle" 
   coords="217,43,32"
   onClick="sendCommand(5,0);"
   onMouseOver="showButtons('Look Right');" 
   onMouseOut="showButtons('');"/>


  <area shape="circle" 
   coords="43,130,32"
   onClick="sendCommand(8,135);"
   onMouseOver="showButtons('Look Up');" 
   onMouseOut="showButtons('');"/>
 
  <area shape="circle" 
   coords="130,130,32"
   onClick="sendCommand('f',100);" 
   onMouseOver="showButtons('Move Forward');" 
   onMouseOut="showButtons('');"/>
 
  <area shape="circle" 
   coords="217,130,32"
   onClick="sendCommand(6,45);"
   onMouseOver="showButtons('Look Toward');" 
   onMouseOut="showButtons('');"/>

  <area shape="circle" 
   coords="43,217,32"
   onClick="sendCommand('l',100)"
   onMouseOver="showButtons('Turn Left');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="130,217,32"
   onClick="sendCommand('x',0)"
   onMouseOver="showButtons('Full Stop');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="217,217,32"
   onClick="sendCommand('r',100)"
   onMouseOver="showButtons('Turn Right');" 
   onMouseOut="showButtons('')"/>


  <area shape="circle" 
   coords="43,304,32"
   onClick="sendCommand(2,5)"
   onMouseOver="showButtons('Sonar Scan');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="130,304,32"
   onClick="sendCommand('b',100)"
   onMouseOver="showButtons('Move Backward');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="217,304,32"
   onClick="sendCommand(3,5)"
   onMouseOver="showButtons('Sonar Sweep');" 
   onMouseOut="showButtons('')"/>

  
  <area shape="circle" 
   coords="43,391,32"
   onClick="sendCommand(10,0)"
   onMouseOver="showButtons('Sleep/Wake');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="130,391,32"
   onClick="sendCommand(31,10)"
   onMouseOver="showButtons('Random Walk');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="217,391,32"
   onClick="sendCommand('t',5)"
   onMouseOver="showButtons('Turn Toward');" 
   onMouseOut="showButtons('')"/>

</map>

</center>
   </div>

      <center>
        <p>Copyright 2013 Michael Ball 
            <a href="http://arduino-pi.blogspot.com" target ="new">Arduino-Pi</a> 
            Email:  <a href="mailto:unix_guru@hotmail.com?Subject=Arduino-Pi" target="new">unix_guru@hotmail.com</a></p>
      </center>

</body>
</html>
On action, the Command and Parameter values are passed to php  via the sendCommand  function.

// #########################################################################

// autonomous.js


function showButtons(name){     / Show imagemap buttons values for controlling robot
  document.myform.stage.value = name;
}

function changeSpeed(){  // Slider control for managing robot speed 
    var speed=document.getElementById("speed");
    $.get("insert_commands.php", { command: 18, parameter: speed.value },                       // In my case '18' is the command
       function(command,parameter){  }, "json");      // to change motor speed
}
// This function passes both the Command and Parameter Variables from the Image map       // to a serverside PHP Script which will create a Websocket to the Robot. 
function sendCommand(command,parameter){                                          
    var parameter2=document.getElementById("param");

    if(parameter2 > 0) parameter = parameter2.value;    // Override default
    $.get("insert_commands.php", { command: command, parameter: parameter2.value },
       function(command,parameter){}, "json");
}

function init() {
    // Initalize the various page elements here...
    // in this case it is a dummy function...   
}
The php script accepts the Command and Parameter values, formats them into a string, and passes them to the Raspberry Pi, via a Web Socket. 
<?php
/*  
# BotTwo "insert_commands.php" Takes a Command/Paramater pair and sends 
# them to A listening robot via Web Socket
# 2014 Michael Ball  unix_guru at hotmail dot com

/*** robot address ***/
$robot_addr = '192.168.0.105';

/*** robot socket port ***/
$robot_sock = '5000';
if (isset($argv)) { // If running from the command line
   $command = $argv[1];
   $parameter = $argv[2];
}
else { // Else if called from a Web Page
   $command = $_GET['command'];
   $parameter = $_GET['parameter'];
}

// Initialize a Command string from the values passed in 'Command' and 'Parameter'
        $sock_cmd = $command . ", " . $parameter . "\n\r";  

if (isset($command)) { // If a command is available

// Create a TCP/IP socket to Robot
// http://www.php.net/manual/en/function.socket-create.php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

if ($socket === false) {
   return "Error: socket_create() failed: reason: " .
       socket_strerror(socket_last_error());
}
// Connect to the server running on the 'bot at  $robot_addr:$robot_sock
$result = socket_connect($socket, $robot_addr, $robot_sock);
if ($result === false) {
   return "Error: socket_connect() failed.\nReason: ($result) " .
       socket_strerror(socket_last_error($socket));
}

// Write the command string to the Robot
socket_write($socket, $sock_cmd, strlen($sock_cmd));
socket_close($socket);
}
?>

As I stated, this is an example... If you wish for me to go further into detailed explanation,   please comment below.


In the next article (2 of 3), I will describe how to receive and parse the command in python on the Raspberry Pi and forward it to an attached Arduino via the I2C bus.


And finally in article 3 of 3, I will describe the Arduino code that receives the I2C command, and turn it into motor actions to steer and run the robot. 

Edit (05/13/2014):  here is the button_box image... it's missing from my github... 



Resources:

https://code.google.com/p/phpwebsocket/
http://www.w3schools.com/tags/att_area_shape.asp
http://letsmakerobots.com/node/39791
http://www.php.net/manual/en/function.socket-create.php
http://www.php.net/manual/en/sockets.examples.php