LM335 with Arduino - Page 1
Forum section: electronics / beginners

author: skillz21
author_rank: Frequent Contributor
timestamp: September 15, 2017, 10:09:58 am
content: I found this code online on how to use an LM335 with Arduino. In the code, there is a part where you have to enter in the voltage of the 5v rail of the Arduino. Unfortunately, this changes when other loads are connected (Like a HC-05... I have some questions on that, later). is there a way to change the code, so that the voltage is measured by the Arduino constantly so that the calibration voltage changes each time the voltage changes?Code:class LM335{float cal;int pin;public:LM335(float mCal, int mPin);float measureV();float measureK();float measureC();float measureF();float measureRankine();};LM335::LM335(float mCal, int mPin){cal = mCal;pin = mPin;}float LM335::measureV(){float retVal = (float) analogRead(pin);retVal = (retVal*cal)/1024.0;return retVal;}float LM335::measureK(){return measureV()/0.01;//10mV/k}float LM335::measureC(){return (measureV()/0.01)-273.15;}float LM335::measureF(){return (((measureV()/0.01)-273.15)*1.8)+32;}float LM335::measureRankine(){return measureF() + 458.67;}LM335 mTemp(4.98, 0);//supply volts, analog pinvoid setup(){Serial.begin(9600);}void loop(){/*Serial.print(mTemp.measureV());Serial.println(" volts");Serial.println("Temperatures: ");Serial.print("Kelvin: ");Serial.println(mTemp.measureK());Serial.print("Fahrenheit: ");Serial.println(mTemp.measureF());Serial.println("Celsius: ");*/Serial.print(mTemp.measureC());/*Serial.print("Rankine: ");Serial.println(mTemp.measureRankine());*/delay(10);}

author: skillz21
author_rank: Frequent Contributor
timestamp: September 15, 2017, 10:50:34 am
content: For the Bluetooth part, I'm trying to make an app, with MIT AI2. Does anyone know how to trigger an action from an inbound message from a Bluetooth module?

author: danadak
author_rank: Super Contributor
timestamp: September 15, 2017, 11:00:40 am
content: A/D, the problem with Atmel is no onchip absolute reference. So for accuracyone has to1) Add a known reference and measure it as part of the T measurement process.2) Add a known reference and use it to power the Arduino rail.3) Use a T sensor that has its own internal reference and outputs its result over SPI or I2C or One Wire.Regards, Dana.

author: Monkeh
author_rank: Super Contributor
timestamp: September 15, 2017, 11:03:55 am
content: Quote from: danadak on September 15, 2017, 11:00:40 amErr.. they do have one.https://www.arduino.cc/en/Reference/AnalogReference

author: skillz21
author_rank: Frequent Contributor
timestamp: September 15, 2017, 12:16:43 pm
content: Quote from: Monkeh on September 15, 2017, 11:03:55 amThat's what I was thinking, could I use that?

author: danadak
author_rank: Super Contributor
timestamp: September 15, 2017, 01:56:25 pm
content: My error, thanks for catching that.Note, I looked at 2560 reference, its kind of sloppy -1V Ref ranges from 1V to 1.2V, thats not even 4 bits absolute accurate,even though A/D is capable of 10 bits.So the prior comments about solutions still apply if you want > 4 bits absoluteaccuracy. Do an end to end error budget to see what total error will be.Using external Vref -http://tronixstuff.com/2013/12/12/arduino-tutorials-chapter-22-aref-pin/If you desire much higher onchip accuracy PSOC has an onboard refgood for +/- 1% for low end, PSOC 4, +/- .1% for PSOC 5LP.Regards, Dana.

author: retrolefty
author_rank: Super Contributor
timestamp: September 15, 2017, 03:52:48 pm
content: QuoteRange variation is that much from chip to chip but is more likely 10X better for variation on any specific chip. The actual chip band-gap voltage can be measured and compensated in firmware.Arduino ADC is useful and can be adequate for many applications, however it is not a instrumentation quality ADC. I use TI I2C ADC ( adafruithttps://www.adafruit.com/product/1085) when a project requires decent accuracy with built-in reference. Asian clones of this are very inexpensive.

author: macboy
author_rank: Super Contributor
timestamp: September 15, 2017, 04:17:56 pm
content: You can't simply use the Arduino's internal 1.1 V reference because doing so limits the ADC range to 0 to 1.1 V full scale. The LM335 output is around 3 V at room temperature (300 Kelvin at 0.01 V/Kelvin). You could work around this by scaling the LM335 output with a voltage divider. (Note this will also increase the impedance at the ADC input, so you'll need to add a capacitor from input to ground to help). To calibrate the system, you would need to measure your reference voltage by measuring the AREF pin. You'll also need to adjust for you voltage divider by either scaling that voltage up accordingly, or by modifying the calculations in the code.Alternatively, you can use an external reference connected to the VREF pin. A plain old TL431, with appropriate voltage setting resistors, can be set to an arbitrary value. Around 4 V should work well.

author: danadak
author_rank: Super Contributor
timestamp: September 15, 2017, 04:34:17 pm
content: There is a way of using internal ref with high precision, approach mainly forhigh volume production.Basically during production test a cal routine is activated in DUT, it sendsa request to an external high accuracy/precision DC generator, and thatrequest, a specific V, is then supplied to the A/D input. The A/D value thenread and stored. After a sweep is done a least squares curve fit to the datais performed (or a power series curve). The equation is then used in normalmode each time the A/D returns a value to correct its deviation. This can bedone over T as well.Regards, Dana.

author: Monkeh
author_rank: Super Contributor
timestamp: September 15, 2017, 05:22:51 pm
content: Never said it had a good reference, just that it had one..

author: Boschi
author_rank: Regular Contributor
timestamp: September 15, 2017, 07:19:44 pm
content: Well, its stable enough over the temperature range, and the way that dana propose is the best one, but if you only want to know the temperature whitout very good precision its possible to use the internal themperature sensor of the atmega.You just set the Vref to 1.1 volt, tweak the analog input multiplexer register to read the internal sensor and do a analog read.Inviato dal mio A0001 utilizzando Tapatalk

author: HB9EVI
author_rank: Frequent Contributor
timestamp: September 15, 2017, 09:17:06 pm
content: I'm not using the Atmel internal references; what they thought about the 1.1V reference in certain uC, likely only god knows.In general I prefer binary-wise convenient references like 2.048V or 4.096V; for less accurate needs I go for the MCP1541, what would likely be my choice also in combination with the LM335. For higher accuracy I choose an ADR420 or a REF3040

author: retrolefty
author_rank: Super Contributor
timestamp: September 15, 2017, 10:57:42 pm
content: Quote from: Boschi on September 15, 2017, 07:19:44 pmThe problem is that the internal die temp is more an indication of current draw on output pins that would dominate over any indication of ambient temp.

author: skillz21
author_rank: Frequent Contributor
timestamp: September 15, 2017, 11:57:53 pm
content: Woah.... that's a lot of replies. Anyway, I don't really understand what you guys mean, can't I just input the 5v back into, say A1, and then read that, then set the voltage value in the code, to that? Also, can't I just input 5v into the AREF pin?

author: skillz21
author_rank: Frequent Contributor
timestamp: September 16, 2017, 12:00:18 am
content: Also, do you know anything about the app side of things? MIT App inventor?

author: danadak
author_rank: Super Contributor
timestamp: September 16, 2017, 12:25:51 am
content: Start with basics -1) What is your accuracy goal for T measurements ?2) How often do you want to measure the T, SPS ?3) What is the max worst case range of T you will want to measure ?Regards, Dana.

author: sleemanj
author_rank: Super Contributor
timestamp: September 16, 2017, 01:30:59 am
content: It is possible to read the Vcc by an AVR itself, you do this by reading the internal reference relative to Vcc, since you "know" the internal reference, and you don't know Vcc, but you do know that a full Vcc read will give you a certain result of the ADC, it's easy to "solve for Vcc".However as noted the internal reference isn't that accurate, indeed it can be really inaccurate. However, it is pretty stable, so what you do, is calibrate it first. You do your "read the Vcc" code, get the result that it calcualted, then you actually measure your Vcc (with a multimeter) and then you can calculate what the internal reference actually is (or, just a scaling factor) and adjust that in your code (or eeprom or however you want to store it). Once you have calibrated that once you are good-enough.Here is some code for doing that, readVcc() on an ATMega328 (Arduino Uno, Pro-Mini, Nano), but the theory is the same for other AVR, just the registers might be different. This was ripped out of one of my programs, I think it should be stand-alone though. You can see that when I calibrated the internal "1.1v" reference on whatever I was putting this code onto, I found it was 1.081 (and some change), so quite far out, but it is relatively stable at that.Code:/** Read the AVR's own VCC.** Note that the internalReference must be calibrated* perform a reading, and get the result, measure the actual Vcc** if the actual Vcc is lower, the internalReference needs to decrease* internalReference = internalReference * ( readVcc / realVcc )** if the actual Vcc is higher, the internalReference needs to increase* internalReference = internalReference * ( realVcc / readVcc )***/float readVcc(uint8_t force){uint32_t result = 0;const uint8_t analogDiscard = 1;const uint8_t analogSamples = 5;const float internalReference = 1.081187434;static float lastReading = 0;static uint32_t lastRefVoltageUpdate = 0;if(!force && (lastReading != 0 && (millis() - lastRefVoltageUpdate) < 10000)){return lastReading;}lastRefVoltageUpdate = millis();// Connect the internal 1.1v to the ADC MUX// (leave the Analog Reference as Vcc_ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);delay(2); // Wait for Vref to settle// Discard some results to get better accuracyfor(uint8_t dummy = 0; dummy < analogDiscard; dummy++){ADCSRA |= _BV(ADSC); // Convertwhile (bit_is_set(ADCSRA,ADSC));}uint32_t resultSum = 0;// Average some conversionsfor(uint8_t dummy = 0; dummy < analogSamples; dummy++){ADCSRA |= _BV(ADSC); // Convertwhile (bit_is_set(ADCSRA,ADSC));result = ADCL;result |= ADCH<<8;resultSum += result;}// Read the ADC result// result = ADCL;// result |= ADCH<<8;result = resultSum / analogSamples;// We know we measured 1.1v// So the result 0 to 1023 is a proportion of Vcc// 1.1 = (Result / 1023) * Vcc// 1.1 = Vcc * (Result / 1023)// 1.1 / (Result / 1023) = VcclastReading = (internalReference / ( (float)result / 1023.0 ));return lastReading;}1023 vs 1024Additional because somebody might point it out in my code...I use "1023" for ADC operations, others use "1024", there are valid arguments for and against either.In short, we need to remember that an ADC result doesn't represent a specific voltage, it's a voltage range (of course since we have a discrete number of steps), assuming a 5v reference for sake of demonstration, it's roughly a 5mv range for each ADC result.So we get 1024 boxes numbered 0 - 1023, box 0 (ADC result 0) contains any readings 0v to approx 5mv, box 1023 contains any readings approx 4.995v to 5v.Dividing the reference by 1024 (number of boxes) you always wind up calculating to the lower value, 0 = 0v, and 1023 = 4.996v, that's totally fine and correct, provided you remember that it's actually "0-0.005mv" and "4.996-5v".If you divide by 1023 (maximum box number), you will calculate the lower value of the range at the bottom (0 = 0v) and the upper value of the range at the top (1023 = 5v)Both approaches have merit but I prefer 1023.You are not really introducing any error that isn't already there, just moving it from always being a negative error of up to 5mv, to being an error that can vary +/-2.5mv (indeed, see thisgoogle sheet I made comparing 1023 to 1024)Of course 5mv is probably in the noise anyway on pretty much any sort of Arduino design, so this is all pretty academic.

author: danadak
author_rank: Super Contributor
timestamp: September 16, 2017, 12:30:26 pm
content: You can get a Vref part for 50 cents or less, +/- .1%.Take the easy way out. Or use a UP like PSOC that has onchipref.Regards, Dana.

author: skillz21
author_rank: Frequent Contributor
timestamp: September 17, 2017, 03:02:35 am
content: Quick question, I added a potentiometer so I could manually change the calibration voltage of the LM335. But when i copy the part where i have to enter the VCC into the code, into the loop (from before the setup), it stops working, how do i change the code so that i can continually adjust the calibration voltage? also, is it possible to use the potentiometer and change the voltage range to 4v to 6v, intead of 0v to 6v as i have it now?Thanks#include <LiquidCrystal_I2C.h>const int inPin = A0;float inPinout1;float inPinout2;const int buzz = A1;int sensorPin = A3;float sensorValue = 0;LiquidCrystal_I2C lcd(0x27, 16, 2);class LM335{float cal;int pin;public:LM335(float mCal, int mPin);float measureV();float measureK();float measureC();float measureF();float measureRankine();};LM335::LM335(float mCal, int mPin){cal = mCal;pin = mPin;}float LM335::measureV(){float retVal = (float) analogRead(pin);retVal = (retVal*cal)/1024.0;return retVal;}float LM335::measureK(){return measureV()/0.01;//10mV/k}float LM335::measureC(){return (measureV()/0.01)-273.15;}float LM335::measureF(){return (((measureV()/0.01)-273.15)*1.8)+32;}float LM335::measureRankine(){return measureF() + 458.67;}LM335 mTemp(4.99, 2);//supply volts, analog pinvoid setup() {pinMode(inPin, INPUT);pinMode(buzz, OUTPUT);Serial.begin(9600);lcd.begin();lcd.backlight();}void loop() {{LM335 mTemp(sensorPin, 2);//supply volts, analog pinsensorValue = analogRead(sensorPin) / 170.5;inPinout1 = analogRead(inPin)/ 200.588;inPinout2 = inPinout1 / 200.588;Serial.println(inPinout1);lcd.setCursor(0, 0);lcd.print(analogRead(inPin)/200.588);lcd.setCursor(5, 0);lcd.print("Volts");lcd.setCursor(0, 1);lcd.print(mTemp.measureC());lcd.setCursor(8, 1);lcd.print(sensorValue);delay(50);}{if(inPinout1 == 1.37){digitalWrite(buzz, HIGH);delay(1);digitalWrite(buzz, LOW);delay(1);}}}