-
-
Save gileri/5a9285d6a1cfde142260 to your computer and use it in GitHub Desktop.
http://bradsrpi.blogspot.fr/2013/03/sending-data-from-arduino-to-raspberry.html |
#include <Wire.h> | |
#define SLAVE_ADDRESS 0x04 | |
#define FLOATS_SENT 2 | |
float temperature = 10.5; | |
float luminosity = 5.2; | |
float data[FLOATS_SENT]; | |
void setup() { | |
pinMode(13, OUTPUT); | |
Serial.begin(9600); | |
data[0] = temperature; | |
data[1] = luminosity; | |
// initialize i2c as slave | |
Wire.begin(SLAVE_ADDRESS); | |
// define callbacks for i2c communication | |
Wire.onRequest(sendData); | |
} | |
void loop() { | |
delay(100); | |
} | |
void sendData(){ | |
Wire.write((byte*) &temperature, FLOATS_SENT*sizeof(float)); | |
} |
import time | |
import struct | |
import smbus | |
# 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 get_data(): | |
return bus.read_i2c_block_data(address, 0, 8) | |
def get_float(data, index): | |
bytes = data[4*index:(index+1)*4] | |
return struct.unpack('f', "".join(map(chr, bytes)))[0] | |
while True: | |
try: | |
data = get_data() | |
print(get_float(data, 0)) | |
print(get_float(data, 1)) | |
except Exception as e: | |
print(e) | |
continue | |
finally: | |
time.sleep(1) |
Hi, I have the code implemented as given below (pretty much the same as given above, minus error catching to get to the error message).
I get an IOError: [Errno 121] Remote I/O error
on the Raspberry Pi (running Python 2.7), once it tries bus.read_i2c_block_data(address, 0, 8)
.
When, however, I try to do a bus.read_byte(address)
, no error is encountered.
I'm quite possibly being an idiot, but does anyone have any ideas why this 121 keeps on happening?
i2c_float.py
import struct
import smbus
import time
bus = smbus.SMBus(1)
address = 0x04
def get_data():
return bus.read_i2c_block_data(address, 0, 8);
def get_float(data, index):
bytes = data[4*index:(index+1)*4]
return struct.unpack('f', "".join(map(chr, bytes)))[0]
while True:
time.sleep(1);
data = get_data()
print(get_float(data, 0))
print(get_float(data, 1))
and i2c_float.c on Arduino Mega
#include <Wire.h>
#define SLAVE_ADDRESS 0x04
#define FLOATS_SENT 2
float temperature = 10.5;
float luminosity = 5.2;
float data[FLOATS_SENT];
void setup() {
pinMode(13, OUTPUT);
Serial.begin(9600);
data[0] = temperature;
data[1] = luminosity;
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onRequest(sendData);
}
void loop() {
delay(100);
}
void sendData(){
Wire.write((byte*) &temperature, FLOATS_SENT*sizeof(float));
}
thanks @Tarkahn For Your Help
Thank you for this post it has been very useful for me.
I tried to add a third floating value. On the Arduino side everything goes well. I added the following lines in the code:
#define FLOATS_SENT 3;
then I declare my third floating value
float` humidity = 19.6;
and added the line
data [2] = humidity;
On the side of the Raspberry, I added a print (get_float (data, 2))
, but I have the following error message:
return struct.unpack ('f', "" .join (map (chr, bytes))) [0]
struct.error: unpack requires a string argument of length 4
Any ideas please?
This is the program we setup in the Rpi
import smbus
import time
import struct
bus = smbus.SMBus(1)
address = 0x04
def get_data():
return bus.read_i2c_block_data(address, 0);
def get_float(data_bytes,index):
bytes = data_bytes[4index:4index+4]
aux = bytearray(bytes)
data_float=struct.unpack('<f',aux)[0]
return data_float
while True:
try:
data = get_data()
print(str(get_float(data, 0))+" dB")
print(str(get_float(data, 1))+" index")
print(str(get_float(data, 2))+" ppm")
print(" ")
except:
continue
time.sleep(1);
This is the program we setup in the Arduino Program
#include <Wire.h>
#define SLAVE_ADDRESS 0x04
#define FLOATS_SENT 3
float audio = 10.5;
float uv = 5.2;
float aire = 1500.8;
float data[FLOATS_SENT];
void setup() {
pinMode(13, OUTPUT);
Serial.begin(9600);
data[0] = audio;
data[1] = uv;
data[2] = aire;
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onRequest(sendData);
}
void loop() {
delay(100);
}
void sendData(){
Wire.write((byte*) &data, FLOATS_SENT*sizeof(float));
}
I'm a bit late to the party, I'm sure you guys have figured it out by now. I've only done 1 unit in JAVA data structures so this it might not be the best way but its what worked for me.
Arduino slave
#include "ph_grav.h" //my headerfile
#include <Wire.h>
#define SLAVE_ADDRESS 0x08 // arduino slave address
volatile float pHreading;
Gravity_pH pH = Gravity_pH(A0);
//int FLOATS_TO_SEND = 4;
void setup() {
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveData);
Wire.onRequest(sendData); //send data when raspberry askes for it
}
void loop() {
noInterrupts(); // inssure the reading and data sending not a the same time
pHreading = (pH.read_ph()); // my sensor function, use what ever float you want
interrupts();
delay(1000);
}
void sendData()
{
Wire.write((uint8_t*) &pHreading, sizeof(float)); // writes the float to wire (4 bytes)
}
//just make a for loop to send an array of floats e.g
//for ( int i = 0; i < FLOATS_TO_SEND; i++){
//Wire.write((uint8_t*) &pHreading[i], sizeof(float));}
//and the same on the raspberry pi side
void receiveData(int bytecount)
{
//if you want to do something when the rasberry sends data
}
Raspberry master
import struct
import smbus
import time
bus = smbus.SMBus(1) # raspberry pi address this might be (0) if on older raspberry pi's
address = 0x08 #address of arduino
def get_data():
temp = bytearray(bus.read_i2c_block_data(address, 1, 1)) # through away the first byte as this is the ack byte
return bytearray(bus.read_i2c_block_data(address, 1, 4));# return 4 bytes in a array ( bytes sent from arduino)
while True:
time.sleep(1);
data = get_data()
data2 = struct.unpack('f', data) # convert the array of bytes to floats
print(data2); #check the it worked
I am also late to the party ;-) hopefully there is still somebody around.
Basically, it does exactly what I want. The modification
read_i2c_block_data(address, 0, 8);
is essential (and python would not complain about the semicolon???).
Otherwise, without the third parameter, the Arduino runs wild and keeps sending 255 and would not stop.
But I have a problem:
The parameter "cmd", which is 0 in this case, is not understood by the Arduino.
When reading from the raspi, the Arduino confuses this 0 with other incoming data.
So this solution works one-way only.
I found another solution here:
https://stackoverflow.com/questions/14420372/how-to-read-data-from-arduino-with-raspberry-pi-with-i2c?rq=2
i2c_msg can be used to request the amount of data needed.
Wow, thanks for the interest in the script !
I cleaned up the script and implemented the read size based on @wicusverhoef's suggestion. However I can't test it right now, I count on you lads :)
The python code will not work because the smbus library hasn't been included - amazing nobody noticed this?
try adding at the top of the code
import smbus
also,
The code does not print to the console. I'm investigating.
Adding a print(data) after data=get_data() in the while loop prints the entire array that is passed but the data appears erroneous.
I'm getting [0,0,40,65,0,0,0,0,255,255,255...... ]
This is just printing the raw data passed to rpi - before get_float() function is called. Also, since I'm relatively new to Python I don't understand the use of structs so I have absolutely no idea what this function is doing. The lack of any comments doesn't help. Can anyone enlighten me about the purpose of the get_float() function and why the block data sent to rpi is as I show above??
No answer from anyone on these questions so I'll update what I've found so far. Another library missing from the code is time. Add the following:
import time
Also noticed that this code as is won't work with Python 3 so stick with 2.7 until someone/me comes up with new code that will work with python 3.n
Also, even though I finally can see that a float has been passed (the 10.5 temperature value), the lumninosity value does not pass. I'm getting a 0.0 for this. Investigating. - Ok I see now, the Arduino code had only a reference to temperature being sent to the rpi. Change the sendData function to:
void sendData(){ Wire.write((byte*) &data, FLOATS_SENT*sizeof(float)); }
Works like a charm when you add the correct libraries and send the reference to data. !!