Unsupported Sensors? Try Arduino + Lua!

How did you convert to celcius?

I had a very early version of the example script thats changed a lot to the latest version, but I had a look and I think you need to change line28 uint8_t units = USE_FAHRENHEIT; to USE_CELSIUS.

1 Like

@Yuri_Rage can you help?

I am trying to log multiple DS18B20 sensors in the data-flash logs.

I have modified the BS18B20.lua script with this additional line:

logger.write(‘MOTE’,‘Mt’ … idx,‘f’,‘O’,’-’,val)

Assuming that the same syntax used for the “gcs:send_named_float” would work. It does indeed add a new entry into the logs “MOTE” and stores temperature for MT0 but I am also expecting to see MT1, MT2 etc but I do not. What am I doing wrong?

See here for entire function

function update()
    for idx = 0, num_sensors - 1 do
        -- request to store sensor temperature in I2C registers for given index
        arduino_i2c:write_register(SET_SENSOR_INDEX, idx)
        -- now read the register data and collect its value as a float
        local val = unpack_ieee754_float(read_register_data())
        gcs:send_named_float('Mt' .. idx, val)
		logger.write('MOTE','Mt' .. idx,'f','O','-',val)	
						
    end
    return update, RUN_INTERVAL_MS
end

@tegwin,

It looks like each log entry must be fully formed at write time via a table of values or a discrete argument list. Try this:

function update()
    local log_data = {}
    local log_names = {}

    for idx = 0, num_sensors - 1 do
        -- request to store sensor temperature in I2C registers for given index
        arduino_i2c:write_register(SET_SENSOR_INDEX, idx)
        -- now read the register data and collect its value as a float
        local val = unpack_ieee754_float(read_register_data())
        gcs:send_named_float('Mt' .. idx, val)
        log_names[idx + 1] = string.format('Mt%d', idx)
        log_data[idx + 1] = val
    end
    logger.write('MOTE', table.concat(log_names, ', '), string.rep('f', #log_data), table.unpack(log_data))
    return update, RUN_INTERVAL_MS
end

(I edited this post a couple of times with increasingly elegant solutions)

1 Like

That works perfectly, thank you so much. This is going to be so useful for detecting failures and performance monitoring.

I have two questions for you.

  1. With multiple temperature sensors, what is the best way of identifying which physical sensor relates to which message? Is there some logic behind their numbering?
  2. I am using a Matek L431 Can node at each motor - in an ideal world I would have an arduino connected via i2c to each node with a temperature probes for each motor. Have you experimented with the L431? Any thoughts on if this might work to enable logging or each motor temp over the canbus?

You can eliminate the string length check - see my most recent edit.

There is no rhyme/reason to the sensor indexing other than that the sensors are found/reported in address order, which seems to be consistent, so I don’t think there’s a need to double check their position from one run instance to the next, so long as the sensors are not physically replaced (or additional sensors added).

You could add a method to retrieve the DS18B20 addresses to the Arduino sketch example if you wanted to be more specific in the indexing.

You’ll have to do some experimentation to determine which sensor corresponds to each index value.

I have not used the L431 at all yet, but it’s on my list of things to try. I have some mRo CAN nodes on hand and have used them to put serial GPS modules on the CANbus. I have not yet tried anything “fancier” than that, but I don’t see why you couldn’t get some I2C comms working using the method you describe.

1 Like

@Yuri_Rage Thank you.

I will have a poke around in AP_periph to see if I can get the i2c data from the arduino into an L431. All a bit new to me but with examples like yours its a bit easier to start to figure out how things work together.

2 Likes

@Yuri_Rage thanks for sharing this. I’m trying to implement for a 16bit digital flow sensor that uses a 2 byte register for flow measurement. I was able to get the sensor working on Arduino uno but am having issues outputting to ardupilot. When running the LUA script, I get ‘did not return a function error (0x0)’.

Code:
sfm3300_ardunio_to_px4.lua (3.1 KB)
sfm3300_ardunio_ino.txt (2.1 KB)

The problem looks to be either with implementation of I2C_slave on the arduino sketch or with the LUA script itself.

The lines circled in red below are the only I2C_slave commands that I’ve added to my existing ardunio sketch. Following this, there is a loop that continuously runs a function to output and print data from a single flow measurement register (flowSensor.begin() function is called from a header file). See attached sfm3300_ardunio_ino for full code.

With my LUA script, I suspect the issue is most likely in the update function shown below. I am unsure whether I’m using the correct register in the write_register function on line 81. I currently have the 16bit flow measurement register.

Would very much appreciate if you’d be able to take a look and provide any feedback - thanks.

@Yuri_Rage I updated both the ardunio and lua scripts to simplify- here’s where I landed. LUA script now runs, but outputs 0 instead of the expected flow rate. Any ideas on the issue here? Thanks

Arduino Sketch with I2C Slave commands:

LUA Script:

Code:
sfm3300_ardunio_to_px4.lua (2.0 KB)

Assuming the sketch code to read your sensor data is valid (which it does not appear to be since you seem to be ignoring bytes and also attempting to store them in varying types), you’re sending a float value but not collecting it properly in Lua. Look at the DS18B20 example. Floats are 4 bytes and have to be “reassembled” on the autopilot side. It would make more sense to send the raw data and do the conversion in Lua.

Additionally, you’re giving the Arduino the same address as the sensor, so even if any of the code was functional, it would be unpredictable in results.

But…since your device already appears to be an I2C device, there really isn’t a need to use my library. You could just interact with it directly from the autopilot via Lua.

The intent of this project is to provide data from a non-I2C device to the autopilot, using the Arduino and I2C as a conduit.

Thanks for the advice.I resorted to ardunio + lua because the standard ardupilot firmware can only read I2C devices with 8 bit registers and my device uses 16 bit registers.

I see the unpack_ieee754_float() function implemented in the ds18b20 example but am struggling to understand how it works and what needs to be changed for my use case.

My virtual registers should be 2 bytes where the 1st byte (register 0) stores the size of the data and the second stores the actual data (register 1). It looks like the unpack_ieee754_float() function in the ds18b20 example takes a 4x1 table as the input (perhaps to account for the additional data registers?) where I need to take a 2x1 table.

1 Like

Use your Arduino sketch to read the data and store it in an integer or float as needed. Use the example scripts to see how those types can be handled across the I2C interface between the Arduino and the autopilot.

And be careful - the library uses the Wire object already. If you reinitialize it for use with another I2C device, you may get strange results (I wrote this as a way to interface non-I2C devices).

1 Like

Thanks @Yuri_Rage . I was able to get a very basic example working where I write a fixed float using Slave.writeRegisters() in the Arduino sketch, then unpack the 32 bit number in LUA via unpack_ieee754_float() and output to Mission planner.

When I apply the same methodology to my sensor, read_register_data() outputs a nil table. So there appears to be an issue with reading the register from the Arduino in LUA. I verified that the Arduino sketch outputs the intended float data from the sensor, but when I connect the Arduino + sensor to the PX4 I2C port and run the LUA script there is an issue collecting the data (even before being unpacked).

The only issue I can think of here is incorrect inputs to the arduino_i2c = i2c.get_device(I2C_BUS, SLAVE_ADDR) function. In the LUA script, I set the SLAVE_ADDR to 0x09 (not the sensor address 0x40) which is the address used to designate the Arduino board. I also verified that I set the correct I2C_BUS index.

I’m not really sure what else to try at this point, would appreciate any other troubleshooting recommendations you may have. Thanks.

LUA script:

I suspect this has to do with mishandling the Wire object in your Arduino code.

I am using Wire.write() and Wire.read() to read the data register in the sketch. To avoid reinitializing, do I need to find a workaround that does not use wire object at all?

You should use the TwoWire library and different SDA/SCL pins. Or rewrite my library to do that.

What you’re doing now will be entirely ineffective.

Again, I did not envision nor account for the use case where the Arduino will act as both master and slave.

1 Like

Hi @Yuri_Rage
I have a lua driver for a temperature sensor that is working well. I would now like to see it on my GCS, I can see it in Mission planner by sending a named float, but I also need to see it in QGC (on herelink). QGC allows me to view the temperatures of the battery, so if I can write to these values then I’ll be all set.
I think one can send a mavlink message to set these values, but I have no idea how to do this from within lua.
How would I go about setting the BATTERY_STATUS.Temperature value from within lua?

You can’t without a binding to the battery monitor backend. Like @iampete, I don’t have a solution for you if QGC doesn’t display custom float values.

Excellent work @Yuri_Rage thanks.
works great with Arduino nano, but could not compile on ESP32.
Got compilation error: " Compilation error: exit status 1 "
the error: "Arduino\libraries\I2C_Slave\src/I2C_Slave.h:58:46: error: ‘_registers’ declared as an ‘inline’ variable ".
The code line that triggers the error:
" inline volatile char _registers[BUFFER_LENGTH]; // BUFFER_LENGTH from Wire.h "

is it possible to eliminate it ?

That looks like a problem with your build environment. I did not write Wire.h, but I do know that Espressif often updates the libraries that get included with various IDEs.