rccursach
rccursach

Reputation: 315

Parsing float from i2c with ruby on raspberry pi

this is mostly a ruby question.

I'm stuck trying to parse some bytes from an i2c device to a float value on ruby.

Long story:

I'm trying to read a float value from an i2c device with raspberry pi and an AtTiny85 (the device). i'm able to read its value from console through i2ctools

Example:

i2cset -y 0 0x25 0x00; sleep 1; i2cget -y 0 0x25 0x00; i2cget -y 0 0x25 0x00; i2cget -y 0 0x25 0x00; i2cget -y 0 0x25 0x00

Gives me:

0x3e
0x00
0x80
0x92

that means 0.12549046, which is a value in volts that i'm able to check with my multimeter and is ok. (the order of the bytes is 0x3e008092)

Now i need to get this float value from a ruby script, I'm using the i2c gem.

A comment on this site suggest the following conversion method:

hex_string = '42880000'
float = [hex_string.to_i(16)].pack('L').unpack('F')[0]
# => 68.0

float = 66.2
hex_string  = [float].pack('F').unpack('L')[0].to_s(16)
# => 42846666

But i haven't been able to get this string of hex values. This fraction of code:

require "i2c/i2c"
require "i2c/backends/i2c-dev"

@i2c = ::I2C.create("/dev/i2c-0")
sharp = 0x25

@i2c.write(sharp, 0)

sleep 1

puts @i2c.read(sharp, 4).inspect

Puts on screen

">\x00\x00P"

Where the characters '>' and 'P' are the ASCII values of the byte in that position, but then i cannot know where/how to split the string and clean it up to at least try the method showed above.

I could write a C program to read the value and printf it to console or something and run it from ruby, but i think that would be an awful solution.

Some ideas on how can this be done would be very helpful!

Greetings.

Upvotes: 0

Views: 572

Answers (2)

matt
matt

Reputation: 79793

You probably just need to use unpack with a format of g, or possible e depending on the endianness.

@i2c.read(sharp, 4).unpack('g')[0]

The example you are referring to is taking a string of hex digits and first converting it to a binary string (that’s the [hex_string.to_i(16)].pack('L') part) before converting to an integer (the L directive is for 32 bit integers). The data you have is already a binary string, so you just need to convert it directly with the appropriate directive for unpack.

Have a read of the documentation for unpack and pack.

Upvotes: 0

rccursach
rccursach

Reputation: 315

I came with something:

bytes = []

for i in (0..3) do
  bytes << @i2c.read_byte(sharp).unpack('*C*')[0].to_s(16)
  bytes[i] = "00" unless bytes[i] != "0"
end

bytes = bytes.join.to_s
float = [bytes.to_i(16)].pack('L').unpack('F')[0]

puts float.to_s

Not shure about unpack(' * C * ') though, but it works. If it's a better way to do it i'd be glad with another answer.

Greetings!

Upvotes: 0

Related Questions