How to.... interface the RPi with spirit board

The more technical aspects of Spirit, and carry-over discussion from Kickstarter updates.
mgodiya
Posts: 5
Joined: Tue Oct 31, 2017 3:45 am

How to.... interface the RPi with spirit board

Postby mgodiya » Mon Nov 27, 2017 1:26 am

Hello there,

I wrote a small program that lets you play the Buzzer on the rover board through RPi using i2c bus. I'm still working on SPI bus to make other attached devices.Before you run the program you need to follow the below steps. tested OK on Raspian Jesse(dont know about NOOBS).

1. Expand file system & turn on I2C and SPI
sudo raspi-config

    Advanced Options > Expans Filesystem
    Interfacing Options > Enable SPI and I2C

Reboot the Pi

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install i2c-tools libi2c-dev python-smbus
sudo vi /etc/modprobe.d/raspi-blacklist.conf
Place a hash ‘#’ in front of blacklist i2c-bcm2708
If the above file is blank or doesn’t exist, then skip the above step

sudo vi /etc/modules
Add these two lines;
    i2c-dev
    i2c-bcm2708

Update /boot/config.txt
sudo vi /boot/config.txt
Add to the bottom;
    dtparam=i2c_arm=on
    dtparam=i2c1=on
sudo reboot
Once your Raspberry Pi reboots, you can check for any components connected to the i2c bus by using i2cdetect;
sudo i2cdetect -y 1
After the above command, if you see the following output, you have done everything right.
pi@moebot:~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- 1c -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- 32 -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- 6b -- -- -- --
70: -- -- -- -- -- -- -- --

on your RPi, create a file called testdemo.py and paste the code below

Code: Select all

import smbus
import time
bus = smbus.SMBus(1)
address = 0x32
reg_buzzer = 41
serv_def_value = 90
serv_values = [150,150,150]
reg_serv_values = [52,53,54]
serv_inc_value = 10
x=0
direction=1#0 for dec and 1 for inc
def writeBytes(value,reg_val):
   bus.write_i2c_block_data(address ,reg_val,value)
   return -1

def writeByte(value,reg_val):
   bus.write_byte_data(address,reg_val,value)
   return -1

def readNumber(reg_val):
   number = bus.read_byte_data(address,reg_val)
   return number

def highbyte(num):
   hex_num = hex(num >> 8)
   dec_num = int(hex_num,16)
   return dec_num

def lowbyte(num):
   hex_num = hex(num & 0xFF)
   dec_num = int(hex_num,16)
   return dec_num

def playbuzzer(freq):
   data = [highbyte(freq),lowbyte(freq)]
   writeBytes(data,reg_buzzer)

while True:
   time.sleep(1)
#   playbuzzer(1000)
#   time.sleep(1)
#   playbuzzer(0)
   if(direction==1):
      serv_values[x % 3]=serv_values[x % 3]+serv_inc_value
   else:
      serv_values[x%3]=serv_values[x%3]-serv_inc_value
   if(serv_values[2]>=180): direction=0
   elif(serv_values[2]<=1): direction=1
   writeByte(serv_values[x%3],reg_serv_values[x%3])
   print serv_values
   x = x + 1


run the program by typing python testdemo.py. The program will make the buzzer on and off every second and increase the servo by 10, when the servos reach the max limit, it will start subtracting by 10. I havent used the function 'readNumber' yet, but it works similarly. For more info goto http://www.raspberry-projects.com/pi/programming-in-python/i2c-programming-in-python/using-the-i2c-interface-2 or reply back to this post and I will try my best to answer your questions.

PS: Sorry for the poor formatting..

Kevin
Posts: 179
Joined: Tue Jul 28, 2015 12:56 am

Re: How to.... interface the RPi with spirit board

Postby Kevin » Tue Nov 28, 2017 1:28 am

As soon as we finish shipping I'll have a proper guide with a library for the Pi so everyone can start to really play with this. Thanks for sharing!!

-Kevin

odinshen
Posts: 3
Joined: Sat Oct 14, 2017 5:40 pm

Re: How to.... interface the RPi with spirit board

Postby odinshen » Sun Dec 03, 2017 2:39 pm

Thanks for your share, mgodiya

I had tried today, sometime I can send buzzer command from Python, but more of time I saw IOError message during the 1st I2C command, have you saw that also? Any idea?

Traceback (most recent call last):
File "sr_python_test.py", line 49, in <module>
writeByte(serv_values[x%3],reg_serv_values[x%3])
File "sr_python_test.py", line 17, in writeByte
bus.write_byte_data(address,reg_val,value)
IOError: [Errno 121] Remote I/O error

Kevin
Posts: 179
Joined: Tue Jul 28, 2015 12:56 am

Re: How to.... interface the RPi with spirit board

Postby Kevin » Tue Dec 05, 2017 2:35 am

The Pi's I2C hardware has some bugs. This is apparently related to the chip used on the Pi and it caused all kinds of problems and hassles getting things to work correctly. For most of the Pi to Pic I2C communications, we need to add a slight delay (I think it's a few microseconds if I remember correctly) before the PIC responds back. There is a function in the library we wrote to deal with this and only needs to be called once. This together with the rest of the pre-written functions in our Python library makes all the coms real easy. I'm going to get that posted with some instructions soon. Almost done shipping these guys. That's the big focus right now then we'll have the Pi stuff posted. Hang in there. It should work smoothly once that's posted.

Thanks!
-Kevin

mgodiya
Posts: 5
Joined: Tue Oct 31, 2017 3:45 am

Re: How to.... interface the RPi with spirit board

Postby mgodiya » Wed Dec 06, 2017 12:50 am

Thanks for the details Kevin, we are patiently waiting for your python library.
Meanwhile odinshen, you could try the following to make the above script work.
1. Make sure you are following the steps above correctly despite my poor formatting :)
2. Make sure I2C and SPI are enabled on the Pi
3. As Kevin mentioned, try uncommenting the 3 lines in the script (it will give the pi more time to respond)

let me know how you make out.
Sorry for the late reply.

PS:I am pretty sure your problem can be solved using 1st or/and 2nd solutions.

User avatar
Astroghost
Posts: 8
Joined: Tue Oct 24, 2017 10:35 pm

Re: How to.... interface the RPi with spirit board

Postby Astroghost » Thu Dec 14, 2017 11:33 am

Hi all,
Have a special robotic day in ower Fablab tomorrow , , i was expecting that we get all information from Kevin to control the robot by BLE and remote controller , but nothing come at this day.
Bad news that i would not be able to present the spirit rover :cry:

Do someone know how to send data to serial via BLE , i've actually programmed the HC-06 BLE module to work at 57600 and connecting it with my smartphone , but spirit rover don"t received the command i send.
BERNIER François
FabLab Manager at L.A.B. Aix en Provence -FRANCE
http://astronomie-astrophotographie.fr

User avatar
esba1ley
Posts: 58
Joined: Sun Jan 01, 2017 2:15 am
Location: Pasadena, CA
Contact:

Re: How to.... interface the RPi with spirit board

Postby esba1ley » Mon Jan 22, 2018 3:41 am

Does anyone have a list of what's available at what addresses on the i2c in addition to what was in the python test code above? A handy reference would be nice...
----------
Erik S. Bailey
Entry, Descent, and Landing Aerospace Engineer
Maker, and Musician
SpiritRover Kickstarter Backer #460

User avatar
esba1ley
Posts: 58
Joined: Sun Jan 01, 2017 2:15 am
Location: Pasadena, CA
Contact:

Re: How to.... interface the RPi with spirit board

Postby esba1ley » Sun Jan 28, 2018 6:09 am

Also, what frequency is the i2c bus running at? It seems to be higher than 100kHz based on what I'm seeing with pigpiod and piscope, which can only sample the gpio at 1 microsecond... I'm also seeing 20Hz activity on the I2C pins (2 and 3) on the RasPI...
----------
Erik S. Bailey
Entry, Descent, and Landing Aerospace Engineer
Maker, and Musician
SpiritRover Kickstarter Backer #460

User avatar
esba1ley
Posts: 58
Joined: Sun Jan 01, 2017 2:15 am
Location: Pasadena, CA
Contact:

Re: How to.... interface the RPi with spirit board

Postby esba1ley » Mon Jan 29, 2018 8:34 am

Hey all:

You might like this more object-oriented, user-friendly version of commanding Pan/Tilt from the Raspberry Pi using Python:

Code: Select all



''' This program is to explore the i2c bus on the SpiritRover Kit.

Original Source:  http://forum.plumgeek.com/viewtopic.php?f=18&t=6575

The SpiritRover i2c bus looks like:

pi@spirit_rover:~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- 1c -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- 32 -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- 6b -- -- -- --
70: -- -- -- -- -- -- -- --

0x32 is the PIC microprocessor.

'''
from __future__ import print_function
import smbus
import time


class ServoCal(object):

    def __init__(self, minVal=45, center=90, maxVal=135):
        self.min = minVal
        self.center = center
        self.max = maxVal

    def setCalArray(self, cal_array):
        self.min = cal_array[0]
        self.center = cal_array[1]
        self.max = cal_array[2]


class Servo(object):

    def __init__(self, register, i2c_bus=1, PIC_address=0x32):
        self.i2c_bus = smbus.SMBus(i2c_bus)
        self.PIC_address = PIC_address
        self.cal = ServoCal()
        self.register = register
        self.state = None
        self.write_err_count = 0

    def writeByte(self, value):
        self.write_err_count = 0
        try:
            self.i2c_bus.write_byte_data(self.PIC_address,
                                         self.register, value)
            return 0
        except:
            self.write_err_count += 1
            self.writeByte(value)
            return self.write_err_count * -1

    def limitCheck(self, cmd):
        ''' clamp specified location value to limit
        if outside specified axis limits '''

        if cmd > self.cal.max:
            cmd = self.cal.max
        elif cmd < self.cal.min:
            cmd = self.cal.min
        return cmd

    def moveAbsolute(self, cmd):
        ''' Move to specific value w/ limit check '''
        cmd = self.limitCheck(cmd)
        self.writeByte(cmd)
        self.state = cmd
        return 0

    def moveTo(self, cmd):
        ''' Move to cal-center-relative position '''
        cmd = self.cal.center + cmd
        self.moveAbsolute(cmd)
        return 0

    def moveDelta(self, delta):
        ''' Move relative to current position'''
        self.moveAbsolute(self.state + delta)
        return 0

    def center(self):
        ''' Center specified axis to cal value'''
        self.moveTo(0)
        return 0


class PanTilt(object):

    def __init__(self):
        self.pan = Servo(53)
        self.pan.cal.setCalArray([30, 90, 150])
        self.tilt = Servo(52)
        self.tilt.cal.setCalArray([20, 110, 150])
        self.center()

    def vecMoveAbsolute(self, cmd_vec):
        ''' move both axes to specified locations '''
        self.pan.moveAbsolute(cmd_vec[0])
        self.tilt.moveAbsolute(cmd_vec[1])
        return 0

    def vecMoveTo(self, cmd_vec):
        ''' move both axes to cal-center-relative positions '''
        self.pan.moveTo(cmd_vec[0])
        self.tilt.moveTo(cmd_vec[1])
        return 0

    def vecMoveDelta(self, cmd_vec):
        ''' Move both axes relative to current position'''
        self.pan.moveDelta(cmd_vec[0])
        self.tilt.moveDelta(cmd_vec[1])
        return 0

    def center(self, axis='both'):
        ''' Center all axes to cal center values'''
        if axis == 'both':
            self.pan.center()
            self.tilt.center()
        else:
            getattr(self, axis).center()
        return 0

    def fullScan(self, pan_delta=15, tilt_delta=15, pause=0.5):
        self.pan.moveAbsolute(self.pan.cal.min)
        self.tilt.moveAbsolute(self.tilt.cal.min)

        while True:
            if (self.pan.state <= self.pan.cal.min) or \
                    (self.pan.state >= self.pan.cal.max):
                pan_delta = -pan_delta
            if (self.tilt.state >= self.tilt.cal.max) and \
               (self.pan.state >= self.pan.cal.max):
                self.center()
                break
            elif (self.pan.state >= self.pan.cal.max) or \
                 (self.pan.state <= self.pan.cal.min):
                self.tilt.moveDelta(tilt_delta)
                self.pan.moveDelta(pan_delta)
            else:
                self.pan.moveDelta(pan_delta)
                time.sleep(pause)


if __name__ == "__main__":
    pt = PanTilt()
    pt.fullScan()



(code above updated 1/30/18)

This provides for absolute moves, moves relative to calibrated center point, and moves relative to the current stored position. I based this off of code posted in this forum to use the i2c interface to talk to the PIC on device 0x32, registers 53 (pan) and 52 (tilt).

Has anyone got code that writes multiple bytes to multiple registers in one go instead of my having to sequence the axes like I did above? For large moves, it's annoying because pan always moves first, then tilt. It'd be nice to be able to move both simultaneously.

-Erik
----------
Erik S. Bailey
Entry, Descent, and Landing Aerospace Engineer
Maker, and Musician
SpiritRover Kickstarter Backer #460

gpvillamil
Posts: 75
Joined: Fri Jan 26, 2018 5:03 pm

Re: How to.... interface the RPi with spirit board

Postby gpvillamil » Wed Jan 31, 2018 6:54 pm

This is very cool. So you're using I2C to talk directly to the PIC processor?

I was poking around the Arduino sketch, and it looks like you can probably use SPI to go from the Pi to the Arduino, and then use the existing Arduino libraries. You could probably do all the code for easing/simultaneous moves on the Arduino, I think.

It looks like the missing piece of the puzzle is just some libraries on the Pi side to send commands over SPI.


Who is online

Users browsing this forum: No registered users and 2 guests