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

Introduce ESP32 ADC driver #1264

Open
wants to merge 2 commits into
base: release-0.6
Choose a base branch
from

Conversation

UncleGrumpy
Copy link
Collaborator

@UncleGrumpy UncleGrumpy commented Sep 9, 2024

This set of changes adds support for the ESP32 ADC peripheral, with documentation and examples.

Closes #250

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

Copy link
Contributor

@petermm petermm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested PR with elixir code successfully on c3card..

@UncleGrumpy
Copy link
Collaborator Author

After these changes are merged atomvm/exatomvm#26 should also be merged to enable Elixir use.

term raw = interop_kv_get_value_default(read_options, ATOM_STR("\x3", "raw"), FALSE_ATOM, ctx->global);
term voltage = interop_kv_get_value_default(read_options, ATOM_STR("\x7", "voltage"), FALSE_ATOM, ctx->global);

int adc_reading[samples_val];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is super dangerous, it might lead to stack corruption if a big enough value of samples_val is provided. I suggest allocating memory rather than this construction.
But, it looks like old array values are not even used, and it looks like we are just using the element at index i, am I wrong?

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you are absolutely right all the way around. I had just used a static array in my early testing, but completely forgot so change that when I implemented the code in the driver.

This should just be an int.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and tested.


struct UnitResource *unit_rsrc = (struct UnitResource *) obj;

if (!IS_NULL_PTR(unit_rsrc->unit_handle)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is unit_rsrc->unit_handle being NULL an unlikely/error situation?
Let's remember that IS_NULL_PTR is just a convenience macro (mostly aimed to allocation checks) to avoid to type every time UNLIKELY(a == NULL), and UNLIKELY purpose is to tell compiler that a branch is unlikely.
Also, I'm not sure that !LIKELY works as we expect.

The same comment applies to every occurrence of IS_NULL_PTR(...).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These occurrences should all be fixed now. The usage these was very ambiguous now that you point it out, and probably not at all what I intended.

term read_options = argv[2];
VALIDATE_VALUE(read_options, term_is_list);
term samples = interop_kv_get_value_default(read_options, ATOM_STR("\x7", "samples"), term_from_int32(DEFAULT_SAMPLES), ctx->global);
int samples_val = term_to_int32(samples);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are missing validity checks here. The default values guarantees that a value is always assigned, but it doesn't guarantee us that the value will be correct too.
Please, let's check they are integer, and let's also check the are in the accept valid range (e.g. what happens if a negative number is provided?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. That was an oversight, I try to be careful about always validating input parameters, I don't know why I forgot to here. I even did the calculation to determine the maximum number of samples that can be taken without overflowing the int that holds the accumulated results before diving by the number of samples. I don't know why I didn't just use that as a failsafe (except that over 62000 samples seemed an unrealistic concern).


const struct Nif *atomvm_adc_get_nif(const char *nifname)
{
TRACE("Locating nif %s ...", nifname);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how much this API can be made generic also for non EPS32 devices, if this operation is not straightforward I suggest calling this module esp_adc or something like that, so that would make the transition path to a generic and cross-platform adc API smoother.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point. When I first started I thought that stm32 was similar enough that they could use a common API. After closer examination it is quite different, and Pico will be more similar to stm32, but there are enough differences that starting with platform specific interfaces and later writing a common API is probably the best approach here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module, and Elixir exports have been renamed.

This driver offers resource based nif APIs using unit and channel resources, as
well as a set of convenient gen_server based APIs, which use pin numbers, as an
alternative to using the nif resource handles.

Unit 2 is currently disabled for the ESP32C3 by default due to known hardware
limitations, see:
https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/adc_oneshot.html#hardware-limitations

Unit 2 is currently disabled by default for the ESP32 (classic) due to WiFi
conflicts. This decision can be revisited after PR atomvm#1137 is merged, and the
WiFi driver can be successfully stopped after it has been started.

Unit 2 is enabled by default for all other chips with more than one ADC unit,
as the chips released after the ESP32 classic have a functional ADC arbitrator
peripheral that allows ADC unit 2 use while WiFi is enabled.

Signed-off-by: Winford <winford@object.stream>
Adds Erlang and Elixir examples for the ESP32 ADC driver, and documentation to
the Programmers Guide.

Signed-off-by: Winford <winford@object.stream>
@petermm
Copy link
Contributor

petermm commented Sep 26, 2024

retested on c3card - good to go!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants