Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESP32: Sonar Module #3090

Closed
wants to merge 3 commits into from
Closed

ESP32: Sonar Module #3090

wants to merge 3 commits into from

Conversation

jpeletier
Copy link
Contributor

  • This PR is for the dev-esp32 branch rather than for master.
  • This PR is compliant with the other contributing guidelines as well (if not, please describe why).
  • I have thoroughly tested my contribution.
  • The code changes are reflected in the documentation at docs/*.

This PR introduces a new sonar module. This module drives the typical ultrasound based sonars, such as the HC-SR04. These sensors are activated by a "trig" pin that must be held high for a few microseconds. Then, an ultrasound is emitted and when it bounces back some surface, an echo is received. The sensor sends back to the microcontroller a pulse as long as the time it took for the echo to come back. Distance can be then calculated by multiplying the total travel time by the speed of sound.
HC-SR04

You can learn more about this sensor in this detailed guide.

ESP32/ESP-IDF implementation details

Internally, this module uses the RMT infrared driver hardware present in ESP32 for accurate pulse measurement. Each sonar object takes two RMT channels, one for sending and another for receiving. There are 8 RMT channels in ESP32, so up to 4 sonars can be driven simultaneously. Code could be optimized to share these channels in the future.

Hardware connection:

Connecting this sensor takes 2 GPIO pins. One to send the trigger signal and the other to listen to the echo pulse.

HC-SR04 is driven by 5V, therefore care must be taken to bring down the echo signal to the 3.3V the ESP32 operates at, for example with a simple voltage divider in which R2 = 2*R1

You can follow this schematic as a guide:

How to connect ESP32 to HC-SR04

LUA API

Example

t = tmr.create()
s = sonar.new(25, 5)
t:alarm(500, tmr.ALARM_AUTO, function()
    s:ping(function(d)
        if d == sonar.DISTANCE_OUT_OF_RANGE then
            print("no obstacle detected")
        else
            print("distance to obstacle:", d)
        end
    end)
end)
t:start()

sonar.new()

Creates a sonar driver.

Syntax

sonar.new(trigPin, echoPin)

Parameters

  • trigPin GPIO pin connected to HC-SR04 Trig pin
  • echoPin GPIO pin connected to HC-SR04 Echo pin

Returns

Sonar object (sonarObj) that exports the ping() method.

sonarObj:ping()

Sends out a sonar ping. The callback will be invoked passing the distance in mm as the only parameter, or the special constant sonar.DISTANCE_OUT_OF_RANGE if no obstacle is detected.

Syntax

sonar.ping(callback)

Parameters

  • callback: Function that will be called when the ping bounces back or no echo is detected.

Returns

nothing

@jpeletier jpeletier changed the title Sonar ESP32: Sonar Module May 6, 2020
@vsky279
Copy link
Contributor

vsky279 commented May 6, 2020

Hi @jpeletier Javier, I have a project for ESP8266 using this sonar device to measure level of water in a watertank. I wonder whether you need a Lua C module for something that can be achieved with few lines of Lua code. I extracted minimum example from my project:

local PIN_T = 7 -- GPIO13 - trigger
local PIN_E = 6 -- GPIO12 - echo

local alarm_trigger = tmr_create()

local ts = 0

local function echo(level)
    local delay = tmr.now()-ts
    if delay < 0 then delay = (delay + 0x7FFFFFFE) + 1 end  -- workaround for #1691
    if delay <= 28876 then -- 5 meters
        local dist = (delay - 150) * 0.017315 -- this needs proper calibration for each implementation
        print("Distance: ", dist)
    end
end

local function trigger()
    gpio.serout(PIN_T, gpio.LOW, {50, 100}, 1, function() ts = tmr.now() end)
end

gpio.mode(PIN_E, gpio.INT)
gpio.trig(PIN_E, "down", echo)
gpio.mode(PIN_T, gpio.OUTPUT)

alarm_trigger:alarm(350, tmr.ALARM_AUTO, trigger)

To get reliable measurement I am performing n (=10) measures in sequence and then I calculate median of results.

Also I think you should not be using malloc and free in your Lua C code and use lua_newuserdata. It seems to me that this module is in many ways similar to the net_info module (PR #2854) that is now well C Lua-wise designed thanks to @TerryE. So this can be a good inspiration 😄 for your module.

@pjsg
Copy link
Member

pjsg commented May 6, 2020

Note that trig callback function actually includes (as an argument) the timestamp of the edge: https://nodemcu.readthedocs.io/en/dev/modules/gpio/#gpiotrig

@vsky279
Copy link
Contributor

vsky279 commented May 6, 2020

@pjsg, was it there always? I had to miss it.
Measurements should be more precise when using the feature.

@pjsg
Copy link
Member

pjsg commented May 6, 2020 via email

@vsky279
Copy link
Contributor

vsky279 commented May 6, 2020

Good. My project is older than that.

@jpeletier
Copy link
Contributor Author

@vsky279 Thank you for your comments.

I looked at your implementation in LUA, but in the ESP32 gpio.serout() is not implemented, but that is not an excuse, we should have it soon.

In any case, I worry measurements will not be as accurate. Using the ESP32 RMT hardware to measure yields very accurate time measurements that are not affected by RTOS context switches. For measuring something slow like a water tank it may not be as useful, but for a fast moving object, like a robot car, I think is good to be able to take a good number of samples per second to detect if something is near or just crossed by, without overloading the VM too much.

In my applications for NodeMCU, I try to leave hardware as much as possible to the C world, offloading Lua VM and keeping it for configuration and business logic. For this module, I did some research on how to drive the sonar best with an ESP32 and distilled what I thought was the best approach into this PR.

Regarding your comment on userdata vs malloc/free, I took a look at PR #2854 (nice job!) and the discussion around userdata, however I don't see why the overhead of creating a Lua userdata object is necessary when the information I am using malloc/free for is tiny and unrelated to Lua. Note also that I am using luaM_malloc() to make sure Lua is aware of the memory I am acquiring, not the raw C malloc()

Thank you again for the comments!

@vsky279
Copy link
Contributor

vsky279 commented May 7, 2020

@jpeletier Your motivation to have it in C Lua module seems to be reasonable.
I don't know if the precision achievable via Lua would be so different. Maybe not. Especially when the trig callback has the timestamp as pointed out above.
Anyway I know only little about ESP32 and its RMT module.

I have only one somewhere here with a camera (unfortunately not yet supported by NodeMCU) and did not found nice application for the time being. 😄

@stale
Copy link

stale bot commented Jun 16, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Jun 16, 2021
@stale stale bot closed this Jul 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants