Grove i2c motor driver with GrovePi

After searching EVERYWHERE for a python version of code for the Grove i2c motor driver (as well as any tutorial, walkthrough, guide, ect) I decided I would just make my own.

Please take a look at this and see if it is going in the right direction. Have not had a chance to test it. Any advice you can give or errors you notice…please let me know

pastebin


#!/usr/bin/env python
#########################################################################
#Author:Paul Binder Jr
########################################################################

import smbus
import time
import RPi.GPIO as GPIO

rev = GPIO.RPI_REVISION
if rev == 2 or rev == 3:
    bus = smbus.SMBus(1)
else:
    bus = smbus.SMBus(0)

	
MOTOR_SPEED_SET = 0x82
PWM_FREQUENCE_SET = 0x84
DIRECTION_SET = 0xaa
MOTOR_SET_A = 0xa1
MOTOR_SET_B = 0xa5
NOTHING = 0x01
ENABLE_STEPPER = 0x1a
UNENABLE_STEPPER = 0x1b
STEPERNU = 0x1c
I2C_MOTOR_DRIVER_ADD = 0x0f #Set the address of the I2CMotorDriver

##set the steps you want, if 255, the stepper will rotate continuely;
def SteperStepset(stepnu):
	bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,STEPERNU,[stepnu,NOTHING])
 
## Enanble the i2c motor driver to drive a 4-wire stepper. the i2c motor driver will
##driver a 4-wire with 8 polarity  .
##Direction: stepper direction ; 1/0
##motor speed: defines the time interval the i2C motor driver change it output to drive the stepper
##the actul interval time is : motorspeed * 4ms. that is , when motor speed is 10, the interval time 
##would be 40 ms

def StepperMotorEnable(Direction,motorspeed):
	bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,ENABLE_STEPPER,[Direction,motorspeed])
		
##function to uneanble i2C motor drive to drive the stepper.
def StepperMotorUnenable():
	bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,UNENABLE_STEPPER,[NOTHING,NOTHING])
	
##Re-maps a number from one range to another. That is, a value of fromLow would get mapped to toLow, 
##a value of fromHigh to toHigh, values in-between to values in-between, etc.
##see http://www.arduino.cc/en/Reference/Map
def Map(x, in_min, in_max, out_min, out_max):
	return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min

##Function to set the 2 DC motor speed
##motorSpeedA : the DC motor A speed; should be 0~100;
##motorSpeedB: the DC motor B speed; should be 0~100;

def MotorSpeedSetAB(MotorSpeedA , MotorSpeedB):
	MotorSpeedA=Map(MotorSpeedA,0,100,0,255)
	MotorSpeedB=Map(MotorSpeedB,0,100,0,255)
	bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,MOTOR_SPEED_SET,[MotorSpeedA,MotorSpeedB])

##set the prescale frequency of PWM, 0x03 default;
def MotorPWMFrequenceSet(Frequence):   
	bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,PWM_FREQUENCE_SET,[Frequence,NOTHING])

##set the direction of DC motor. 
## Adjust the direction of the motors 0b0000 I4 I3 I2 I1
def MotorDirectionSet(Direction):
	bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,DIRECTION_SET,[Direction,NOTHING]) 

##you can adjust the driection and speed together
def MotorDriectionAndSpeedSet(Direction,MotorSpeedA,MotorSpeedB) :
	MotorDirectionSet(Direction)
	MotorSpeedSetAB(MotorSpeedA,MotorSpeedB)

I keep getting an I/O error and not sure where to go from here. I enabled i2c on raspi-config.

root@raspberrypi:/home/pi# sudo python te.py
setup begin
sent command to + direction, very fast
Traceback (most recent call last):
File “te.py”, line 109, in <module>

stepperrun() File "te.py", line 81, in stepperrun StepperMotorEnable(1, 1) #ennable the i2c motor driver a stepper. File "te.py", line 41, in StepperMotorEnable bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,ENABLE_STEPPER,[Direction,motorspeed]) IOError: [Errno 5] Input/output error

Here is my code

import smbus
import time
import RPi.GPIO as GPIO

rev = GPIO.RPI_REVISION
if rev == 2 or rev == 3:
    bus = smbus.SMBus(1)
else:
    bus = smbus.SMBus(0)

MOTOR_SPEED_SET = 0x82
PWM_FREQUENCE_SET = 0x84
DIRECTION_SET = 0xaa
MOTOR_SET_A = 0xa1
MOTOR_SET_B = 0xa5
NOTHING = 0x01
ENABLE_STEPPER = 0x1a
UNENABLE_STEPPER = 0x1b
STEPERNU = 0x1c
I2C_MOTOR_DRIVER_ADD = 0x0f #Set the address of the I2CMotorDriver

##set the steps you want, if 255, the stepper will rotate continuely;
def SteperStepset(stepnu):
    bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,STEPERNU,[stepnu,NOTHING])

## Enanble the i2c motor driver to drive a 4-wire stepper. the i2c motor driver will
## driver a 4-wire with 8 polarity  .
## Direction: stepper direction ; 1/0
## motor speed: defines the time interval the i2C motor driver change it output to drive the stepper
## the actul interval time is : motorspeed * 4ms. that is , when motor speed is 10, the interval time 
## would be 40 ms

def StepperMotorEnable(Direction,motorspeed):
        bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,ENABLE_STEPPER,[Direction,motorspeed])

##function to uneanble i2C motor drive to drive the stepper.
def StepperMotorUnenable():
    bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,UNENABLE_STEPPER,[NOTHING,NOTHING])

##Re-maps a number from one range to another. That is, a value of fromLow would get mapped to toLow, 
##a value of fromHigh to toHigh, values in-between to values in-between, etc.
##see http://www.arduino.cc/en/Reference/Map
def Map(x, in_min, in_max, out_min, out_max):
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min

##Function to set the 2 DC motor speed
##motorSpeedA : the DC motor A speed; should be 0~100;
##motorSpeedB: the DC motor B speed; should be 0~100;

def MotorSpeedSetAB(MotorSpeedA , MotorSpeedB):
    MotorSpeedA=Map(MotorSpeedA,0,100,0,255)
    MotorSpeedB=Map(MotorSpeedB,0,100,0,255)
    bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,MOTOR_SPEED_SET,[MotorSpeedA,MotorSpeedB])

##set the prescale frequency of PWM, 0x03 default;
def MotorPWMFrequenceSet(Frequence):   
    bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,PWM_FREQUENCE_SET,[Frequence,NOTHING])

##set the direction of DC motor. 
## Adjust the direction of the motors 0b0000 I4 I3 I2 I1
def MotorDirectionSet(Direction):
    bus.write_i2c_block_data(I2C_MOTOR_DRIVER_ADD,DIRECTION_SET,[Direction,NOTHING]) 

##you can adjust the driection and speed together
def MotorDriectionAndSpeedSet(Direction,MotorSpeedA,MotorSpeedB) :
    MotorDirectionSet(Direction)
    MotorSpeedSetAB(MotorSpeedA,MotorSpeedB)

def stepperrun():
    print(&quot;sent command to + direction, very fast&quot;)
    SteperStepset(255)
    StepperMotorEnable(1, 1) #ennable the i2c motor driver a stepper.
    time.sleep(5)
    print(&quot;sent command to - direction, slow&quot;)
    SteperStepset(255);
    StepperMotorEnable(0, 20)
    time.sleep(5)
    print(&quot;sent command to - direction, fast&quot;)
    StepperMotorEnable(0, 2) # ennable the i2c motor driver a stepper.
    delay(5000);
    print(&quot;sent command to + direction,100 steps, fast&quot;)
    SteperStepset(100)
    StepperMotorEnable(1,5)
    time.sleep(5)

    print(&quot;sent command to shut down the stepper&quot;)
    StepperMotorUnenable();
    time.sleep(5)

    print(&quot;sent command to - direction, slow, and 10 steps then stop&quot;)
    SteperStepset(10)
    StepperMotorEnable(0,40)
    time.sleep(5)
    print(&quot;sent command to shut down the stepper&quot;)
    StepperMotorUnenable()
    delay(5000);

print(&quot;setup begin&quot;)
stepperrun()

Updated the GrovePi firmware, now the code produces no errors. Yet the motors do not move.

After reboot I get the i/o errors yet again. It appears the connection is bad between the device and the driver.

running i2cdetect -y 1 shows the driver as 0x0f
yet after running i2cdetect -y 1 again it no longer shows up.

It appears as soon as you try to read/write from the device it shorts out.

Any help would be great as I have no idea how to resolve this at this point

Hey,
I am not sure about what the problem is, but it looks like because of a problem with the I2C communications. Can you try running sudo i2cdetect -y 1 and see what you get. This would show you the list of devices which the Pi can see and you should see the motor controller there.

Let me know what you get with this.

-Karan

running i2cdetect -y 1 the first time displays 0x0f (as well as the GrovePi)

running it a second time displays nothing. THe grovepi also does not show up on the second run of i2cdetect

HOWEVER when I unplug the driver and run i2cdetect I see the Grove fine, no matter how many times I run i2cdetect

Hey,
It might be a case of weak pullups or some other problem with the motor driver. Can you try running it a couple of times to see if does keep on disappearing. I don’t think you would be able to get the script to work if the motor driver does not show up consistently.

-Karan

It consistently disappear after the first i2cdetect.

One question : Does the GrovePi i2c ports feed the 5 volts from the driver DIRECTLY to the pi? or does it provide some resistance?

And I am guessing you are suggesting this is a driver issue? If so I will try to get seeed to replace it as I just got the grove i2c driver.

The GrovePi motor driver should not be supplying the 5V to the Pi. You should have a separate power supply for the Pi and the motor driver.

I am not sure that you might have a defective unit with you. You should try out a few more things before deciding to get it replaced.

What kind of power supply are you using to power the whole setup.

-Karan

Thanks for getting back to me so quickly Karen.

I have 4 AA batteries going to the driver and the Raspberry pi is plugged into the wall socket.

I do admit I found these batteries in a drawer and I am not sure how much power is left in them

However when I put all the batteries in the driver lights up.

Could weak batteries be the issue?

I checked the schematics for the motor driver and the batteries are indeed supplying the power to the whole motor driver even for the comms. The regulator used is a 7508 and has a big dropout so you should use good batteries and at least 6 batteries.

The motor driver also uses an Atmega8 for interpreting the commands, so you can even rewrite the code on that if you want later if you feel like adding some features or making it easier to use.

-Karan

thanks karen, ill buy a 6 battery case today and new batteries and test it out, then let you know.

I will have to learn about the Atmega8, but I would be up for adding said features to my library

it seems there was a need for a python library for this driver. So hoping I can get this working and provide the community with the library i create/translate.

Do let us know how the driver work with a 6 battery pack.

Atmega8 is similar to the Atmega328 which is used with Arduino so you should be able to write the code for it in the Arduino IDE itself if you ever want to.

It would be great if you are able to get the driver for the motor controller working with the GrovePi. We would be glad to merge it with the GrovePi repository so that others can use it too.

-Karan

I added 2 aa batteries to the mix and the result is the same. I noticed now that the raspberry pi can be powered by the ic2 input alone. Is this normal/safe?

So my current setup is

8AAs(9v)–Motor Driver --Grove Pi I2C1 port – Raspberry Pi

entering i2cdetect shows my Grove at address 0x04 and my driver at 0x0f. After hitting the command again both addresses are gone. (showing --)

as well as running my code gives an input/output error when trying to read/write

Sorry to flood this thread,
just tried to remove the J4 jumper so no power would go to the raspberry

same result.
I am leaning towards the fact that the board I have is bad.

It would be better to power the GrovePi separately and not use the motor controller to back power it.

I am not sure what the problem is exactly. Can you try to measure the voltage on the 9V line and the 5V line to see what it says. Also, do you have and Arduino and a Grove Arduino shield to check if the board works with Arduino. That might be a better test to find if the Shield works or not.

-Karan

I have an Arduino,
plugged it directly to the Arduino and the driver worked.

Then I pluged the driver directly to the raspberry pi and it failed.

I had my friend bring over his raspberry pi to test and see if it was an error with my pi.

Same results with his raspberry pi. i/o error.

Also tested the connection with the meter. I see what you were talking about with the drop off, huge drop off of volts from the chip to the motors.

However the Arduino recieved volts coming in and volts to the motors, neither of the pis did.

any reason this driver would not function with the raspberry pi?

Hey Exadon and Karan, I’m coming late to the party here. Sorry about that. I have a few quick questions, and thoughts.

It sounds like the I2C is getting hung up on the driver. It seems like you ping it once, and the device freezes up.

First, since there’s an Atmega8 on the driver, I wonder if there’s a software error, or hardware error. Have we doubled back and seen if there are any comments on the Seeed site about this? I’m just curious.

Also, because we have multiple devices on the line, with multiple pullups, we might also be pulling the I2C lines high (or they’re getting stuck high). Exadon, do you feel adventurous enough to pull the pullup resistors off the sensor and see if affectss things? I’m leaning even more strongly to this because the behavior when you remove the sensor; it seems to work, and the line seems to be released.

Sorry for the late reply. I let my nephew have the board for his Arduino. I am feeling adventgerious enough though that I will give this a shot.

Also will check the comments to see if I missed anything (from seeeds site)

Hey Exadon,
No problems, you have laid a good foundation for someone else who comes looking for the code for the motor driver and this should be a good starting point for others in the future.

Do let us know if you get a chance in the future to test the driver more.

-Karan