Ранее в статье http://rcl-radio.ru/?p=131906 был описан пример создания терморегулятора на ADS1115, в этой статье показан аналогичный пример создания терморегулятора. Так как в ADS1115 имеется два дифференциальных входа, то можно сделать 2-х канальный терморегулятор. Каналы терморегулятора будут работать отдельно друг от друга, в каждом канале можно задать свой тип термопары и температуру регулирования.
Как и в http://rcl-radio.ru/?p=131906 терморегулятор рассчитан на работу с тремя типами термопар:
- S — ТПП (Платина — 10 % родий/платина)
- К — ТХА [Никель — хром/никель — алюминий (хромель/алюмель)]
- L — ТХК (Хромель/копель)
Управление терморегулятора осуществляется 4-я кнопками:
- Кнопка выбора канала для регулировки температуры регулирования и типа используемой термопары
- Две кнопки увеличения или уменьшения температуры регулирования
- Кнопка выбора типа термопары
При измерении температуры при помощи термопары необходимо учитывать температуру окружающей среды, в качестве датчика температуры окружающей среды используется цифровой датчик температуры LM75.
Показания текущей температуры и температура регулирования выводится на дисплей LCD1602 с модулем I2C.
На дисплей выводится следующая информация:
- Измеренная температура с учетом температуры окружающей среды для обеих каналов
- Температура регулирования для обеих каналов
- Тип применяемой термопары L K или S (ТХА ТХК или ТППs)
Подключение термопары к терморегулятору можно напрямую или использовать термокомпенсационные провода того же типа что и термопара.
Схема терморегулятора
Так как не у всех радиолюбителей имеется в наличии высокоточные измерительные приборы, поэтому компоненты терморегулятора выбраны таким образом, чтобы терморегулятор не нуждается в калибровке и обеспечивал погрешность измерения не более чем ±1°С во всем диапазоне измерения. Единственный параметр который Вы можете изменить в скетче это гистерезис, который имеет значение в 1°С.
#define GIS 1.0 #include <Wire.h> #include <Adafruit_ADS1015.h> // https://github.com/adafruit/Adafruit_ADS1X15/archive/master.zip #include <LiquidCrystal_I2C.h> #include <LM75.h> // https://github.com/thefekete/LM75.git #include <EEPROM.h> LiquidCrystal_I2C lcd(0x27,16,2); Adafruit_ADS1115 ads; LM75 sensor(LM75_ADDRESS | 0b111); // if A0->Vcc, A1->Vcc and A2->Vcc int16_t results_0,results_1; float multiplier = 0.0078125F; float t,u,t_iz,t_reg_0,t_reg_1,t_0,t_1; unsigned long times,times0; bool w,w1,kanal; int tip_0,tip_1,up_0,dw_0,up_1,dw_1; //K const float a[]{0,2.5173462*10,-1.1662878,-1.08336338,-8.9773540/10,-3.7342377/10,-8.6632643/100,-1.0450598/100,-5.1920577/10000}; const float a1[]{0,2.508355*10,7.860106/100,-2.503131/10,8.315270/100,-1.228034/100,9.804036/10000,-4.413030/100000,1.057734/1000000,-1.052755*pow(10,-8)}; const float a2[]{-1.318058*100,4.830222*10,-1.646031,5.464731/100,-9.6550715/10000,8.802193/1000000,-3.110810/100000000}; //L const float b[]{1.1573067/10000,1.5884573*10,4.0458554/100,0.3170064,0.1666128,5.146958/100,9.5288883/1000,1.0301283/1000,6.0654431/100000,1.5131878/1000000}; const float b1[]{7.2069422/1000,1.5775525*10,-0.2261183,9.4286756/1000,-3.5394655/10000,1.0050886/100000,-1.9323678/10000000,2.3816891/1000000000,-1.7130654*pow(10,-11),5.4857331*pow(10,-14)}; //S const float c[]={0,1.84949460*100,-8.00504062*10,1.02237430*100,-1.52248592*100,1.88821343*100,-1.59085941*100,8.23027880*10,-2.34181944*10,2.79786260}; const float c1[]={1.291507177*10,1.466298863*100,-1.534713402*10,3.145945973,-4.163257839/10,3.187963771/100,-1.291637500/1000,2.183475087/100000,-1.447379511/10000000,8.211272125/1000000000}; const float c2[]={-8.087801117*10,1.621573104*100,-8.536869453,4.719686976/10,-1.441693666/100,2.081618890/10000}; const float c3[]={5.333875126*10000,-1.235892298*10000,1.092657613*1000,-4.265693686*10,6.247205420/10}; void setup() { Wire.begin();delay(200); lcd.init();lcd.backlight(); ads.setGain(GAIN_SIXTEEN); ads.begin(); analogReference(INTERNAL);// опорное 1,1 В pinMode(2,INPUT_PULLUP); pinMode(3,INPUT_PULLUP); pinMode(4,INPUT_PULLUP); pinMode(5,INPUT_PULLUP); pinMode(8,OUTPUT); pinMode(9,OUTPUT); if(EEPROM.read(100)!=0){for(int i=0;i<101;i++){EEPROM.update(i,0);}} tip_0 = EEPROM.read(0);t_reg_0 = EEPROM.read(1)*256 + EEPROM.read(2); tip_1 = EEPROM.read(10);t_reg_1 = EEPROM.read(11)*256 + EEPROM.read(12); } void loop() { if(digitalRead(5)==LOW && kanal==0){kanal=1;w=1;times0=millis();delay(300);} if(digitalRead(5)==LOW && kanal==1){kanal=0;w=1;times0=millis();delay(300);} if(kanal==0){ if(digitalRead(2)==LOW){tip_0++;if(tip_0>2){tip_0=0;}w=1;times0=millis();delay(300);} if(digitalRead(3)==LOW){if(up_0>10){t_reg_0+=10;}else{t_reg_0++;}up_0++;w=1;w1=1;times0=millis();delay(200);lcd.clear();} if(digitalRead(4)==LOW){if(dw_0>10){t_reg_0-=10;}else{t_reg_0--;}dw_0++;w=1;w1=1;times0=millis();delay(200);lcd.clear();} if(digitalRead(3)==HIGH){up_0=0;}if(digitalRead(4)==HIGH){dw_0=0;} if(tip_0==0 && t_reg_0<-240){t_reg_0=-240;}if(tip_0==0 && t_reg_0>1372){t_reg_0=1372;} if(tip_0==1 && t_reg_0<-200){t_reg_0=-200;}if(tip_0==1 && t_reg_0>800){t_reg_0=800;} if(tip_0==2 && t_reg_0<-50){t_reg_0=-50;} if(tip_0==2 && t_reg_0>1768){t_reg_0=1768;} } if(kanal==1){ if(digitalRead(2)==LOW){tip_1++;if(tip_1>2){tip_1=0;}w=1;times0=millis();delay(300);} if(digitalRead(3)==LOW){if(up_1>10){t_reg_1+=10;}else{t_reg_1++;}up_1++;w=1;w1=1;times0=millis();delay(200);lcd.clear();} if(digitalRead(4)==LOW){if(dw_1>10){t_reg_1-=10;}else{t_reg_1--;}dw_1++;w=1;w1=1;times0=millis();delay(200);lcd.clear();} if(digitalRead(3)==HIGH){up_1=0;}if(digitalRead(4)==HIGH){dw_1=0;} if(tip_1==0 && t_reg_1<-240){t_reg_1=-240;}if(tip_1==0 && t_reg_1>1372){t_reg_1=1372;} if(tip_1==1 && t_reg_1<-200){t_reg_1=-200;}if(tip_1==1 && t_reg_1>800){t_reg_1=800;} if(tip_1==2 && t_reg_1<-50){t_reg_1=-50;} if(tip_1==2 && t_reg_1>1768){t_reg_1=1768;} } if(millis()-times>1000){times=millis();w=1;} if(w==1){w=0; results_0 = ads.readADC_Differential_0_1(); results_1 = ads.readADC_Differential_2_3(); t_iz = sensor.temp(); switch(tip_0){ case 0: txa(results_0);break; case 1: txk(results_0);break; case 2: tpp_s(results_0);break; } t_0=t; switch(tip_1){ case 0: txa(results_1);break; case 1: txk(results_1);break; case 2: tpp_s(results_1);break; } t_1=t; lcd.setCursor(0,0); if(t_0==10000){lcd.print(" HIGH ");} else if(t_0==-1000){lcd.print(" LOW ");} else{ if(kanal==0){lcd.print("T1>");}else{lcd.print("T1 ");}lcd.print(t_0,0);lcd.print((char)223);lcd.print("C ");} lcd.setCursor(0,1); if(t_1==10000){lcd.print(" HIGH ");} else if(t_1==-1000){lcd.print(" LOW ");} else{ if(kanal==1){lcd.print("T2>");}else{lcd.print("T2 ");}lcd.print(t_1,0);lcd.print((char)223);lcd.print("C ");} lcd.setCursor(10,0); switch(tip_0){ case 0: lcd.print("K");break; case 1: lcd.print("L");break; case 2: lcd.print("S");break; } lcd.setCursor(10,1); switch(tip_1){ case 0: lcd.print("K");break; case 1: lcd.print("L");break; case 2: lcd.print("S");break; } lcd.setCursor(12,0);lcd.print(t_reg_0,0); lcd.setCursor(12,1);lcd.print(t_reg_1,0); } if(millis()-times0>10000 && w1==1){w1=0; EEPROM.update(0,tip_0);EEPROM.update(1,highByte(int(t_reg_0)));EEPROM.update(2,lowByte(int(t_reg_0))); EEPROM.update(10,tip_1);EEPROM.update(11,highByte(int(t_reg_1)));EEPROM.update(12,lowByte(int(t_reg_1))); } if(t_reg_0 >= t_0 + GIS){digitalWrite(8,HIGH);} if(t_reg_0 <= t_0 - GIS){digitalWrite(8,LOW);} if(t_reg_1 >= t_1 + GIS){digitalWrite(9,HIGH);} if(t_reg_1 <= t_1 - GIS){digitalWrite(9,LOW);} } void txa(float rez){u=rez * multiplier+t_iz*0.0403;// окружающая температура 0.0403 мВ на 1 гр.Цельсия от 0 до 40 гр.Цельсия для TXA(K) if(u<0){t=(a[0]*pow(u,0))+(a[1]*pow(u,1))+(a[2]*pow(u,2))+(a[3]*pow(u,3))+(a[4]*pow(u,4))+(a[5]*pow(u,5))+(a[6]*pow(u,6))+(a[7]*pow(u,7))+(a[8]*pow(u,8));} if(u>=0&&u<=20.64){t=(a1[0]*pow(u,0))+(a1[1]*pow(u,1))+(a1[2]*pow(u,2))+(a1[3]*pow(u,3))+(a1[4]*pow(u,4))+(a1[5]*pow(u,5))+(a1[6]*pow(u,6))+(a1[7]*pow(u,7))+(a1[8]*pow(u,8))+(a1[9]*pow(u,9));} if(u>20.64){t=(a2[0]*pow(u,0))+(a2[1]*pow(u,1))+(a2[2]*pow(u,2))+(a2[3]*pow(u,3))+(a2[4]*pow(u,4))+(a2[5]*pow(u,5))+(a2[6]*pow(u,6));} if(rez * multiplier<-6.35){t=-1000;} if(rez * multiplier>54.9){t=10000;} } void txk(float rez){u=rez * multiplier+t_iz*0.06555;// окружающая температура 0.06555 мВ на 1 гр.Цельсия от 0 до 40 гр.Цельсия для TXK(L) if(u<0){t=(b[0]*pow(u,0))+(b[1]*pow(u,1))+(b[2]*pow(u,2))+(b[3]*pow(u,3))+(b[4]*pow(u,4))+(b[5]*pow(u,5))+(b[6]*pow(u,6))+(b[7]*pow(u,7))+(b[8]*pow(u,8))+(b[9]*pow(u,9));} if(u>=0){t=(b1[0]*pow(u,0))+(b1[1]*pow(u,1))+(b1[2]*pow(u,2))+(b1[3]*pow(u,3))+(b1[4]*pow(u,4))+(b1[5]*pow(u,5))+(b1[6]*pow(u,6))+(b1[7]*pow(u,7))+(b1[8]*pow(u,8))+(b1[9]*pow(u,9));} if(rez * multiplier<-9.49){t=-1000;} if(rez * multiplier>66.47){t=10000;} } void tpp_s(float rez){u=rez * multiplier+t_iz*0.005875;// окружающая температура 0.005875 мВ на 1 гр.Цельсия от 0 до 40 гр.Цельсия для TPPs(S) if(u<1.874){t=(c[0]*pow(u,0))+(c[1]*pow(u,1))+(c[2]*pow(u,2))+(c[3]*pow(u,3))+(c[4]*pow(u,4))+(c[5]*pow(u,5))+(c[6]*pow(u,6))+(c[7]*pow(u,7))+(c[8]*pow(u,8))+(c[9]*pow(u,9));} if(u>=1.874&&u<10.332){t=(c1[0]*pow(u,0))+(c1[1]*pow(u,1))+(c1[2]*pow(u,2))+(c1[3]*pow(u,3))+(c1[4]*pow(u,4))+(c1[5]*pow(u,5))+(c1[6]*pow(u,6))+(c1[7]*pow(u,7))+(c1[8]*pow(u,8))+(c1[9]*pow(u,9));} if(u>=10.332&&u<17.536){t=(c2[0]*pow(u,0))+(c2[1]*pow(u,1))+(c2[2]*pow(u,2))+(c2[3]*pow(u,3))+(c2[4]*pow(u,4))+(c2[5]*pow(u,5));} if(u>=17.536&&u<=18.694){t=(c3[0]*pow(u,0))+(c3[1]*pow(u,1))+(c3[2]*pow(u,2))+(c3[3]*pow(u,3))+(c3[4]*pow(u,4));} if(rez * multiplier<-0.236){t=-1000;} if(rez * multiplier>18.70){t=10000;} }
При сборке терморегулятора обратите внимание на адресную шину датчика температуры LM75, по умолчанию I2C адрес датчика 0х48, такой же как и у АЦП ADS1115, поэтому все выходы адресной шины должны быть замкнуты на VCC (A0 > VCC, A1 > VCC, A2 > VCC), для установки адреса 0x55.
За адрес шины в скетче отвечает строка:
LM75 sensor(LM75_ADDRESS | 0b111); // if A0->Vcc, A1->Vcc and A2->Vcc
При выход за границы диапазона ТЭДС на входе терморегулятора на дисплее появится надпись HIGH или LOW.
Если замкнуть вход терморегулятора, на дисплее должна отображаться температура окружающей среды.
Форум — http://forum.rcl-radio.ru/viewtopic.php?pid=10165#p10165