User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

RPi to Arduino i2c Control

Mon Jul 27, 2015 8:28 pm

Hi;

I am building a Web Controlled Robot and have been testing i2c communication between my RPi and an Arduino Nano.

I have had some success with webiopi and pigpio cotrolling servos over a web page connected directly to the Pi and now I want to add an Arduino Nano into the mix.

The Nano will be connected via i2c and before I try it over webiopi (web page) I thought Id' get it working first from a Python IDLE session.

I have successfully connected the Pi and the Nano over i2c and my test program (borrowed from Oscar Laing) is working.

Couple of problems:
1. When I enter 1 the LED illuminates but when I enter another number it does not go off?
2. If I enter the same number twice in a row Python exits and complains of a EOF (sorry not got the error in front of me).

Also I had to force this to use Python 2 because Python 3 complained of "no module named smbus, is there a version for Python 3?

Here is the Python code:

Code: Select all

#!/usr/bin/env python

import smbus
import time
# For RPi version 1 use "bus = smbus.SMBus(0)"
bus = smbus.SMBus(1)
# This is the address we setup in the Arduino Program
address = 0x04
def writeNumber(value):
    bus.write_byte(address, value)
    # bus.write_byte_data(address, 0, value)
    return -1
def readNumber():
    number = bus.read_byte(address)
    # number = bus.read_byte_data(address, 1)
    return number
while True:
    var = input("Enter 1 - 9: ")
    if not var:
        continue
    writeNumber(var)
    print("RPI: Hi Arduino, I sent you ", var)
    # sleep one second
    time.sleep(1)
    number = readNumber()
    print("Arduino: Hey RPI, I received a digit ", number)
    print()
Here is the Arduino code:

Code: Select all

// Arduino Pin A5 to RPi SDA
// Arduino Pin A6 to RPi SCL
// Inspiration from Ocra Laing's Blog http://blog.oscarliang.net/raspberry-pi-arduino-connected-i2c

#include <Wire.h>

#define SLAVE_ADDRESS 0x04
int number = 0;
int state = 0;

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(9600); // Start serial for output
  
  Wire.begin(SLAVE_ADDRESS); // Initialise i2c as slave
 
  // Define callbacks for i2c communication
  Wire.onReceive(receiveData);
  Wire.onRequest(sendData);

  Serial.println("Ready!");
}

void loop() {
  delay(100);
}

// Callback for received data
void receiveData(int byteCount) {
  
  while(Wire.available()) {
    number = Wire.read();
    Serial.println("data received: ");
    Serial.println(number);
    
    if (number == 1){
      
      if (state == 0){
        digitalWrite(13, HIGH); // turn the LED on
        state = 1;
      }
      else{
        digitalWrite(13, LOW); // turn the LED off
        state = 0;
      }
    }
  }
}

// Callback for sending data
void sendData() {
  Wire.write(number);
}
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

ghp
Posts: 1516
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: RPi to Arduino i2c Control

Mon Jul 27, 2015 8:58 pm

Hello,
when I look into the arduino code, it is acting only when a '1' is received. So first 1 is switching on, second should switch it off.
You could change the arduino code to switch on 1 and switch off on 0.
Regards,
Gerhard

User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

Re: RPi to Arduino i2c Control

Tue Jul 28, 2015 8:02 am

Is it because my else statement is in line with the second if statement? So the code checks the number received and only does something if its a 1, if the number is not 1 (apart from being printed) it just gets ignored.
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

ghp
Posts: 1516
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: RPi to Arduino i2c Control

Tue Jul 28, 2015 4:51 pm

Hello,
the if (number == 1){ ...} controls the majo rpart of the arduino code.
If you place a if (number == 1){ switch on..} if (number == 2){ switch off ...}, then you will have more precise control.
Regards,
Gerhard

User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

Re: RPi to Arduino i2c Control

Tue Jul 28, 2015 7:31 pm

That's great, this is how I was thinking of receiving command from python / webiopi web page to drive the web controlled robot, using single integers:
0 - stop
1 - forward
2 - backwards
3 - turn right
4 - turn left
etc

I also found this during my research on how to do this, I may use it but want to start simple first:

Code: Select all

#include <Wire.h>

#define EN0_PIN 5
#define EN1_PIN 6

#define M1_0_PIN 2 
#define M1_1_PIN 4

#define M2_0_PIN 7
#define M2_1_PIN 8

struct packet {
	bool complete;
	int data_index;
	int data[3];	
};

packet command;

void setup()
{
	// I2c init address 3
	Wire.begin(3);
	Wire.onReceive(receive_data);
//	Wire.onRequest();

	//debug serial
	Serial.begin(115200);
	//setup Enable pins
	//min speed == 100 pwm 
	pinMode(EN0_PIN, OUTPUT);
        pinMode(EN1_PIN, OUTPUT);

	digitalWrite(EN0_PIN, LOW);
	digitalWrite(EN1_PIN, LOW);

	//direction pins
	pinMode(M1_0_PIN, OUTPUT);
	pinMode(M1_1_PIN, OUTPUT);
	pinMode(M2_0_PIN, OUTPUT);
	pinMode(M2_1_PIN, OUTPUT);

	digitalWrite(M1_0_PIN, HIGH);
	digitalWrite(M1_1_PIN, LOW);
	digitalWrite(M2_0_PIN, HIGH);
	digitalWrite(M2_1_PIN, LOW);

}

void loop()
{
}

void get_battery()
{
	int raw = analogRead(0);
	float val = fmap(raw, 0, 1023, 0.00, 5.00);
  	Serial.println(val);
}


float fmap(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void execute_command(){
	// called when a complete packet has been received 
	switch (command.data[0]){
		case 1:
			get_battery();
		break;
		case 2:
		//drive motors in forward
			drive(1, command.data[1],command.data[2]);
		break;		
		case 3:
		//drive motors in backward
			drive(2, command.data[1],command.data[2]);
		break;
		case 4:
		//drive one motor fw one bw
			drive(3, command.data[1],command.data[2]);
		break;
		case 5:
			drive(4, command.data[1],command.data[2]);
		//drive one motor bw one fw
		break;
	}
}

void drive(int dir, int pwm1, int pwm2){
	analogWrite(EN0_PIN, pwm1);
	analogWrite(EN1_PIN, pwm2);
	switch (dir){
		case 1:	
			digitalWrite(M1_0_PIN, HIGH);
			digitalWrite(M1_1_PIN, LOW);
			digitalWrite(M2_0_PIN, HIGH);
			digitalWrite(M2_1_PIN, LOW);
		break;
		case 2:
			digitalWrite(M1_1_PIN, HIGH);
			digitalWrite(M1_0_PIN, LOW);
			digitalWrite(M2_1_PIN, HIGH);
			digitalWrite(M2_0_PIN, LOW);
		break;
		case 3:
			digitalWrite(M1_0_PIN, HIGH);
			digitalWrite(M1_1_PIN, LOW);
			digitalWrite(M2_1_PIN, HIGH);
			digitalWrite(M2_0_PIN, LOW);
		break;
		case 4:
			digitalWrite(M1_1_PIN, HIGH);
			digitalWrite(M1_0_PIN, LOW);
			digitalWrite(M2_0_PIN, HIGH);
			digitalWrite(M2_1_PIN, LOW);
		break;
	}
}

void process(int data){
	switch (data){
		case 0:
			command.complete=0;
			command.data_index=0;
		break;
		case 2:
			command.complete=1;
			//Serial.println("Command complete");
			execute_command();			
		default:
			if (command.complete==0) {
				command.data[command.data_index]=data;
				command.data_index++;
			}
	}
}

void receive_data(int byteCount)
{
  //read the command from wire
	int data = Wire.read(); 
	process(data);
}
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

Return to “Interfacing (DSI, CSI, I2C, etc.)”