fhunleth/elixir_ale

hc-sr04 distance sensor

Closed this issue · 7 comments

I am trying to use elixir_ale with the HC-SR04 sonar distance sensor. I have a repo set up showing the code I am using: https://github.com/idahogray/exhcsr04.

I am using the GPIO interrupt feature in my gen_statem state machine. The best pulse duration I can get is about 0.046 seconds which translates into a distance of 789cm, which is way outside the range of this sensor. I am curious if the interrupts are fast enough for this application.

When I run the Python code from https://www.modmypi.com/blog/hc-sr04-ultrasonic-range-sensor-on-the-raspberry-pi, I get the correct distance.

I have tried searching for other people using the HC-SR04 with Elixir but haven't found anything. Is there a better way to accomplish this?

The Erlang VM really isn't made for hard real-time tasks like what you're trying to do and elixir_ale is particularly slow for this. My general approach has been to offload these tasks to microcontrollers or timer hardware inside the main processor that isn't affected the whims of the Erlang VM or Linux schedulers. I suspect that for your project that all of what I said is overkill. I have a few ideas for you:

  1. Code a short C program that reads the sensor and prints the measurement to stdout. In Elixir, run the program like you would any other one and process the output. It won't isolate you from the Linux scheduler, but that's almost certainly ok if a Python version works.
  2. If you're running on Raspbian or if you're comfortable with making a custom Nerves system that includes Python, call the Python code from Elixir. I personally never do this, but I know a lot of people who are comfortable with Python and do this.
  3. Help me convert elixir_ale to use a NIF rather than a port. The latest versions of Erlang support calling select() on a file descriptor in NIF, and this is the key thing that's needed to handle GPIO interrupts. This feature didn't exist when I wrote elixir_ale, but based on a couple microbenchmarks I've run, I think this will make elixir_ale fast enough for you.

I'm not interested in Rust, but it's nothing against the language. Rust doesn't integrate with Nerves and support is barely available in Buildroot, so it's not an option for me. There's been very intermittent work over the past 2 years to improve the situation, but I'm not hopeful to see something soon. The saving part for elixir_ale is that almost all of the effort in the NIF will be figuring out enif_select. The NIF will be very little C code - much less than the current port implementation. As for NIF vs. port on Erlang VM stability, I've generally been opposed to using NIFs for that reason, but I've come around that elixir_ale is sufficiently trivial that it may be ok - certainly worth a try.

I had been thinking about writing this over the holidays as a fun project when I'm less busy with work. Of course, if you did want to try this in Rust, I'd be very interested to see how it turns out and would probably steal what you learned about enif_select for a pure C implementation that could be used with Nerves.

What is the latency that you need? "Dirty NIFs" would work, but they are only good down to 10us or so: https://potatosalad.io/2017/08/05/latency-of-native-functions-for-erlang-and-elixir

That's more than sufficient. 10us ~= 2 mm and this is for a car parking application. I'm sure that there are other parts of the system that will add more variability and he won't be using the same processor as in the blog post, but I don't think that a couple centimeters will be a problem and he can probably average readings to get better precision if necessary.

Also, I think we should be very clear that a switch to use a NIF doesn't mean that there are any hard real time guarantees and that you will have glitches measurable in milliseconds (maybe even 10s of ms). It should be possible to filter the glitches since they'll be beyond the range of the sensor.

I completely understand about Rust and Nerves, I didn't realize it wasn't supported by Nerves.

I wouldn't mind helping you out on the NIF implementation but I don't want to hold you up because of my limited experience with doing anything serious with C.

I think I might also take another look at option 1 but written in C.

Feel free to close this issue if you want.

Sounds good. Let me know how things go.