Using internal ADC gain modes of M0 Arduino / SAMD21

Now that I got started with Arduino I'm trying to get a datalogger with a light-sensor up and running.

What is troubling me is to set up the ADC to be any good in measuring a low input voltage-range (ca. 0-120mV)

First thing I've done is raising the resolution from the Arduino native 10-bits to the 12-bit capability of the microcontroller via this command in the loop section: analogReadResolution(12);

With the 3,3V internal Vref and 4096 steps of differentiation it should come down to ~0,8mV per step? That would mean getting a resolution of 150 steps for the input range of 0-120mV - which is rather low.

At least that is what I expected, hoping to be able to benefit from the integrated gain modes of the ADC.

So far I didn't get anywhere with that. I see some similar topics here with either suggestions about op-amps or other well-versed talks about AVR/GCC stuff.

I'd llike to give the gain mode a chance and was hoping that someone can help me with setting the right registers?

Strangely I can't find the files and packages associated with the SAMD21 as mentioned in this topic..

Does someone has used the internal gain modes already?

Ah, forgot to mention that the datasheet at Atmel contains a table (p.866) regarding delay when using gain mode, mentioning the following, which is probably the register I want to address?
INTPUTCTRL.GAIN[3:0] with the hex code 0x4 for 16x gain.

Perhaps someone can give me an example how to make use of this when setting up the ADC via Arduino IDE?

Any help is much appreciated!

Best wishes

Max

The best and simplest way to increase input sensitivity & accuracy is to generate a stable and precise EXTERNAL REFERENCE VOLTAGE and apply it to the AREF pin. The applicable enumeration is:

ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val; // Gain Factor Selection
ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_AREFA_Val;

For example, applying a Vref = 1000 mV @ 12 Bit , you get a resolution of 244 uV.

A comprehensive tutorial in 5 parts on Arduino Zero programming, including sketches and guidance on Reference Voltage manipulation is freely available here: this is part 1 and the other parts are available on the same site.

https://www.picotech.com/support/topic24051.html

Cheers,

Glovisol

Glovisol, thanks alot for your valuable input! I will go through that tutorial and also try to use the code.

Today I made initial measurements with photodiodes which give output from 0-150mV, at the higher end the measurement seems fine, but already at 100mV the measured value is getting lower than what I measure with my multimeter, and at below 10mV it is really far off!

I was thinking about a lower reference voltage as well.
But then I had trouble finding a good solution for Aref. BTW, is there a limitation of the chip that doesn't allow Aref to be lower than 1V or so?

My next thought was the internal reference of 1V, which I was reading about in several posts - but then again got confused after reading a comment which said: "You can use the 1.1V reference to change what the ADC measures the incoming voltage against. You can't use the 1.1V reference to do the actual measuring though." which was posted by a user on stackexchange

According to this the 1V reference of the SAMD21 within the chip is only of use internally, but not for adc input?
I assume that is why you are suggesting the external Aref straight away. Do you have an opinion on whether a good Aref or an op-amp is better to use and handle?

I will report my further progress

Cheers

Max

In the links I have cited you can find the exact procedure to set up the EXTERNAL REFERENCE. You cannot use the internal 1.1 V reference, as it is reserved for processor use only, but you can use the internal 1V reference.

Please consider that the internal reference voltages are limited in precision & stability, while you wish to do precision measurements. It is then advisable to use an external reference derived from one of several Precison Reference Generator ICs available in the market. You can also find an examaple of a precision reference circuit in the cited references.

glovisol

Huh,

meanwhile I was trying different parameters with the ADC to observe it's behaviour.

Yet someone else told me that it is possible to use the internal 1V reference. But I'm even more confused if it really does. Via "ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INT1V_Val" I do get raised readings from the sensors. How does this work?

Another thing which seems strange to me is the gain mode. While the signal is high, readings are properly multiplied by the gain factor. However, when signals are as low as 2,5mV, the result seems rather inverted - i.e. instead of 4/4096 I get almost only 0/4096 with most readings - so no signal at all? During this my multimeter still shows 2,5mV readings from the sensor. Without gain mode the readings vary between 2 and 8, which even when averaging doesn't represent the actual signal. Is this normal and am I hitting the normal constraints of the internal ADC here?

I'm just trying to understand the limitations of my setup here :wink:

Please post the code you are using, otherwise it is next to impossible to advise you. I confirm I tried myself to use the 1.1 V internal reference, but did not succeed and the SAMD21 spec sheet does not give any provision for its use externally.

glovisol

Shure, here's the sketch being used (a modified version of a logger-example by Jonathan Davies at github), The part of interest is the ADC enumerations at the very end of setup {}, with the Vref ones being temporarily disabled in this case.

/*
  Simple Logger using internal RTC for Arduino Zero

 Created by:  Jonathan DAvies
 Date:        21 Oct 2015
 Version:     0.1
*/


#include <SPI.h>
#include <SD.h>
#include <RTCZero.h>


#define cardSelect 4  // Set the pins used
//#define VBATPIN A7    // Battery Voltage on Pin A7
#define SENSOR1 A1    // Sensor on Pin A1
#define SENSOR2 A2    // Sensor on Pin A2
#define SENSOR3 A3    // Sensor on Pin A3

File logfile;   // Create file object

RTCZero rtc;    // Create RTC object - strangely setting time doesn't work. Output is always starting at 12:00??
/* Change these values to set the current initial time */
const byte hours = 07;
const byte minutes = 30;
const byte seconds = 0;
/* Change these values to set the current initial date */
const byte day = 04;
const byte month = 07;
const byte year = 16;


//////////////    Setup   ///////////////////
void setup() {

  rtc.begin();    // Start the RTC
  
  // while (! Serial); // Wait until Serial is ready
  // Serial.begin(115200);
  // Serial.println("\r\nAnalog logger test");
  
  pinMode(13, OUTPUT);


  // see if the card is present and can be initialized:
  if (!SD.begin(cardSelect)) {
    Serial.println("Card init. failed!");
    error(2);
  }
  char filename[15];
  strcpy(filename, "ANALOG00.CSV");
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = '0' + i/10;
    filename[7] = '0' + i%10;
    // create if does not exist, do not open existing, write, sync after write
    if (! SD.exists(filename)) {
      break;
    }
  }

  logfile = SD.open(filename, FILE_WRITE);
  if( ! logfile ) {
    Serial.print("Couldnt create "); 
    Serial.println(filename);
    error(3);
  }
  Serial.print("Writing to "); 
  Serial.println(filename);

  pinMode(13, OUTPUT);
  pinMode(8, OUTPUT);
  Serial.println("Ready!");

  ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val; // Gain Factor Selection, 1x,2x,4x,8x,16x
  //ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0_Val; // 1/1.48 VDDANA = 1/1.48* 3V3 = 2.2297 
  //ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INT1V_Val;  // 1.0V voltage reference
  analogReadResolution(12); // change the resolution to 12 bits
}

uint8_t i=0;


///////////////   Loop    //////////////////
void loop() {
  digitalWrite(8, HIGH);

  //int measuredvbat = analogRead(VBATPIN);
  /* measuredvbat *= 2;    // we divided by 2, so multiply back
  measuredvbat *= 3.3;  // Multiply by 3.3V, our reference voltage
  measuredvbat /= 1024; // convert to voltage
*/

  int measure1 = analogRead(SENSOR1);
  int measure2 = analogRead(SENSOR2);
  int measure3 = analogRead(SENSOR3);
      
// Print RTC Time
  Serial.print(rtc.getHours());
  Serial.print(":");
  Serial.print(rtc.getMinutes());
  Serial.print(":");
  Serial.print(rtc.getSeconds());
  Serial.println(",");
  //Serial.print("Battery:");
  //Serial.println(measuredvbat);   // Print battery voltage
  Serial.print("Sensor1:");
  Serial.println(measure1);   // Print Sensor1 measurement
  Serial.print("Sensor2:");
  Serial.println(measure2);   // Print Sensor2 measurement
  Serial.print("Sensor3:");
  Serial.println(measure3);   // Print Sensor3 measurement
  
  
  // Print Data to SD card
  logfile.print(rtc.getHours());
  logfile.print(":");
  logfile.print(rtc.getMinutes());
  logfile.print(":");
  logfile.print(rtc.getSeconds());
  //logfile.print(", ");
  //logfile.println(measuredvbat);   // Print battery voltage
  logfile.print(", ");
  logfile.println(measure1);   // Print Sensor1 measurement
  logfile.print(", ");
  logfile.println(measure2);   // Print Sensor2 measurement
  logfile.print(", ");
  logfile.println(measure3);   // Print Sensor3 measurement
  logfile.flush();
  
  digitalWrite(8, LOW);
  
  delay(5000);
}

///////////////   Functions   //////////////////
// blink out an error code
void error(uint8_t errno) {
  while(1) {
    uint8_t i;
    for (i=0; i<errno; i++) {
      digitalWrite(13, HIGH);
      delay(100);
      digitalWrite(13, LOW);
      delay(100);
    }
    for (i=errno; i<10; i++) {
      delay(200);
    }
  }
}

I'd like to correctly understand how the gain and Vref modes alter the analog-read-value.

Especially at the low end: Is it normal that at a steady 2.5mV input, readings via SAMD21 differ in a range from 2-8 (out of 4096). How stable/reliable can a reading be expected to be?

I just tried another time with the following results, all being based at an almost steady 2.5mV input signal:
Readings at:
gain 1x, VRef 3.3V: vary between 2-7 (1.61-5.64mV)
gain 8x, VRef 3.3V: vary between 0-29 (0-2.92mV)
gain 1x, VRef internal 1V: between 0-7 with mostly 0 (0-1.71mV)
gain 1x, VRef internal 2.23V: between 7-12 (3,81-6.53mV)

-It seems as gain is doing fine for higher readings (>100mV), but on the lower end the insignificance of readings is amplified as well.

-On the other hand, altering the internal VRef behaves similar: proportionally raising higher inputs while the lower ones seem rather strangely affected.

This seems to be the bottleneck to my case, as I was hoping both, another internal Vref and gain mode would help me to get reliable readings.

BTW, open inputs at the ADC are always measured around half the 3.3VRef, how is that?

In my opinion your difficulties stem from the fact that the ZERO reads the sensor with a 12 Bit resolution, but then writes the values out at 8 bit resolution. Please consider the following:

  1. Arduino ZERO has an input resolution command that works, so this is O.K.

  2. Arduino ZERO has an outpur resolution command THAT unfortunately DOES NOT WORK. This command simply re-maps the output config., but the resolution always stays @ 8Bit. This fact has been demonstrated by MartinL here:

http://forum.arduino.cc/index.php?topic=346731.75

  1. Because of not adequate Arduino literature it is very easy to confuse the input resolution issue with the output resolution issue.

All the above is covered (with exhaustive examples and sketches) in the tutorial in several parts I have uploaded in the Picotech site. My suggestion is that you carefully go over it, because there you will see how it is possible to change the output resolution of Arduino ZERO to suit your application. In fact you can download several examples there, including I2C operation suitable for RTC's.

See enclosed example.
glovisol

sketch_voltage_translator.ino (10.9 KB)

1 Like

Glovisol, MaxMux is asking about analog input not analog output !

Thank you Glovisol, your input was keeping my motivation up to get something done.

I got through your tutorials at picotech, which became a good learning resource to me.

Finally I downloaded your 'LOW FREQUENCY PRECISION VOLTAGE TRANSLATOR SKETCH' and tried it with a blank Arduino Zero (M0) board first.

The serial output showed that all empty input channels have quite some variation, not only between channels but also fluctuating between individual measurements. Is that normal?

Afterwards I raised the PERX to 4095 to get 12 Bit-resolution, and then another time I added a photodiode at ~2.5mV to the A0 input channel. Sadly I don't see any change in accuracy in terms of proximity to the true value. The calculated output of your sketch fluctuates between measurements from 5-11mV while my multimeter stays around 2.5mV.

See this serial output as an excerpt:

....................
ARDUINO ZERO - HIGH RESOLUTION/PRECISION PICOLOG BUFFER
RESOLUTION BIT = 10
RESOLUTION mV = 2.18
---------------------------------
INPUT / OUTPUT CHANNELS
--------------------------------
Channel/0, Bits = 432 and mV = 1393.00
Channel/1, Bits = 574 and mV = 1851.00
Channel/2, Bits = 390 and mV = 1258.00
Channel/3, Bits = 630 and mV = 2032.00
Channel/4, Bits = 199 and mV = 641.00
Channel/5, Bits = 421 and mV = 1358.00
....................


--> With Resolution Parameter PERX set to 4095:
....................
ARDUINO ZERO - HIGH RESOLUTION/PRECISION PICOLOG BUFFER
RESOLUTION BIT = 12
RESOLUTION mV = 0.54
---------------------------------
INPUT / OUTPUT CHANNELS
--------------------------------
Channel/0, Bits = 1344 and mV = 1083.00
Channel/1, Bits = 1706 and mV = 1374.00
Channel/2, Bits = 2485 and mV = 2002.00
Channel/3, Bits = 1014 and mV = 817.00
Channel/4, Bits = 2629 and mV = 2118.00
Channel/5, Bits = 2361 and mV = 1902.00
....................


--> with a photodiode at ~2.5mV (multimeter measurement) connected to A0
....................
ARDUINO ZERO - HIGH RESOLUTION/PRECISION PICOLOG BUFFER
RESOLUTION BIT = 12
RESOLUTION mV = 0.54
---------------------------------
INPUT / OUTPUT CHANNELS
--------------------------------
Channel/0, Bits = 11 and mV = 8.00
Channel/1, Bits = 2021 and mV = 1628.00
Channel/2, Bits = 1576 and mV = 1270.00
Channel/3, Bits = 1090 and mV = 878.00
Channel/4, Bits = 2303 and mV = 1855.00
Channel/5, Bits = 1071 and mV = 863.00
....................
ARDUINO ZERO - HIGH RESOLUTION/PRECISION PICOLOG BUFFER
RESOLUTION BIT = 12
RESOLUTION mV = 0.54
---------------------------------

Any further hint on that - or shall I give up on trying to measure voltages that low with a setup solely relying on the SAMD21? I mean at a calculated resolution of 0.54mV and with my prior results of using various gain and VRef modes, I hardly have any expectations regarding adjusting those as well.

Hi MadMux,

First of all please confirm you are using the board Arduino ZERO by Arduino c.c. The Arduino M0 you mention is not by Arduino c.c, but by Arduino Org. and the booloader and IDE are DIFFERENT and NOT COMPATIBLE:

Assuming you are using the board Arduino ZERO, I think is is normal for the Micro to have some fluctuation at very low voltage input. After all you wish to measure precisely bit #1. Consider that even expensive voltmeters have a tolerance for accuracy and over an above that the spec +/- 1 digit is quite common. This means that your measurement will have at least one bit uncertainty.plus tolerance in accuracy.

A foolproof solution would be to use a buffer Operational Amplifier with moderate gain, plus/minus supplies and offset trimming. For example with a gain of 40 in the Buffer Amp, the min. input to the SAMD 21 would be 100 mV, and the problem would be solved, because you would still have a fluctuatìion of 2.4 mV, but over 100 mV. The additional "fluctuation" you see is because the SAMD 21 inputs must be terminated by a low impedance, the lower, the better. Using an Op Amp buffer the Micro inputs would see the zero (nominal) output impedance of the buffer and would then behave better.

If you look here:

https://www.picotech.com/support/topic14829.html

you will find an example of an Op Amp buffer for a Picotech Data Logger. Please understand this is just an example, you would probably need something better than an LM 324 quad op amp.

Cheers,

glovisol

Okay, i get your points.

First of all I'm not using the Arduino zero precisely but a Feather M0 Datalogger by Adafruit. Since it has the same microcontroller I considered it compatible - but you are right that it may use a different bootloader. However regarding the IDE I don't have any other version and also the native Arduino Cortex M0 board-support installed.

Regarding the measurements:
What also came to my mind is that any multimeter as well does averaging which leads to more stable readings.
So perhaps it is also of use to do averaging with several individual readings to gain a more representative value?

Right now I also have an MCP3424 18bit ADC with programmable gain at hand. This is very promising to me and I will try some measurements with this connected via I2C tomorrow. An op-amp as you suggested could also be a solution, but frankly speaking I assume the external ADC with 4ch to be easier to integrate into my circuit than 4 individual op-amps. Or do you have other thoughts to be considered?

Besides that I am drifting away from the solution of using the SAMD21 chip. At first it was promising for it's internal RTC, 12-bit ADC with gain, in my case a micro-sd storage-solution and first and foremost - a way better energy-efficiency than the ATmega microcontrollers. The latter is imperative to me as the intended datalogger should work battery-powered on the long run. But now most of these points don't pay off, the RTC may be useless without a backup-battery and perhaps is not that precise anyway, and the controller (or board) itself seems tricky to connect to (USB) with lot of trial and error to finally upload sketches.

By any chance, do you know another arduino-compatible µC with low energy-footprint? I came from an ATtiny with Arduino bootloader first, but this is probably too limiting in terms of its own (and also my technical) capabilities.

Cheers to you and thanks again for sharing your experience Glovisol!

Max

Glad to have been of use to your project. Some final thoughts....

  1. I am uncertain about the number of measurement channels you need: e.g. if you need to measure just one light sensor's output or four. (four op amps are used in my example because I needed to buffer as many channels of the Pico Data Logger).

  2. If you need just one channel, then one good quality Op Amp would suffice. Micropower Op Amps are available.

  3. You could of course generate the negative supply from the positive power source and end up with a total power consumption of perhaps 300/500 uA @ 5V. Perhaps this is too much for your battery powered app.

  4. Averaging the measurements is a good idea, leading to good results. It does not consume power, but needs more software.

  5. High precision RTC's are available with the xtal and proportional thermostat packed inside the chip and communicating via I2C.

glovisol

Hi MaxMux,

I just completed a translator channel + i2C/DS3231 RTC sketch for Arduino ZERO (enclosed). I do not have time to test it, but it compiles without any problem and should give you a start if you decide to go the SAMD21 way. This RTC is very accurate and kept the second for over one week during tests. Reference is 2.5V, but that is easy to change.

bye,

glovisol

sketch_ZERO_translator___RTC.ino (11.5 KB)

Hi glovisol,

Good to hear that you're using the DS3231 as well. That was part of my initial setup with ATtiny84 together with MCP3424 ADC. I just used it today for measurements. The MCP3424 really operates at another level of precision than the internal ADC. At 8x gain and 16bit-resolution I can flawlessly differentiate low millivolt inputs (theoretically at 7.8 microvolt steps).
Regarding the RTC, I guess it was half a year ago that I was setting it up and it still accurately keeps time! Thanks for sharing your code, perhaps it'll be of use when I add the RTC to the SAMD21 board.

As of the op-amp vs. external ADC question: For one channel I also do believe the op-amp is an easy way to go. However, my point is to read as many sensors with one Arduino as possible. Or max. 10 input-channels to be precise in therms of the SAMD21 board that I have. But now that I have to face several trade-offs with the internal compoments, it will probably be two times MCP3424 along with one Arduino. Plus the external RTC for convenience of battery-backup and precision.

BTW, the external ADC is set to operate at differential mode. AFAIK the SAMD21 ADC supports this as well. Did you try this before or do have some knowledge on how to use it? Perhaps this way the internal ADC is more precise as well (leaving VRef behind)?

All the best, including a nice weekend!

Max

ARDUINO ZERO - SAMD21 OPERATION AT VERY LOW INPUT & AT VERY HIGH RESOLUTION

Hi MaxMux,

Your problem intrigued me and I investigated the reason for the input channel reading fluctuations. To do this I first developed a modified sketch with the following parameters:

RESOLUTION DIGITAL: 16 BIT (65,535)
INTERNAL REFERENCE VOLTAGE: 1 VOLT
RESOLUTION ANALOGUE: 1000000/65365 = 15uV
PWM OUTPUT FREQUENCY: 366 Hz

At this level of resolution the Arduino Zero exibits an amazing performance, with excellent precision and stability down to 10 mV input. Then I discovered that for zero input there was still output and realised this is the sum of two problems:

#1: it is very easy to have stray voltages on the inputs at zero level. True zero level can only be achieved by shorting the inputs to a very short and true ground of the SAMD21. If the ground connection is long (remember we have 1 Bit = 15 uV!) or not very low impedance, you end up with stray floating voltages that alter the measurement.

#2: at this level of sensitivity it is also easy to pick up stray noise and/or hum, which of course would not allow the micro to see 0V or microvolt voltages at the input.

I am enclosing PC pictures showing fixed levels on all input channels used and variable level on "channel 0". Here the last picture shows true 0V input with the input accurately shorted to ground.

To conclude: I think you have given yourself a very hard job to tackle: you need vey good shielding of your circuitry as well as very good wiring/grounding techniques, no matter what devices you are using.

I enclose the special Arduino Zero sketch as well as PC screens.

Cheers,

glovisol

sketch_ZERO_TRANSLATOR_8.ino (10.1 KB)

Glovisol, you're talking about 16bit resolution, but AFAIK the maximum resolution of the SAMD21 ADC is 12bit.
I agree on one fact : precision analog electronic is tricky : the circuit must be very well designed to avoir any noise. 5mV is a pretty low voltage and having noise with this amplitude is almost normal with a standard design. you could maybe explore the differential channel possibility of the SAMD21 ADC to avoid picking noise with long wire. But you still have to ensure that the VDDANA is very clean, separate analog and digital ground and so on.

Hi Alois,

Excuse my ignorance with acronisms, of wich I am not an enthousiast user. What is AFAIK??

Seeing is beliving, look at my attachments. The PC screens and my measurements confirm that Arduino Zero with the uploaded sketch indeed works at in/out resolution of 16 Bit: this is working in my lab now now. You can just try, if you wish. Incidentally I am using your proposed enumerations for INTERNAL/EXTERNAL reference.

This work was done to explore the accuracy & precision limits of the SAMD 21, was stimulated by the uploads of MaxMux. and could serve as a reference.

Apart from this, we both agree that electrical design and layout are most important, perhaps more important than software sometimes, to make a success out of a project. In a practical design I would of course use a separate high accuracy PSU to feed the Arduino board and to produce the 1V accurate voltage reference. By the way do you know which is the lowest external reference voltage you can use with Arduino Zero?

In my opinion the best solution to meter very low D.C. voltages would be to use a precision, zero offset Op Amp bringing the input up by a factor of 10 or more. Then the layout and shielding would be limited to this unit and the Arduino Zero circuitry would be far less critical, because working at a higher voltage.

Thanks for your continued valuable comments & assistance.

Cheers,

glovisol

glovisol:
Excuse my ignorance with acronisms, of wich I am not an enthousiast user. What is AFAIK??

"As Far As I Know"

glovisol:
Seeing is beliving, look at my attachments. The PC screens and my measurements confirm that Arduino Zero with the uploaded sketch indeed works at in/out resolution of 16 Bit: this is working in my lab now now.

Datasheet, page 2 :

-One 12-bit, 350ksps Analog-to-Digital Converter (ADC) with up to 20 channels
•   Differential and single-ended input
•   1/2x to 16x programmable gain stage
•   Automatic offset and gain error compensation
•   Oversampling and decimation in hardware to support 13-, 14-, 15- or 16-bit resolution
–  10-bit, 350ksps Digital-to-Analog Converter (DAC)

Unless you use oversampling, the ADC has 12bit resolution. And your program doesn't seems to use oversamplig. By the way, your program seems to be a bit very complex to answer the initial question. Maybe you could explain it a bit better?

glovisol:
By the way do you know which is the lowest external reference voltage you can use with Arduino Zero?

Datasheet, table 39-2 :

AREFx:
1.0V to VDDANA - 0.6V for ADC 
1.0V to VDDANA - 0.6V for DAC

glovisol:
In my opinion the best solution to meter very low D.C. voltages would be to use a precision, zero offset Op Amp bringing the input up by a factor of 10 or more. Then the layout and shielding would be limited to this unit and the Arduino Zero circuitry would be far less critical, because working at a higher voltage.

Well, I think it's not that simple : a very low noise op amp circuit can also be hard to implement. One good way would be to use a differential instrumentation op amp or an external low-noise, high resolution ADC. The software part could also help to obtain abetter measure, by applying good filtering and averaging.
Finally, maybe the easier method would be to use an other type of sensor, that provide a more suitable output for the application. I would personally choose an integrated circuit, like the TSL2591 : ~2$ alone, ~7$ with breakout board.

Have you tried this program ?