Skip to content

Conversation

@baptleduc
Copy link
Contributor

@baptleduc baptleduc commented Jul 17, 2025

Contribution description

This PR adds initial support for the [Texas Instruments ADS1115 analog-to-digital converter (ADC)] (https://www.ti.com/lit/ds/symlink/ads1115.pdf?ts=1752581207840&ref_url=https%253A%252F%252Fro.mouser.com%252F) by introducing a dedicated RIOT driver.

The driver enables:

This implementation lays the groundwork for further extensions such the other ADS111x, comparator handling etc...

Testing procedure

Issues/PRs references

#21612

@github-actions github-actions bot added the Area: drivers Area: Device drivers label Jul 17, 2025
@baptleduc baptleduc changed the title feat(ads1115): Write an ads1115 drive feat(ads1115): Write an ads1115 driver Jul 17, 2025
Copy link
Contributor

@benpicco benpicco left a comment

Choose a reason for hiding this comment

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

Hello and welcome to RIOT!
Nice first driver, I added some comments - the static test scripts also added their 2¢

*/
typedef struct {
i2c_t i2c; /**< I2C device */
uint8_t addr; /**< I2C address */
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this configurable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you mean by ‘configurable’? This should depend on the board you're using. If you mean that it can be modified dynamically after init, no, that's not the case.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, some devices have a fixed I2C address (then there is no need to make it configurable in the code), for others the I2C address can be set via strap resistors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok so should I keep this like that ?

ads1115_comp_mode_t comp_mode; /**< Comparator mode */
ads1115_comp_polarity_t comp_polarity; /**< Comparator polarity */
ads1115_comp_latch_t comp_latch; /**< Comparator latch */
ads1115_comp_queue_t comp_queue; /**< Comparator queue */
Copy link
Contributor

Choose a reason for hiding this comment

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

Does the device only have a single input channel / input pin?
Otherwise it could make sense to set those at run-time for the different channels depending on what one wants to measure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No the device can handle four different channel. So maybe I would have to add 4 set functions these four parameters so it can be configure at run-time. What do you think ?

typedef struct {
i2c_t i2c; /**< I2C device */
uint8_t addr; /**< I2C address */
ads1115_mux_t mux; /**< Input multiplexer configuration */
Copy link
Contributor

Choose a reason for hiding this comment

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

You also have ads1115_set_ain_ch_input(), why also the compile-time config?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's just to make sure we have a default configuration with a default channel entry. Should I remove it?

Copy link
Contributor

Choose a reason for hiding this comment

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

A single way to set the configuration is enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but the user may want to get the inputs from differents channels during run-time so the ads1115_set_ain_ch_input() should be necessary, right ?

i2c_acquire(DEV);

// Read conversion register
if (i2c_read_regs(DEV, ADDR, ADS1115_REG_CONVERSION, buf, 2, 0) < 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you need to wait for a converstion to finish?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Honestly I don´t know. Do you think the call to i2c_read_regs is a blocking call or we directly read the conversion register ?

Copy link
Contributor

@benpicco benpicco Jul 18, 2025

Choose a reason for hiding this comment

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

The data sheet should be able to tell you 😉

Devices can implement clock stretching if they are not ready yet, but this is rather rare.

How is the sampling started?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In single-shot mode (when bit[8] MODE = 1), writing a 1 to the OS bit (bit[15]) starts a conversion.
In continuous mode (when bit[8] MODE=0), the device get the input value continuously and store it into the conversion register

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I personally think that reading in the conversion register is not a blocking action and we shouldn't wait

*/
typedef enum {
ADS1115_COMP_MODE_TRADITIONAL = 0, /**< Traditional comparator (default) */
ADS1115_COMP_MODE_WINDOW = 1 /**< Window comparator */
Copy link
Contributor

Choose a reason for hiding this comment

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

That sounds like it would generate an interrupt if the comperator matches, but you don't have anything like that in the code - if you don't need it, just leave it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't actually use it, I just add it to have a fully customisable configuration as explained on pages 25-26 of the datasheet.

Copy link
Contributor

@benpicco benpicco Jul 18, 2025

Choose a reason for hiding this comment

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

But what good is it to configure something that you then can not use?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes you right, it for the scalability and to be sure that the default config is loaded when calling the init function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@benpicco What do you think about that ?

@crasbe crasbe added Type: new feature The issue requests / The PR implemements a new feature for RIOT CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Jul 17, 2025
@riot-ci
Copy link

riot-ci commented Jul 17, 2025

Murdock results

✔️ PASSED

6496914 Merge branch 'RIOT-OS:master' into add-ads1115-driver

Success Failures Total Runtime
10536 0 10536 22m:21s

Artifacts

@crasbe crasbe linked an issue Jul 17, 2025 that may be closed by this pull request
6 tasks
@baptleduc baptleduc force-pushed the add-ads1115-driver branch 3 times, most recently from 2e9f348 to 9d7167f Compare July 18, 2025 12:40
@github-actions github-actions bot added Area: boards Area: Board ports Area: Kconfig Area: Kconfig integration labels Jul 18, 2025
@baptleduc baptleduc force-pushed the add-ads1115-driver branch from 9d7167f to b71cc2f Compare July 18, 2025 13:27
@github-actions github-actions bot removed Area: boards Area: Board ports Area: Kconfig Area: Kconfig integration labels Jul 18, 2025
Copy link
Contributor

@crasbe crasbe left a comment

Choose a reason for hiding this comment

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

Thank you for your addition of the ADS1115 driver. In the review I have addressed some general styling related things and Doxygen related issues.

Also, as in the other PR, please adapt the title and commit message according to our commit convention: https://github.com/RIOT-OS/RIOT/blob/master/CONTRIBUTING.md#commit-conventions

A possible title and commit message could be: "drivers: add support for ADS1115 ADC" or similar.

@baptleduc baptleduc changed the title feat(ads1115): Write an ads1115 driver drivers: add support for ADS1115 ADC Jul 21, 2025
@baptleduc baptleduc requested a review from crasbe July 21, 2025 19:23
@baptleduc baptleduc force-pushed the add-ads1115-driver branch from a4c2fe4 to 915158c Compare July 21, 2025 22:50
@baptleduc
Copy link
Contributor Author

@crasbe Do you know why the Uncrustify formatter doesn't reformat lines longer than 100 characters? It causes the static test to fail, and manually fixing those lines is quite tedious.

@crasbe
Copy link
Contributor

crasbe commented Jul 22, 2025

Unfortunately I am not super familiar with uncrustify and what it can and cannot do :(

@baptleduc baptleduc requested a review from crasbe July 27, 2025 14:41
Copy link
Contributor

@crasbe crasbe left a comment

Choose a reason for hiding this comment

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

You don't have to re-request a review on every change, I get an e-mail notification anyway 😅

ADS1115_PARAMS
};

/** @} */ /* close @ingroup drivers_ads1115 */
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/** @} */ /* close @ingroup drivers_ads1115 */
/** @} */ /* close @ingroup drivers_ads1115 */

ADS1115_PGA_0_512V, /**< FSR = ±0.512V */
ADS1115_PGA_0_256V, /**< FSR = ±0.256V */

/* Aliases (same behavior) */
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/* Aliases (same behavior) */
/* Aliases (same behavior) */

* @brief I2C Analog-to-digital converter with programmable gain amplifier.
* The device has four input channels: AIN0, AIN1, AIN2, and AIN3,
* that can be selected by using @ref ads1115_set_ain_ch_input.
*
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
*
* @{
*

Without the opening bracket, Doxygen seems to ignore the content of this file:
image


#ifdef __cplusplus
}
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
#endif
#endif
/** @} */ /* close @defgroup drivers_ads1115 */

@baptleduc
Copy link
Contributor Author

You don't have to re-request a review on every change, I get an e-mail notification anyway 😅

I'm sorry, I didn't know that it sends you an email on every change, it's my first open-source contribution. I'm going to pay attention for the future

@crasbe
Copy link
Contributor

crasbe commented Jul 29, 2025

I just searched for something else in the RIOT code and discovered that there already is a driver that supports the ADC111x ADCs 🤔 😅
https://github.com/RIOT-OS/RIOT/tree/master/drivers/ads101x
https://github.com/RIOT-OS/RIOT/tree/master/tests/drivers/ads101x

Your driver seems to have the nicer code and more documentation, but having two drivers for the same purpose is a bit redundant.

Perhaps you could take a look at the ads101x driver and give an estimate what the best path forward would be here.

@baptleduc
Copy link
Contributor Author

baptleduc commented Jul 30, 2025

I just searched for something else in the RIOT code and discovered that there already is a driver that supports the ADC111x ADCs 🤔 😅

https://github.com/RIOT-OS/RIOT/tree/master/drivers/ads101x

https://github.com/RIOT-OS/RIOT/tree/master/tests/drivers/ads101x

Your driver seems to have the nicer code and more documentation, but having two drivers for the same purpose is a bit redundant.

Perhaps you could take a look at the ads101x driver and give an estimate what the best path forward would be here.

That's a shame..., in fact the integrated driver supports ads111x despite its lack of documentation. What do you suggest ? Should I abandon this driver or update the existant code to document it better?

@crasbe
Copy link
Contributor

crasbe commented Jul 31, 2025

I'll have to check what the best approach is.

@Enoch247
Copy link
Contributor

That's a shame..., in fact the integrated driver supports ads111x despite its lack of documentation. What do you suggest ? Should I abandon this driver or update the existant code to document it better?

I'm sorry to be the bearer of bad news. Yes we should not have duplicate drivers, without some compelling reason. We would very much appreciate if you would open a PR to port any improvements in code and documentation over to the existing driver from your work here though.

Copy link
Contributor

@Enoch247 Enoch247 left a comment

Choose a reason for hiding this comment

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

This driver duplicates an existing one already in RIOT. See here.

@baptleduc
Copy link
Contributor Author

baptleduc commented Aug 26, 2025

@Enoch247 @crasbe The problem is that the existing driver seems to be made for ADS101x and it not fully support the ADS111x family. For example, the data rate configuration description of the ADS101x is not the same as the ADS111x here. For me, it would make sense to write a dedicated driver for the ADS111x even if it means writing a common file for commons functions (ads_init for example)

@baptleduc baptleduc requested a review from Enoch247 August 27, 2025 07:24
@Enoch247
Copy link
Contributor

It is not uncommon to write drivers to have a shared common part in one module along with device specific parts that are compiled on demand as separate modules or pseudomodules. The ST77xx is one example. Is this the approach you have in mind?

@baptleduc
Copy link
Contributor Author

Yes, exactly! We could use a public header ads1x1x and separate private headers for each variant (ads1114, ads1013, etc.). What do you think?

@Enoch247
Copy link
Contributor

Enoch247 commented Sep 1, 2025

I'm not familiar with the devices, but seems like a reasonable approach.

@crasbe
Copy link
Contributor

crasbe commented Sep 4, 2025

Replaced by #21693 #21694.

@baptleduc
Copy link
Contributor Author

Replaced by #21693.

in fact it is #21694

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: drivers Area: Device drivers CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add support for ADS1115 ADC (I2C)

5 participants