Unsupported Sensors? Try Arduino + Lua!

Glad to see it!

I pushed a commit to add the following to the beginning of the example script:

-- Cube and Matek autopilots typically make I2C bus 0 available
-- some Pixhawk (and other) autopilots only expose I2C bus 1
-- set the I2C_BUS variable as appropriate for your board
local I2C_BUS           =    0

...

local arduino_i2c = i2c.get_device(I2C_BUS, SLAVE_ADDR)
1 Like

At the request of @geofrancis, the repo now includes another example for sampling all of the Arduino’s analog pins and viewing their values as named floats.

analogRead() returns a signed int. It would’ve been easier to write the example by casting the value to an unsigned integer (or ignoring the sign, since the value should never be negative), but I wanted to show another example of data unpacking, so I kept the signed integer type intact.

The Lua unpack_int() function should work for any signed integer type, regardless of size, so long as endianness remains the same.


There is also now a very basic example included - this library’s equivalent of blink.ino. Make sure you’re using the latest 0.2.0 version or higher (now rebased as the only commit, since things are so fresh, and there wasn’t much to lose by squashing revision history to this point).

5 Likes

Hey Yuri!
Very kind for you to mention me, but I really haven’t contributed besides questions. However, I think you are chasing something that will really be useful for us and others. Onward my Friend! And sincere Thanks.

-Steve-

1 Like

The initial commit would’ve handled character buffers poorly. That’s now fixed, and there’s a “Hello world” example demonstrating it.

I am electing, at least for now, to forgo handling Arduino String types, since they are implemented as very heavyweight objects, and I expect that this library will often be used on small boards with limited memory. I’d rather leave that memory available for your use. Your sketch will compile if you attempt to store String variables in the virtual registers, but the results will be unpredictable and probably not desirable.

Compiler optimization would probably leave such an implementation out of any sketch that did not otherwise use String variables, so if there is a strong desire for their inclusion, I can revisit the topic.


If you didn’t want to worry about data/object unpacking in Lua, you could copy numeric values into character buffers in your sketches, using sprintf() for integer types and dtostrf() for floating point values. Store those character representations of your sensor values into the virtual registers. In your Lua script, use the tonumber() function to convert a retrieved string back to a numeric value for use in your script logic or named float display.

Awesome Job, thank you!!

Yesterday, I tried to connect Adafruit Precision NXP 9-DOF Breakout Board - FXOS8700 + FXAS21002 for its magnetometer sensor to have a compass on my copter with no luck.

I will try with your library now and lets hope that everything will be OK.

Thank you again!

I’m afraid there are some limitations within scripting, and I don’t see a way to make compass data available to the EKF through the scripting engine.

You’ll be able to get raw magnetometer data using my library and an accompanying script on your autopilot, but I don’t think there is a way to calibrate it and use it for native position and navigation.

As an alternative, Matek makes an inexpensive, firmware supported GPS/compass module.

If you were very motivated, you could have a look at ardupilot/AP_Compass_Backend.h and write a C++ class to extend it for direct support of your module or a scripting driver for native use of scripted compass modules. However, this will require a custom build of the firmware, and I don’t anticipate much motivation from the dev team to accept a pull request for either of these options, since supported GPS/compass modules are cheap and readily available (coupled with this concern for flash size).

1 Like

Thank you for your response.

Although motivated I am not in a position of writing this part of the firmware on my own yet. I have to finish first my Stroustrup books…! This sensor was just in my drawer (for another project) so i thought i could give it a try. So at the moment, the Matek GPS module seems an easier solution

Thank you again!

1 Like

@pketiki, it hadn’t quite occurred to me until I had a separate conversation about other unsupported hardware, but this library could potentially be used to emulate a supported device on the I2C bus. You’d have to know the address and data protocol for the supported hardware, and then write some code to translate data from the unsupported device into the format of the supported one, bypassing Lua scripting entirely. It’s probably not worth the time that it would take vs the cost of a supported magnetometer, but it can be done!


A little project update:

This morning I tested the library with an ESP32 and a Seeed XIAO (SAMD21) board. Things compiled and uploaded fine, but behavior was not quite what I expected. I did get data transfer over the I2C bus, but it was error prone.

I definitely want to support as many hardware variants as possible, and, in particular, the two boards I just mentioned. I know the ESP32 is very popular, and the XIAO boards are really cool little devices with tiny footprints that I feel are ideal for the application.

However, for the time being (v0.2.x), recommend sticking with an Atmega-based board until I can work through some of the oddities.

1 Like

I pushed a commit this morning that should make the library more widely compatible. The XIAO board is now working quite nicely.

However, ESP32 compatibility remains broken. The I2C slave implementation in the ESP32 Arduino framework has apparently been problematic for some time, and a somewhat recent PR to fix it does not appear to have provided a complete solution. I’m afraid I don’t have the depth of knowledge required to get it working properly.

1 Like

Hi @geofrancis, what are those nice multi pin sockets in the background of your last photo?

@pauljeff The big ones are 15 pin gx20 connectors. They come in 3 sizes, gx12, gx16 and gx20.

What if we don’t understand Lua ? :frowning:

You learn it, using examples and on-line videos.

haha nice suggestion

In a separate/private conversation, I was asked whether Lua provides a built-in map() function like the one provided by the Arduino libraries. I thought the answer might be of use to some of you:

To my knowledge, there is no map() function provided natively in Lua.

Here is the Arduino map() function from WMath.cpp:

long map(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

In Lua, it looks like this:

local function map(x, in_min, in_max, out_min, out_max)
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
end

But the Arduino function does integer math, so if you want (nearly) identical output:

local function map(x, in_min, in_max, out_min, out_max)
    return math.floor((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
end
2 Likes

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?