├── .github └── workflows │ └── arduino-lint.yaml ├── .gitignore ├── LICENSE ├── PicoAnalogCorrection.cpp ├── PicoAnalogCorrection.h ├── README.md ├── examples └── SimpleCalibration │ └── SimpleCalibration.ino ├── keywords.txt └── library.properties /.github/workflows/arduino-lint.yaml: -------------------------------------------------------------------------------- 1 | # Execute Arduino Lint automatically upon push or pull request 2 | on: [push, pull_request] 3 | jobs: 4 | lint: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v3 8 | - uses: arduino/arduino-lint-action@v1.0.2 9 | with: 10 | project-type: library 11 | library-manager: update 12 | compliance: specification #strict 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 NuclearPhoenix 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PicoAnalogCorrection.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Pico Analog Correction 3 | https://github.com/NuclearPhoenixx/Arduino-Pico-Analog-Correction/ 4 | */ 5 | 6 | #include "PicoAnalogCorrection.h" 7 | 8 | 9 | PicoAnalogCorrection::PicoAnalogCorrection(size_t adc_res, float vref) { 10 | _adc_init = false; 11 | _adc_res = adc_res; 12 | _max_channel = pow(2, adc_res) - 1; 13 | _gnd_offset = 0; 14 | _vcc_offset = 0; 15 | _vref = vref; 16 | setCorrectionValues(); 17 | } 18 | 19 | 20 | PicoAnalogCorrection::PicoAnalogCorrection(size_t adc_res, size_t gnd_val, size_t vcc_val) { 21 | _adc_init = false; 22 | _adc_res = adc_res; 23 | _max_channel = pow(2, adc_res) - 1; 24 | _gnd_offset = gnd_val; 25 | _vcc_offset = vcc_val; 26 | _vref = 3.3; 27 | setCorrectionValues(); 28 | } 29 | 30 | 31 | PicoAnalogCorrection::PicoAnalogCorrection(size_t adc_res, float vref, size_t gnd_val, size_t vcc_val) { 32 | _adc_init = false; 33 | _adc_res = adc_res; 34 | _max_channel = pow(2, adc_res) - 1; 35 | _vref = vref; 36 | _gnd_offset = gnd_val; 37 | _vcc_offset = vcc_val; 38 | setCorrectionValues(); 39 | } 40 | 41 | 42 | void PicoAnalogCorrection::setCorrectionValues() { 43 | if(_vcc_offset == 0) { 44 | _a = 1.0; 45 | } else { 46 | _a = _max_channel / (_vcc_offset - _gnd_offset); 47 | } 48 | _d = - _a * _gnd_offset; 49 | return; 50 | } 51 | 52 | 53 | void PicoAnalogCorrection::calibrateAdc(size_t gnd_pin, size_t vcc_pin, size_t avg_size) { 54 | float gnd_value = .0; 55 | 56 | for(size_t i = 0; i < avg_size; i++) { 57 | gnd_value += float(analogRead(gnd_pin)); 58 | } 59 | _gnd_offset = gnd_value/avg_size; 60 | 61 | float vcc_value = .0; 62 | 63 | for(size_t i = 0; i < avg_size; i++) { 64 | vcc_value += float(analogRead(vcc_pin)); 65 | } 66 | _vcc_offset = vcc_value/avg_size; 67 | 68 | setCorrectionValues(); 69 | return; 70 | } 71 | 72 | 73 | void PicoAnalogCorrection::returnCalibrationValues() { 74 | Serial.println("(" + String(_gnd_offset) + ", " + String(_vcc_offset) + ")"); 75 | return; 76 | } 77 | 78 | 79 | void PicoAnalogCorrection::analogReadResolution(size_t adc_res) { 80 | _adc_res = adc_res; 81 | ::analogReadResolution(adc_res); 82 | } 83 | 84 | 85 | int PicoAnalogCorrection::analogRead(size_t pin) { 86 | digitalWrite(PS_PIN, HIGH); // Disable power-saving 87 | //delayMicroseconds(2); // Cooldown for the converter to stabilize? 88 | 89 | int value = ::analogRead(pin); // Use normal Arduino analogRead func 90 | 91 | digitalWrite(PS_PIN, LOW); // Re-enable power-saving 92 | 93 | return value; 94 | } 95 | 96 | 97 | int PicoAnalogCorrection::analogCRead(size_t pin, size_t avg_size) { 98 | float value = .0; 99 | 100 | for(size_t i = 0; i < avg_size; i++) { 101 | //value += float( map(analogRead(pin), _gnd_offset, _vcc_offset, 0, _max_channel) ); 102 | value += float( _a * analogRead(pin) + _d ); 103 | } 104 | 105 | return round(value/avg_size); 106 | } 107 | 108 | 109 | float PicoAnalogCorrection::analogReadTemp(pactemp_t type) { 110 | if (!_adc_init) { 111 | adc_init(); 112 | _adc_init = true; 113 | } 114 | adc_set_temp_sensor_enabled(true); 115 | delay(1); // Allow things to settle. Without this, readings can be erratic 116 | adc_select_input(4); // Temperature sensor is analog pin 4 117 | int v = adc_read(); 118 | adc_set_temp_sensor_enabled(false); 119 | float t = 27.0f - ((v * _vref / pow(2, _adc_res)) - 0.706f) / 0.001721f; // From the datasheet with custom values for ADC res and Vref voltage 120 | 121 | if (type == PAC_F) { 122 | return t * 1.8f + 32.0f; 123 | } else { 124 | return t; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /PicoAnalogCorrection.h: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Pico Analog Correction 3 | https://github.com/NuclearPhoenixx/Arduino-Pico-Analog-Correction/ 4 | */ 5 | 6 | #ifndef PicoAnalogCorrection_H 7 | #define PicoAnalogCorrection_H 8 | 9 | #include 10 | #include 11 | 12 | 13 | #define PS_PIN 23 // Power Save Pin, H to disable, L default 14 | 15 | typedef enum { 16 | PAC_C, // Celsius 17 | PAC_F, // Fahrenheit 18 | } pactemp_t; 19 | 20 | 21 | class PicoAnalogCorrection { 22 | private: 23 | size_t _max_channel, _gnd_offset, _vcc_offset, _adc_res; 24 | float _a, _d, _vref; 25 | bool _adc_init; 26 | 27 | void setCorrectionValues(); 28 | 29 | public: 30 | PicoAnalogCorrection(size_t adc_res=12, float vref=3.3); 31 | PicoAnalogCorrection(size_t adc_res, size_t gnd_val, size_t vcc_val); // Backwards Compatability 32 | PicoAnalogCorrection(size_t adc_res, float vref, size_t gnd_val, size_t vcc_val); 33 | 34 | void calibrateAdc(size_t gnd_pin, size_t vcc_pin, size_t avg_size=100); 35 | void returnCalibrationValues(); 36 | 37 | void analogReadResolution(size_t adc_res); 38 | 39 | int analogRead(size_t pin); 40 | int analogCRead(size_t pin, size_t avg_size=1); 41 | float analogReadTemp(pactemp_t type=PAC_C); 42 | }; 43 | #endif 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino-Pico-Analog-Correction 2 | 3 | ![arduino-library-badge](https://www.ardu-badge.com/badge/PicoAnalogCorrection.svg?) ![latest version](https://img.shields.io/github/release/NuclearPhoenixx/Arduino-Pico-Analog-Correction.svg?) ![issues](https://img.shields.io/github/issues/NuclearPhoenixx/Arduino-Pico-Analog-Correction.svg?) ![open pr](https://img.shields.io/github/issues-pr-raw/NuclearPhoenixx/Arduino-Pico-Analog-Correction.svg?) 4 | 5 | Arduino library to calibrate and improve ADC measurements with the Raspberry Pi Pico. Can compensate ADC offsets, calculate the arithmetic mean of any number of measurements and temporarily disable the power-saving mode when analog-reading to improve the power supply ripple and noise. 6 | There is also a function for reading the temperature sensor depending on your ADC resolution and reference voltage. 7 | 8 | This library is also available in the Arduino IDE, see [Arduino Library List](https://www.arduinolibraries.info/libraries/pico-analog-correction). 9 | 10 | ## Using 11 | 12 | You can use the [example calibration sketch](examples/SimpleCalibration) to grab the calibration values, just follow the instructions given in the sketch. 13 | 14 | You can serial print the calibration values anytime using this command: 15 | 16 | ```cpp 17 | void returnCalibrationValues(); 18 | ``` 19 | 20 | Other than that, you can take non-calibrated and calibrated measurements using the corresponding functions: 21 | 22 | ```cpp 23 | int analogRead(size_t pin); 24 | int analogCRead(size_t pin, size_t avg_size=1); 25 | ``` 26 | 27 | Both of these temporarily disable the power-saving mode to improve noise. Only the second function allows taking an arbitrary number of measurements and returning the arithmetic mean while also using the linear calibration. 28 | 29 | You can also read the correct temperature from the temp sensor, even if you're not using the default resolution and reference voltage. Be sure that you supply the constructor with the correct values, then simply call: 30 | 31 | ```cpp 32 | float analogReadTempCorrect(); 33 | ``` 34 | 35 | This defaults to Celsius, you can also print Fahrenheit: 36 | 37 | ```cpp 38 | float analogReadTempCorrect(PAC_F); 39 | ``` 40 | 41 | ## Limitations 42 | 43 | This library is limited to a very simple linear calibration since there are practically only two trusted voltages available to be measured: GND (0V) and VCC (3.3V). This means that the calibration will ensure that the ADC measures GND as 0 and VCC as the maximum channel number (depending on your resolution, e.g. 4095) with any value in between being distributed linearly within these two. 44 | 45 | In addition, as the very helpful [Raspberry Pi Pico datasheet](https://datasheets.raspberrypi.com/pico/pico-datasheet.pdf) suggests, the on-board power supply noise and accuracy are not the best. To get the best results possible, you need to use an external voltage reference. This, however, introduces additonal drawbacks. 46 | 47 | (Temporarily) Disabling the power-saving mode of the regulator is therefore a must-have to significantly reduce ripple on the ADC supply and is exactly what this library does independently from the calibration. 48 | 49 | More info about the Pico's ADC can be found [on this website](https://pico-adc.markomo.me/) by [@ferret-guy](https://github.com/ferret-guy), who did extensive testing to find out the exact specifications. 50 | -------------------------------------------------------------------------------- /examples/SimpleCalibration/SimpleCalibration.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Analog Correction For Raspberry Pi Pico 3 | * https://github.com/NuclearPhoenixx/Arduino-Pico-Analog-Correction 4 | * 5 | * Connect A1 to GND and A0 to VCC (3.3V), then 6 | * upload this sketch and restart the Pico. 7 | * The connections should be as short as possible! 8 | * Your calibration values will be printed to the 9 | * Serial Monitor. Paste them into the 10 | * PicoAnalogCorrection constructor and you're done. 11 | * 12 | * You can also define a custom ADC resolution. 13 | * You can also read the correct temp from the sensor using the Vref value. 14 | * 15 | * MIT, 2022, NuclearPhoenix 16 | */ 17 | 18 | #include 19 | 20 | const uint8_t GND_PIN = A1; // GND meas pin 21 | const uint8_t VCC_PIN = A0; // VCC meas pin 22 | const uint8_t ADC_RES = 12; // ADC bits 23 | const float VREF = 3.0; // Analog reference voltage 24 | 25 | PicoAnalogCorrection pico(ADC_RES, VREF); 26 | 27 | void setup() { 28 | pinMode(GND_PIN, INPUT); 29 | pinMode(VCC_PIN, INPUT); 30 | 31 | analogReadResolution(ADC_RES); 32 | 33 | // Needs to be called only if ADC_RES changes after the initial declaration of PicoAnalogCorrection. 34 | // You can also use this method instead of the stock analogReadResolution() version if you want to change the 35 | // resolution on the go. This will automatically call analogReadResolution(ADC_RES) too. 36 | // pico.analogReadResolution(ADC_RES); 37 | 38 | // Calibrate ADC using an average of 5000 measurements 39 | pico.calibrateAdc(GND_PIN, VCC_PIN, 5000); 40 | 41 | Serial.begin(); 42 | 43 | while (!Serial) { 44 | ; // Wait for Serial 45 | } 46 | } 47 | 48 | void setup1() { 49 | } 50 | 51 | void loop() { 52 | Serial.print("Offset Values: "); 53 | pico.returnCalibrationValues(); 54 | 55 | Serial.print("Uncalibrated GND: "); 56 | Serial.println(pico.analogRead(GND_PIN)); 57 | Serial.print("Calibrated GND: "); 58 | Serial.println(pico.analogCRead(GND_PIN, 10)); 59 | 60 | Serial.print("Uncalibrated VCC: "); 61 | Serial.println(pico.analogRead(VCC_PIN)); 62 | Serial.print("Calibrated VCC: "); 63 | Serial.println(pico.analogCRead(VCC_PIN, 10)); 64 | Serial.println(); 65 | 66 | Serial.print("Temperature (C): "); 67 | Serial.println(pico.analogReadTemp()); 68 | Serial.print("Temperature (F): "); 69 | Serial.println(pico.analogReadTemp(PAC_F)); 70 | Serial.println(); 71 | 72 | delay(1000); 73 | } 74 | 75 | void loop1() { 76 | } 77 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ##################################### 2 | # Syntax Coloring Map For MQ135 3 | ##################################### 4 | 5 | ##################################### 6 | # Datatypes (KEYWORD1) 7 | ##################################### 8 | 9 | PicoAnalogCorrection KEYWORD1 10 | 11 | ##################################### 12 | # Methods and Functions (KEYWORD2) 13 | ##################################### 14 | 15 | calibrateAdc KEYWORD2 16 | returnCalibrationValues KEYWORD2 17 | analogRead KEYWORD2 18 | analogCRead KEYWORD2 19 | analogReadTemp KEYWORD2 20 | analogReadResolution KEYWORD2 21 | 22 | ##################################### 23 | # Constants (LITERAL1) 24 | ##################################### 25 | 26 | pactemp_t LITERAL1 27 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=PicoAnalogCorrection 2 | version=1.4.0 3 | author=NuclearPhoenix 4 | maintainer=NuclearPhoenix 5 | sentence=Arduino library to calibrate and improve ADC measurements with the Raspberry Pi Pico including the built-in temp sensor. 6 | paragraph=Allows simple linear calibration of the Pico's ADC and built-in calculation of the arithmetic mean when reading an analog pin. Also supports temperature reads independent of ADC resolution and reference voltage. 7 | category=Signal Input/Output 8 | url=https://github.com/NuclearPhoenixx/Arduino-Pico-Analog-Correction 9 | architectures=rp2040 10 | --------------------------------------------------------------------------------