I have been trying to figure out how to read data from the single tact force sensor with the electric IMP April devboard… But every time i do a measurment i get a null :0x00 value. Why is that ? Could someone help me with this?
i2c <- hardware.i2c12;
hardware.i2c12.configure(CLOCK_SPEED_100_KHZ);
const i2cA = 0x004;
i2cAddress <- i2cA << 1;
local force = i2c.read(i2cAddress,"\x85",2);
server.log (force);
Yes it is the same same senor you listed.
I tried out x84 aswell, but it was the same result. The values coming out should be in the specified range as listed in the datasheet.
Seems like a simple task, but the result is not so easy to achieve
What value are you getting for i2c.readerror()? This will help you debug — see Understanding I2C Errors.
Do you have pull-up resistors on the I2C lines SDA and SCL? The SingleTact does not include these resistors and neither does the imp, so you will need to add these if you haven’t already.
I am getting the error “-2”. I do have two pull-up resistors of 4.7k Ohm, to both SDA and SCL lines. I don’t quite get the meaning of how to initate to i2c read operation, the I2C guide is still a bit frustrating to me to clearly work this out.
So far i came up with this code. The sensor adress is 0x04, which i shifed by 1. From the datasheet in order to read the values i need to initate bytes 10,11 and read from bytes 132 and 133. Am i doing this correct?
hardware.i2c89.configure(CLOCK_SPEED_100_KHZ);
local i2c = hardware.i2c89;
local i2cAddress = 0x04 << 1;
server.log(“SingleTact i2cAddress:” + i2cAddress);
function readSensor(){
i2c.write(i2cAddress,“\x0A\x0B” + “”);
imp.sleep(1.0);
local force = i2c.read(i2cAddress,“\x84”,2);
server.log("Force = " + force);
if (force == null) server.log("i2c error "+ i2c.readerror());
}
readSensor();
The line local force = i2c.read(i2cAddress, "\x84", 2); should be sufficient to read back the two bytes at 0x84 and 0x85, but section 2.4.3 in the manual says “where a Read operation is not preceded by a Read Request operation the read location defaults to 128 (the sensor output location)”, so I would also try local force = i2c.read(i2cAddress,"\x80",2); and see what happens.
I don’t think you need the i2c.write(i2cAddress, "\x0A\x0B" + ""); so far as I can see from the manual — and all it’s doing is writing the value 0x0B to the register at 0x0A.
I tried x80 aswell, but got the same result - “null = 0 x0”, as far as adressing it should be 0x04, when i try to log adress it shows “8”, maybe this could be wrong? The slave adress should be 0x04.
8 is what you should expect there (4 >> 1 equals 4 x 2).
Your read ops are probably always going to give you null until we get the addressing issue sorted as this is preventing your code from reading from or writing to the sensor.
Try this code:
function debugI2C(i2c = null) {
if (i2c == null) throw "debugI2C() requires a non-null I2C object";
for (local i = 2 ; i < 256 ; i += 2) {
if (i2c.read(i, "", 1) != null) server.log(format("Device at 8-bit address: 0x%02X (7-bit address: 0x%02X)", i, (i >> 1)));
}
}
It’ll show up valid devices on the I2C bus (eg. hardware.i2c89) you pass in.
Looking at some other code, for the Raspberry Pi, it reads the data you want as follows:
// read sensor data
local force = i2c.read(0x04 << 1, "\x04", 2);
so you might want to try that too. That line is preceded by:
// write zero to baseline registers
i2c.write(0x04 << 1, "\x02\x29\x02\x00\x00\xFF");
I plugged the sensor in Arduino and changed the address, that was the issue.
Some data is coming out, but it is repeating , when set on a loop. Is there a way to loop this structure in order to get new values each reading?
My updated code:
hardware.i2c89.configure(CLOCK_SPEED_100_KHZ);
local i2c = hardware.i2c89;
local i2cAddress = 0x04 << 1;
const Measurment_freq = 5.0;
function readSensor()
{
local breakFlag = false;
function readData()
{
local force = i2c.read(i2cAddress, “\x04”, 2);
server.log(readData);
}
i2c.write(i2cAddress, “\x02\x29\x02\x00\x00\xFF”);
imp.wakeup(1.0,readData);
if (!breakFlag)
{
imp.wakeup(Measurment_freq,readSensor)
}
}
readSensor();
I was trying to create a loop, that shows value every 5s, so i could figure out the correct values that i would need, so i could convert the sensor data output to force (N).
Reading the datasheet, I don’t think your code is doing this correctly. When you do the read, you’re sending 0x04 as the first databyte - not the address - then reading 2 bytes. The datasheet indicates that if there’s no read command sent (and you’re not sending one) any read will read from offset 128, so that’s likely what you’re reading.
The first 2 bytes are “frame index”, which increment on every read, so you should see the number increase every 5 seconds. Do you see that?
What you seem to need to do is to read the “output digital scaling” value (bytes 10 & 11), and the “sensor baseline” value (bytes 41 and 42), and use these, combined with the sensor reading, to work out the force.
To read these two, you need to send a read command to the chip as noted in section 2.4.2. Something like this should work:
// Get output digital scaling 16 bit value
i2c.write(i2cAddress, "\x01\x0a\x02\xff"); // read command, 2 bytes at offset 10
local ods_raw = i2c.read(i2cAddress, "", 2);
local outputdigitalscaling = (ods_raw[0]<<8)+ods_raw[1];
// Get sensor baseline 16 bit value
i2c.write(i2cAddress, "\x01\x29\x02\xff"); // read command, 2 bytes at offset 41
local bl_raw = i2c.read(i2cAddress, "", 2);
local baseline = (bl_raw[0]<<8)+bl_raw[1];
// Now read the actual value; no read command is required to be sent first, but we'll read 6 bytes to fetch registers 128-133 inclusive
local reading = i2c.read(i2cAddress, "", 6);
local frameindex = (reading[0]<<8)+reading[1];
local timestamp = (reading[2]<<8)+reading[3];
local sensoroutput = (reading[4]<<8)+reading[5];
// calculate singletact_output per datasheet section 2.5. Note multiplication of outputdigitalscaling by a float to ensure a float division happens
local output = ( (sensoroutput - baseline) / (1.0 * outputdigitalscaling) ) + 255;
server.log(format("frameindex %d, timestamp %fs, output %f", frameindex, timestamp*0.0001, output));
Oh, I should have noted - you should only need to read scaling/baseline once at powerup (those appear to be factory calibrated values which will always be the same for a particular device).
The 6 byte read and calculation can then be done any time you need to read force.