Reputation: 21
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
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
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
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