1

I am trying to connect an Arduino Uno with my Raspberry using the i2c bus, based on the tutorial given here, sending only one byte from RPi to Arduino and back. I was able to implement this and it works fine.

I needed to transfer more than a byte of data, so I referred to this page and tried to implement the given solution with python 3.4.2. However when I implement the solution, it gives me following error:

>>>
[45, 0, 25, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]
Traceback (most recent call last):
  File "/home/pi/codeSampleMaster2.py", line 15, in <module>
    number = readLong()
  File "/home/pi/codeSampleMaster2.py", line 10, in readLong
    n = struct.unpack('<l',(''.join([chr(i) for i in block[:4]])))[0]
TypeError: 'str' does not support the buffer interface

The data in the square bracket is what I received as part of block read. What I require is to be able to extract 45002502 as part of my program.

Please guide me as to where I might have made a mistake. Im fairly new to working with python and Rasp Pi.

Arduino Code:

#include <Wire.h>
#define SLAVE_ADDRESS 0x04
int senseLeft = A0;     // Left side distance sensor input pin.
long senseLeftVal = 0;   // Variable to store the Left side sensor value.

void setup() {
  Serial.begin (9600); // start serial for output 
  Wire.begin(SLAVE_ADDRESS);
  Wire.onRequest(requestEvent); // register event
  Serial.println("Ready!");
}

void loop() {
//  Serial.println(senseLeftVal);
  delay(1);
}

void requestEvent()
{
  senseLeftVal = analogRead(senseLeft);
  Wire.write((const uint8_t*)&senseLeftVal, sizeof(long));
  Serial.println(senseLeftVal);
}

Rasp Pi Code:

import smbus
import time
import struct

bus = smbus.SMBus(1)
address = 0x04

def readLong():
    block = bus.read_i2c_block_data(address,0)
    print(block)
    n = struct.unpack('<l',(''.join([chr(i) for i in block[:4]])))[0]
    return n

while True:
    time.sleep(1)
    number = readLong()
    print("Arduino: ", number)
  • It would be simplest just to use Python 2 rather than Python 3. – joan Oct 5 '16 at 16:58
  • I can try it in Python 2, however, all other modules have been implemented in Python 3. How can I use Python 2 files in a Python 3 project? – Parth D Oct 6 '16 at 8:38
  • Just do what Shreyas Murali suggests. Replace the struct.unpack line with n = block[0] | block[1] << 8 | block[2] <<16 | block[3] << 24 which should give the correct answer. – joan Oct 6 '16 at 9:44
  • @joan when I implement the solution mentioned I am able to OR two bytes of data at a time block[0] | block[1] << 8 and block[2] | block[3] << 8. Any additional byte changes the output to random numbers. – Parth D Oct 6 '16 at 18:21
  • Would it be possible for you to include in your question the result of print(block) together with the number you expect? – joan Oct 6 '16 at 19:04
1

I have a feeling the following line is the problem

n = struct.unpack('<l',(''.join([chr(i) for i in block[:4]])))[0]

try replacing it with this

n = block[0] << 24 | block[1] << 16 | block[2] << 8 | block[3]
print(n)

if the numbers dont make sense, you might need to swap the order like so (for endianess)

n = block[3] << 24 | block[2] << 16 | block[1] << 8 | block[0]
print(n)

and check again


Also as joan suggests in this post, you might want move the analogRead after the Wire.write to reduce the chances of missed communication as timing is critical in dealing with i2c devices. Ideally you'd want to move the analogRead into the loop and simply send out the last read value when requested in the event handler


Looking at the documentation and example for smbus over here it makes sense that you are getting 32 bytes of data

Example 2: Read a block of data Note: You can read up to 32 bytes at once.

from smbus2 import SMBusWrapper    
with SMBusWrapper(1) as bus:
# Read a block of 16 bytes from address 80, offset 0    
block = bus.read_i2c_block_data(80, 0, 16) # Returned value is a list of 16 bytes    
print(block)

Since you are only sending 4 bytes = (sizeof(long)) from your arduino, there are 28 bytes filled with junk (255 in your case). the simplest fix is just use the first 4 bytes like so

import smbus
import time
import struct

bus = smbus.SMBus(1)
address = 0x04

def readLong():
    block = bus.read_i2c_block_data(address,0)
    print(block) # should print the array with 32 entries

    reading = block[0:4] # select only first 4 bytes
    print(reading)

    n = reading[0] << 24 | reading[1] << 16 | reading[2] << 8 | reading[3]
    print(n)

    return n

while True:
    time.sleep(1)
    number = readLong()
    print("Arduino: ", number)

  • Considering joans suggestion I had kept the printing statements after the Write.write is executed. Also, I tried implementing your solution but in either case the output value of ndoesn't make sense. Is it like there is a type change when i link the block data to n? – Parth D Oct 6 '16 at 8:34
  • I am currently using smbus rather than smbus2 as indicated in the documentation. Also, when i implement your solution, the result is as follows: Output of print(reading) is [62, 0, 53, 0]. However the output of print(n) is 1040200960. – Parth D Oct 8 '16 at 17:47
  • Reversing the statement of n as reading[3] << 24 | reading[2] << 16 | reading[1] << 8 | reading[0] also gives junk value at output. However, i was able to merge 2 bytes just like before as commented above. – Parth D Oct 8 '16 at 17:57
  • According to the documentation here the function analogRead returns a int between 0 and 1023 which means you should get only values between [0,0,0,0] and [0,0,3,255]. In your case you are getting [62,0,53,0] which is quite puzzling as no combination of those numbers will give 0<= value <= 1023. Recommend trying the following: (a) comment out Serial.println(senseLeftVal); (b) send a fixed number from the arduino and verify you get the expected value on the pi. if the fixed number comes through ok, then analogRead needs to be looked at. – Shreyas Murali Oct 8 '16 at 18:33
  • I found my mistake. I was trying to use a different analog port pin and forgot to comment previous pin. After making corrections, I was able to receive correct output. – Parth D Oct 10 '16 at 7:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.