drivers/srf04: range finder sensor#9035
drivers/srf04: range finder sensor#9035haukepetersen merged 2 commits intomasterfrom unknown repository
Conversation
jnohlgard
left a comment
There was a problem hiding this comment.
I don't have the hardware to test this, but it looks like a reasonable draft at first glance. There are a few issues with the design that should be addressed however, see my comments below. There are also a few copy paste mistakes in the documentation where there are references to another driver or the wrong docs, and some missing docs
drivers/include/srf04.h
Outdated
| * @brief GPIO pins for hc-sr04_t device | ||
| */ | ||
| typedef struct { | ||
| srf04_params_t p; |
There was a problem hiding this comment.
I understand you copied the structure for this driver from one of the existing drivers, but this modifiable params struct is what I would call an antipattern in RIOT. A better solution is to change this to const srf04_params_t * to let the parameters reside in ROM.
There was a problem hiding this comment.
According to this page, this is a recommended pattern. But your concern sounds valid. We have the same question in #7067. @haukepetersen what's your opinion ?
drivers/include/srf04.h
Outdated
| gpio_t trigger; | ||
| gpio_t echo; | ||
| uint32_t distance; | ||
| uint32_t time; |
There was a problem hiding this comment.
I suggest moving the time, distance, and state to the srf04_params_t struct and keep only the hardware configuration parameters in srf04_params_t
drivers/include/srf04.h
Outdated
| * @return SRF04_OK on success | ||
| * @return SRF04_GPIO on gpio init failure | ||
| */ | ||
| int srf04_init(srf04_t *dev, const gpio_t trigger, const gpio_t echo); |
There was a problem hiding this comment.
Get rid of the gpio arguments here and place the configuration in srf04_params.h
drivers/include/srf04.h
Outdated
| } srf04_params_t; | ||
|
|
||
| /** | ||
| * @brief GPIO pins for hc-sr04_t device |
| SRF04_IDLE, | ||
| } srf04_state_t; | ||
|
|
||
| typedef struct { |
drivers/include/srf04.h
Outdated
| SRF04_INT = -2, /**< error initializing gpio interrupt*/ | ||
| }; | ||
|
|
||
| typedef enum { |
drivers/include/srf04.h
Outdated
| * | ||
| * @param[in] dev device descriptor of sensor | ||
| * | ||
| * @return SRF04_OK |
There was a problem hiding this comment.
Does it always return this value? Is there any reason to have a return value then?
drivers/srf04/include/srf04_params.h
Outdated
| /** @} */ | ||
|
|
||
| /** | ||
| * @brief BMX055 configuration |
drivers/include/srf04.h
Outdated
| */ | ||
| enum { | ||
| SRF04_OK = 0, /**< exit without error */ | ||
| SRF04_GPIO = -1, /**< error initializing gpio*/ |
There was a problem hiding this comment.
I would slightly update the name of the error enums by introducing ERR: SRF04_ERR_GPIO, etc
drivers/include/srf04.h
Outdated
| * @brief GPIO pins for hc-sr04_t device | ||
| */ | ||
| typedef struct { | ||
| srf04_params_t p; |
There was a problem hiding this comment.
According to this page, this is a recommended pattern. But your concern sounds valid. We have the same question in #7067. @haukepetersen what's your opinion ?
drivers/srf04/include/srf04_params.h
Outdated
| { .state = SRF04_IDLE, \ | ||
| .trigger = SRF04_PARAM_TRIGGER, \ | ||
| .echo = SRF04_PARAM_ECHO, \ | ||
| .distance = (0U), \ |
There was a problem hiding this comment.
Why do you need distance in the initialization parameters ? Same question for time and I also saw below that state is also added in the parameter struct. I think those 3 should go in the device descriptor.
drivers/srf04/srf04.c
Outdated
| dev->p.time = 0; | ||
|
|
||
| if ((gpio_init(dev->p.trigger, GPIO_OUT) != 0) | | ||
| (gpio_init(dev->p.echo, GPIO_IN) != 0)){ |
drivers/srf04/srf04.c
Outdated
|
|
||
| if ((gpio_init(dev->p.trigger, GPIO_OUT) != 0) | | ||
| (gpio_init(dev->p.echo, GPIO_IN) != 0)){ | ||
| DEBUG("[srf04] Error: could not initialize GPIO\n"); |
There was a problem hiding this comment.
I would split this check in 2: one for the trigger pin, one for the echo pin
tests/driver_srf04/README.md
Outdated
| This example shows the usage of an srf04 module. | ||
| The application exerts a timer and two gpio. | ||
| The module is an ultrasonic range finder with a small protocoll, | ||
| hence an implementation can be kept simple. |
There was a problem hiding this comment.
I don't think this comment is very useful. Better detail the mentioned protocol ? or give a link to a datasheet.
tests/driver_srf04/README.md
Outdated
| Build, flash and start the application: | ||
| ``` | ||
| export BOARD=your_board | ||
| make -j 2 flash term |
There was a problem hiding this comment.
Missing closing ```
Or just indent the block with 4 spaces (and remove the opening ```). I would put the command line on a single line and remove -j2
tests/driver_srf04/main.c
Outdated
|
|
||
| srf04_init(&dev, srf04_params[0].trigger, srf04_params[0].echo); | ||
|
|
||
| while(1) { |
tests/driver_srf04/main.c
Outdated
|
|
||
| int main(void) | ||
| { | ||
| printf("SRF04 range finder example\n"); |
tests/driver_srf04/main.c
Outdated
|
|
||
| xtimer_usleep(SAMPLE_PERIOD); | ||
|
|
||
| printf("D: %d mm\n", (((srf04_read(&dev) * 100) / 292) / 2)); |
There was a problem hiding this comment.
Here, I think it would be better to extend the driver API with a srf04_read_distance that return the mesure in mm. The computation seems weird (x * 100 / 292 / 2). And better define constants in the driver header instead of hard coded values.
haukepetersen
left a comment
There was a problem hiding this comment.
Some ideas for optimizing the mem usage and making the API functions more safe against miss-usage.
drivers/srf04/srf04.c
Outdated
| #define SRF04_DISTANCE (584U) | ||
| #endif | ||
|
|
||
| static bool state; |
There was a problem hiding this comment.
thie variable is not used, right?!
drivers/srf04/srf04.c
Outdated
| uint32_t t = xtimer_now_usec(); | ||
|
|
||
| srf04_t* dev = (srf04_t*)arg; | ||
| if (dev->state == SRF04_IDLE) { |
There was a problem hiding this comment.
we could simply use dev->distance for keeping the state:
if (dev->distance > SRF04_MEASURING) {
.... // start new measurement
}
else {
... // save actual distance time
}
drivers/include/srf04.h
Outdated
| typedef enum { | ||
| SRF04_MEASURING, | ||
| SRF04_IDLE, | ||
| } srf04_state_t; |
There was a problem hiding this comment.
I suggest a small change here, which would allow us to keep state using the distance variable, and using these values as return codes as well:
enum {
SRF04_OK = 0,
SRF04_INVALID = -1,
SRF04_MEASURING = -2,
}; // no need for typing thisThere was a problem hiding this comment.
and simply append the error codes from above with -3, -4...
| * | ||
| * @param[in] dev device descriptor of sensor | ||
| * | ||
| * @return time of flight in ms |
There was a problem hiding this comment.
add something like
@return time of flight in ms (>= SRF04_OK)
@return SRF04_MEASURING if measurement is in progress
@return SRF04_INVALID if no valid measurement is available
| * | ||
| * @param[in] dev device descriptor of sensor | ||
| * | ||
| * @return time of flight in mm |
drivers/srf04/srf04.c
Outdated
|
|
||
| if (gpio_init_int(dev->p.echo, GPIO_IN, GPIO_BOTH, _cb, (void*)dev) != 0) { | ||
| DEBUG("[srf04] Error: could not initialize GPIO echo pin\n"); | ||
| return SRF04_ERR_INT; |
There was a problem hiding this comment.
I think I would simplify here and drop the explicit ERR_INT value in favor of returning SRF04_ERR_GPIO in both cases.
| void srf04_trigger(const srf04_t* dev) | ||
| { | ||
| gpio_irq_enable(dev->p.echo); | ||
|
|
There was a problem hiding this comment.
maybe it makes sense to add a check so we don't trigger a new measurement while one is in progress:
if (dev->distance == SRF04_MEASURING) {
return;
}
drivers/srf04/srf04.c
Outdated
|
|
||
| int srf04_read_distance(const srf04_t* dev) | ||
| { | ||
| return ((dev->distance * 100) / SRF04_DISTANCE); |
There was a problem hiding this comment.
here we should also add a check:
if (dev->distance >= SRF04_OK) {
return ((dev->distance * 100) / SRF04_DISTANCE);
}
return dev->distance;
tests/driver_srf04/main.c
Outdated
|
|
||
| xtimer_usleep(SAMPLE_PERIOD); | ||
|
|
||
| printf("D: %d mm\n", srf04_read_distance(&dev)); |
There was a problem hiding this comment.
with my proposed changes, it could be nice to check the return value of srf04_read_distance() and print an explicit error message in case the value is not valid (val < SRF04_OK).
| * @return time of flight in mm | ||
| */ | ||
| int srf04_read_distance(const srf04_t* dev); | ||
|
|
There was a problem hiding this comment.
last proposal: how about a convenience function that combines _trigger() and read_distance() together with the mandatory wait time?! This could be useful for very simple use cases (as e.g. the example application below...).
|
@SemjonKerner I guess you 'll ping the reviewers once you addressed their comments? |
|
@PeterKietzmann as far as i recall, all comments are implemented. Since i am on parental leave i have no option to check if my changes are working on the real hardware. I will update and inform everyone when i get back in August, if this is ok with you. If someone needs the driver as fast as possible, then i can make it happen sooner. |
|
No stress from my side. In general it makes sense to inform or even ping reviewers after you addressed their comments. @haukepetersen, @gebart, @aabadie if your comments were addressed, please remove the change request. |
drivers/srf04/srf04.c
Outdated
|
|
||
| int srf04_init(srf04_t* dev) | ||
| { | ||
| dev->p = srf04_params[0]; |
There was a problem hiding this comment.
This limits the driver to a single hardware sensor. I suggest adding an argument to the init function, passing a pointer to a params struct. See other device drivers in the tree, for example https://github.com/RIOT-OS/RIOT/blob/master/drivers/io1_xplained/io1_xplained.c#L37-L39
aabadie
left a comment
There was a problem hiding this comment.
Found minor things remaining: improvement in driver dependencies, test application makefile, documentation.
tests/driver_srf04/Makefile
Outdated
|
|
||
| RIOTBASE ?= $(CURDIR)/../.. | ||
|
|
||
| FEATURES_REQUIRED = periph_gpio |
There was a problem hiding this comment.
This is not needed anymore with this added in the driver module dependencies.
tests/driver_srf04/Makefile
Outdated
| @@ -0,0 +1,11 @@ | |||
| APPLICATION = driver_srf04 | |||
There was a problem hiding this comment.
Use include ../Makefile.tests_common instead of that line.
drivers/include/srf04.h
Outdated
| * @brief GPIO pins for srf04 device | ||
| */ | ||
| typedef struct { | ||
| gpio_t trigger; |
There was a problem hiding this comment.
The attributes of this struct are not documented, same for the srf04_t struct below.
drivers/include/srf04.h
Outdated
| * @brief Status and error return codes | ||
| */ | ||
| enum { | ||
| SRF04_OK = 0, /**< exit without error */ |
There was a problem hiding this comment.
Maybe align a bit the values and comments
|
Hey @haukepetersen , @gebart , @aabadie finally there is a new commit fixing your requested changes. |
|
@haukepetersen @gebart @aabadi just a small reminder |
aabadie
left a comment
There was a problem hiding this comment.
My comments are addressed but I didn't test. Just a minor and non blocking comment.
Untested-ACK
|
|
||
| while (1) { | ||
| distance = srf04_get_distance(&dev); | ||
| if (distance < SRF04_OK) { |
There was a problem hiding this comment.
This check is weird since the function can return a distance or error codes (negative values). Maybe just compare to 0 ? Or use a switch and address each potential error code ?
There was a problem hiding this comment.
Comparing to SRF04_OK is the same thing, since the enum defines it as zero and all errors would evaluate negative. I could distinguish between errors, but i deem it not necessary.
There was a problem hiding this comment.
I thought about your concern and now i believe to understand what you meant. Distance should only be SRF04_OK (zero), when srf04_init returns successfully. After the first get_distance (wich happens before) it will either have a valid distance (greater or equal to 0) or give an error. So comparing to SRF04_OK is just meant to be a clean way of checking for new data.
As the sensor only measures values from a minimum distance of i think 3 cm it should always be greater then zero when successfull.
There was a problem hiding this comment.
I'd say the check is fine. Maybe more intuitive would simply be to test for distance < 0, semantically considering the 0 as distance value instead of some arbitrarily defined named value. But I'd say this is up to the creative freedom of the author :-)
haukepetersen
left a comment
There was a problem hiding this comment.
Some tiny details, else ok.
drivers/include/srf04.h
Outdated
| * @note for inch define "SRF04_DISTANCE (1480U)" | ||
| */ | ||
| #ifndef SRF04_DISTANCE | ||
| #define SRF04_DISTANCE (584U) |
There was a problem hiding this comment.
as discussed offline: drop the support for inches and move this define into the source file
drivers/include/srf04.h
Outdated
| /** | ||
| * @brief defines sensor required sample time | ||
| */ | ||
| #define SRF04_SAMPLE_PERIOD (50U * US_PER_MS) |
There was a problem hiding this comment.
this is static and has nothing to do with the interface -> move into source file
|
|
||
| /** | ||
| * @brief Convenience function triggers a measurement and returns distance | ||
| * |
There was a problem hiding this comment.
I would add a comment about the blocking nature of this function (will only return after the new value is acquired, which takes ~50ms..).
drivers/srf04/srf04.c
Outdated
| } | ||
| return dev->distance; | ||
| } | ||
|
|
There was a problem hiding this comment.
duplicate newline :-)
tests/driver_srf04/main.c
Outdated
|
|
||
| srf04_t dev; | ||
| srf04_init(&dev, &srf04_params[0]); | ||
| int distance; |
There was a problem hiding this comment.
scope: cppcheck will probably complain that distance should be define inside the while loop below
tests/driver_srf04/main.c
Outdated
| puts("SRF04 range finder example"); | ||
|
|
||
| srf04_t dev; | ||
| srf04_init(&dev, &srf04_params[0]); |
There was a problem hiding this comment.
for demonstration purposes, it is good practice to check the return value of the init function, e.g.
if (srf04_init(dev, params) != SRF04_OK) {
puts("error initializing...");
return 1;
}or similar..
|
|
||
| while (1) { | ||
| distance = srf04_get_distance(&dev); | ||
| if (distance < SRF04_OK) { |
There was a problem hiding this comment.
I'd say the check is fine. Maybe more intuitive would simply be to test for distance < 0, semantically considering the 0 as distance value instead of some arbitrarily defined named value. But I'd say this is up to the creative freedom of the author :-)
|
@haukepetersen fixes are commited. |
haukepetersen
left a comment
There was a problem hiding this comment.
Concept, arch, code, and style are good, also tested the PR and it functions as expected -> ACK
please squash
|
My comments seem to have been addressed |
|
all green -> lets go @SemjonKerner nice job, thanks! |
This is an initial driver implementation for the srf04 ultra sonic range finder. The driver provides functions for initializing the gpio, triggering a measurement and reading out the measured data. This PR also provides a very simple test.