Patryk Dzierżawski
Patryk Dzierżawski

Reputation: 21

Difference I2C Sensor Reading Raspberry Pi and Arduino

I am using the Sensirion SFM3300 flow sensor and can read the correct values with the Arduino with the following code (I2C):

#include <Wire.h>

void setup() {
  // put your setup code here, to run once:
  Wire.begin();
  Serial.begin(115200);
  Wire.beginTransmission(byte(0x40));
  Wire.write(byte(0x10));
  Wire.write(byte(0x00));
  Wire.endTransmission();
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(100);
  Wire.requestFrom(0x40,2);
  uint16_t a = Wire.read();
  uint8_t  b = Wire.read();
  a = (a<<8) | b;
  float flow = ((float)a - 32768) / 120;
  Serial.println(flow);
}

But using the Raspberry Pi I have written the nearly the same code, hoping that it also will works. This is the code:

from smbus2 import SMBus
import time
import numpy as np

address=0x40
bus = SMBus(1)

def write(value):
    bus.write_byte(address,value)

write(0x10)
write(0x00)

while True:
    time.sleep(0.1)
    a = np.uint16(bus.read_byte(0x40))
    b = np.uint8(bus.read_byte(0x40))
    a = (a<<8) | b
    flow = (float(a)-32768)/120
    print(flow)

The code really looks the same, but I only get -273,06666666666 as a return value. Does somebody knows where are the differences between Raspberry Pi and Arduino I2C and can help me to get the right values on the Pi?

Upvotes: 0

Views: 1556

Answers (3)

Patryk Dzierżawski
Patryk Dzierżawski

Reputation: 21

I found a working solution. It would be nice if a I2C-expert could tell me why the following code is working instead of the python code above.

from fcntl import ioctl
from struct import unpack
from smbus import SMBus

address = 0x40

SMBus(1).write_byte_data(address,16,0)
i2c = open("/dev/i2c-1", "rb", buffering=0)
ioctl(i2c,0x0703,address)
i2c.read(3)

d0,d1,c = unpack('BBB', i2c.read(3))
d = d0 << 8 | d1
a = (float(d)-32768.)/120
print(a)

Upvotes: 0

hcheung
hcheung

Reputation: 4069

You can use read_i2c_block_data(addr, offset, numOfBytes) method to get more than 1 byte of data from i2c. the return data is a list of bytes. So it is very easy to convert into an integer.

Edited based on datasheet and Arduino sketch

Here is the complete code for Python that should matched the Arduino example:

from SMBus2 import SMBus
import time

offset = 32768
scale = 120

addr = 0x40
cmd = [0x10, 0x00]

with SMBus(1) as bus:
    bus.write_i2c_block_data(addr, 0, cmd)
    time.sleep(0.1)
    block = bus.read_i2c_block_data(addr, 0, 3)
reading = block[0] * 256 + block[1]
crc = block[2]    # should do some crc check for error
flow = (reading - offset)/scale
print(flow)

Upvotes: 1

guidot
guidot

Reputation: 5333

I don't think your read process in python is correct. Reading from port 40 two times is different from reading two bytes from port 40.

I suggest to use read_byte_data(0x40, 0, 2) and process that with struct.unpack(">H").

Upvotes: 0

Related Questions