PDA

View Full Version : PID thermostat Issue



Scampy
- 23rd February 2015, 20:22
Several years ago, Darrel (god rest his soul) and Henrik helped me no end in developing a 4 channel controller for my reptiles. I've used this code as a basis to develop simplified version, and to see if I can use a DTH11 sensor to incorporate humidity readout. Here is what I've got



'************************************************* ******************************
' Set Config - 18F2520
'************************************************* ******************************
ASM
__CONFIG _CONFIG1H, _OSC_HS_1H
__CONFIG _CONFIG2L, _PWRT_ON_2L
__CONFIG _CONFIG2H, _WDT_ON_2H & _WDTPS_512_2H
__CONFIG _CONFIG3H, _MCLRE_ON_3H & _LPT1OSC_OFF_3H & _PBADEN_OFF_3H
__CONFIG _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L
ENDASM

'************************************************* ******************************
' LCD (20 x 4) set up
'************************************************* ******************************

DEFINE LCD_DREG PORTB ' LCD Data port
DEFINE LCD_DBIT 0 ' starting Data bit (0 or 4)
DEFINE LCD_EREG PORTB ' LCD Enable port
DEFINE LCD_EBIT 5 ' Enable bit (on EasyPIC 5 LCD)
DEFINE LCD_RSREG PORTB ' LCD Register Select port
DEFINE LCD_RSBIT 4 ' Register Select bit (on EasyPIC 5 LCD)
DEFINE LCD_BITS 4 ' LCD bus size (4 or 8 bits)
DEFINE LCD_LINES 4 ' number of lines on LCD
DEFINE LCD_COMMANDUS 2000 ' Command delay time in us
DEFINE LCD_DATAUS 50 ' Data delay time in us

'************************************************* ******************************
' Defines Statements
'************************************************* ******************************

DEFINE OSC 20 ' 18F4520 / 18F2520, 20mhz crystal
clear

'************************************************* ******************************
'Analog and Comparator settings
'************************************************* ******************************

ADCON0 = %00000000 'AD converter module disabled
ADCON1 = %00001111 'All Digital
ADCON2 = %00000000
CMCON = 7 'Disable Comparators

'************************************************* ******************************
'Pins & Ports
'************************************************* ******************************

TempSensor1 VAR PORTA.5 ' Pin assigned to Sensor (EasyPIC5)
HeaterOut1 VAR PORTA.4 ' Output

'************************************************* ******************************
'Interrupts
'************************************************* ******************************

DEFINE WRITE_INT 1
INCLUDE "DT_INTS-18.bas" ; DT's Base Interrupt System

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR1_INT, HeaterDrive, ASM, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

T1CON = %00000001 ; free-running, 1:4 prescaler
@ INT_ENABLE TMR1_INT ; enable Timer1 interrupts

'************************************************* ******************************
'DS18B20 Defines
'************************************************* ******************************
DEFINE DS1820_DECIMALS 1 ' 1
DEFINE DS1820_VERIFYCRC YES ' NO
DEFINE DS18B20_ONLY YES ' NO
INCLUDE "DT18x20.pbp" ' Include DT18x20 module

'************************************************* ******************************
'Inc PID routine
'************************************************* ******************************
INCLUDE "incPID.pbp" ' Include the PID routine.

'************************************************* ******************************
'Varibles
'************************************************* ******************************

TempWD VAR WORD ' temporary WORD variable
FlashStar VAR BIT
GIE VAR INTCON.7
spMode1 VAR BYTE
SetPoint1 VAR WORD[1]
Temperatures VAR WORD
HeatCycle VAR BYTE BANK0 SYSTEM
HeaterDrives VAR BYTE[4] BANK0 SYSTEM
HeatDrive1 VAR HeaterDrives[0]
SensorActive VAR BIT
ChannelPWR VAR BIT

'************************************************* ******************************
'EEPROM data
'************************************************* ******************************

EE_spMode1 DATA 0 ' 0=Manual mode, temp is set by Pot.
EE_SetPoint1 DATA WORD 250 ' 25.0 deg. after programming if not in manual mode
EE_pid_Kp1 DATA WORD $0700 ' PID constants $0700, $0080, $0200
EE_pid_Ki1 DATA WORD $0080
EE_pid_Kd1 DATA WORD $0200
EE_pid_Ti1 DATA 8 ' Update I-term every 8th call to PID
EE_pid_I_Clamp1 DATA 250 ' Clamp I-term to max ±100
EE_pid_Out_Clamp1 DATA WORD 255 ' Clamp the final output to 255
EE_CH1PWR DATA 1


READ EE_spMode1, spMode1
READ EE_SetPoint1, WORD SetPoint1
READ EE_pid_Kp1, WORD pid_Kp
READ EE_pid_Ki1, WORD pid_Ki
READ EE_pid_Kd1, WORD pid_Kd
READ EE_pid_Ti1, pid_Ti
READ EE_pid_I_Clamp1, pid_I_Clamp
READ EE_pid_Out_Clamp1, WORD pid_Out_Clamp
read EE_CH1PWR, ChannelPWR

'************************************************* ******************************
'Initialization
'************************************************* ******************************

LCDOUT $FE,1:FLAGS=0:PAUSE 250:LCDOUT $FE,1:PAUSE 250 ' Initialize LCD

@ DS1820_Select _TempSensor1 ; Select the DS18x20 pin
@ DS1820_Resolution 12 ; Set # of bits in resolution

LOW HeaterOut1 ; set heater pin to Output

'************************************************* ******************************
'Main Program
'************************************************* ******************************
Main:

@ DS1820_Convert ; start a temperature conversion
@ DS1820_Stat ; check the sensors status
; enable interrupts after 1-wire
PAUSEUS 20
GIE = 1 ; disable interrupts before 1-wire
@ DS1820_Read ; get the temperature result
@ DS1820_Stat
GIE = 1

IF DS1820_Error = 0 THEN ; if there were no errors
LCDOUT $FE,$80 ; line 1, col 0
TempWD = TempC : GOSUB TempToLCD ; display TempC
'TempWd = TempC
pid_Error = SetPoint1 - TempC
TempWD = pid_Error ; Display the error value
Gosub PID
IF pid_Out.15 THEN pid_Out = 0 ; only keep positive values
HeatDrive1 = pid_Out
ELSE ;--- Error reading Sensor --------
gosub senserror
ENDIF

GOTO Main

'************************************************* ******************************
'Interrupt
'************************************************* ******************************
ASM
HeaterDrive
incf HeatCycle,F ; HeatCycle

movf _HeatDrive1,W ; HeatDrive1
subwf HeatCycle,w
btfsc STATUS,C
bcf _HeaterOut1
btfss STATUS,C
bsf _HeaterOut1


INT_RETURN
ENDASM
'************************************************* ******************************
'Display Temp
'************************************************* ******************************
TempToLCD:
IF TempWD.15 THEN LCDOUT "-" ; if negative, display minus sign
TempWD = ABS(TempWD) ; get the positive value
LCDOUT DEC TempWD/DS1820_DIG ; Display the Integer portion
@ if (DS1820_DECIMALS > 0) ; if using decimals
LCDOUT "." ; display decimal point
TempWD = TempWD//DS1820_DIG ; get decimal portion
@ if (DS1820_DECIMALS > 1) ; with DECIMALS=2, next DIG is 1
LCDOUT DEC1 TempWD DIG 1
@ endif
@ if (DS1820_DECIMALS >= 1) ; with DECIMALS=1, next DIG is 0
LCDOUT DEC1 TempWD DIG 0
@ endif
@ endif
RETURN
'************************************************* ******************************
'Sensor Error
'************************************************* ******************************
senserror:
LCDOUT $FE,1,"Sensor Error"
HeatDrive1 = 0
pause 1000
LCDOUT $FE,1," "
return


The only issue I have with this code is that unlike the 4 channel version (which has been keeping my vivariums at a stable temperature for the past 4 years) is that it jumps out to the sensor error routine to often for my liking. Other than that it behaves just like the 4ch version and maintains a stable temperature around the set point. Any suggestions as to reduce the frequency that it detects a miss-read would be welcome.

OK now on to the second part.

I've used this code below to read the DTH11 sensor and display the results on an LCD



ASM
__CONFIG _CONFIG1H, _OSC_HS_1H
__CONFIG _CONFIG2L, _PWRT_ON_2L
__CONFIG _CONFIG2H, _WDT_ON_2H & _WDTPS_512_2H
__CONFIG _CONFIG3H, _MCLRE_ON_3H & _LPT1OSC_OFF_3H & _PBADEN_OFF_3H
__CONFIG _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L
ENDASM


'************************************************* ******************************
' LCD (20 x 4) set up
'************************************************* ******************************

DEFINE LCD_DREG PORTB ' LCD Data port
DEFINE LCD_DBIT 0 ' starting Data bit (0 or 4)
DEFINE LCD_EREG PORTB ' LCD Enable port
DEFINE LCD_EBIT 5 ' Enable bit (on EasyPIC 5 LCD)
DEFINE LCD_RSREG PORTB ' LCD Register Select port
DEFINE LCD_RSBIT 4 ' Register Select bit (on EasyPIC 5 LCD)
DEFINE LCD_BITS 4 ' LCD bus size (4 or 8 bits)
DEFINE LCD_LINES 4 ' number of lines on LCD
DEFINE LCD_COMMANDUS 2000 ' Command delay time in us
DEFINE LCD_DATAUS 50 ' Data delay time in us

'************************************************* ******************************
' Defines Statements
'************************************************* ******************************

DEFINE OSC 20 ' 18F4520 / 18F2520, 20mhz crystal
ADCON1 = $0F
clear

'************************************************* ******************************
'Analog and Comparator settings
'************************************************* ******************************

ADCON0 = %00000000 'AD converter module disabled
ADCON1 = %00001111 'All Digital
ADCON2 = %00000000
CMCON = 7 'Disable Comparators

'************************************************* ******************************
'Port and Register settings (interrupts)
'************************************************* ******************************

TRISA = %00010111
TRISB = %00000011
T0CON = %11000111

T1CON = %00000001 ; free-running, 1:1 prescaler
TMR1H = %11111111
TMR1L = %11111011


'************************************************* ******************************

LCDOUT $FE,1:FLAGS=0:PAUSE 250:LCDOUT $FE,1:PAUSE 250 ' Initialize LCD

'************************************************* ******************************

dht var byte[32]
humidite var byte
haut var byte
bas var byte
temp var byte
x var byte
dht11 var portA.5

'************************************************* ******************************
start:
TRISA.5 = 0 '
high dht11
pause 2000 ' wait 2 sec
low dht11 : pause 18' send 20ms low
high dht11 : pauseus 30 ' send 40us hi
TRISA.5 = 0
PulsIn PORTA.5, 1, haut
if haut < 15 then goto start
for x = 31 to 0 step-1
PulsIn PORTA.5, 1, dht[x] ' 1
next x
For x = 31 to 0 step-1
if dht(x)>=9 and dht(x)<=21 then dht(x)=0 'if pulsewidth between 20 and 40uS then read as '0'
if dht(x)>=29 and dht(x)<=41 then dht(x)=1 'if pulsewidth between 60 and 80uS then read as '1'
next x
humidite=dht[31]*128+dht[30]*64+dht[29]*32+dht[28]*16+dht[27]*8+dht[26]*4+dht[25]*2+dht[24]*1
temp=dht[15]*128+ dht[14]*64+dht[13]*32+dht[12]*16+dht[11]*8+dht[10]*4+dht[9]*2+dht[8]*1
lcdout $FE,1
lcdout $FE,$C0,"Humidite = ",#humidite,"% "
lcdout $FE,$80,"Temperature = ",#temp,$DF,"C"
goto start


The part I'm stumped with is how to use the data from the DTH11 to replace that of the 18B20. If I read the data sheet for the DHT11 once triggered to send data, it sends a 40 bit serial data which includes 8 bit temperature and 8 bit humidity (presumably bits 8-15 for temp and bits 24 to 31 for humidity by the code high-lighted). The data sheet for the 18B20 states that you can set the resolution for temp to digital conversion between 9 and 12 bits, but I'm sure it sends the actual temperature value as 2 x 8 bit bytes ??

Constructive comments welcome.

Tabsoft
- 23rd February 2015, 22:24
Is your code for the second part working?
I'm not quite sure what you are asking.

Scampy
- 24th February 2015, 00:49
Yes, both programs work independently. The second section of code just proves the DHT sensor works, the code above it is the main program that uses a DS18B20 sensor. I would like to use the DHT sensor in place of the 18B20 so that it's temperature data can be used to work with the PID routine. The problem is that the two sensors use different one wire protocol as far as I can tell, and that's what I am asking for help with.

Tabsoft
- 24th February 2015, 01:04
Okay.
It looks like the routines you want to use the data in are in the Include files..

Do you want to provide the whole thing so we can see if we can help?

HenrikOlsson
- 24th February 2015, 07:38
Hi,
I've never used the DS18B20 or the DHT11.
Looking at the datasheet for the DS18B20 it seems like it returns the temperature in units of 0.1 degrees. Ie, if the returned value is 325 the actual temperature is 32.5C.
The DHT11 claims a resoultion of 8 bits and 1 degree but it still seems to send 16 bits of temperature data where the low byte is the decimal part (which I guess will always be 0).

Anyway, if you want to use the DHT11 instead of the DS18B20 what you want to do is make sure to scale the raw value so that they both give you the same value at a certain temperature. They might already DO that but you need to verify it. In the DS18B20 code your raw value seems to be the TempC variable while in the DHT11 code it's the temp variable.

At a known temperature (roughly) run the 18B20 code and output the TempC variable on the LCD. What does it say? Something like 225 for 22.5 degrees?
At the same temperarure (roughly) run the DHT11 code and output the Temp variable on the LCD. What does it say? Something like 220 for 22.5 degrees? Or something else?

If they are the same (roughly) then you're good to go as is. Otherwise you need to scale the DHT11 value so it gives the same result as the DS18B20 code for any given temperature (or you need to retune the PID filter).

With that said that DHT11 code looks REALLY convoluted, especially the highlighted part.

I wrote the following, not saying it'll work, have no sensor here to verify with.

PulsWidth VAR BYTE
BitValue VAR BIT

Humidity VAR WORD
Temperature VAR WORD
Checksum VAR BYTE


Main:
GOSUB ReadDHT11
LCDOUT $FE, 1, "RH: ", DEC Humidity, "%"
LCDOUT $FE, $C0, "Temp: ", DEC Temperature, "c"
PAUSE 1000
Goto Main


ReadDHT11:
' DHT11 reference: http://www.micropik.com/PDF/dht11.pdf
' Datasheet says resolution is 8 bits yet it sends 16 bits
' of data. This might be legacy from the higher resolution
' DHT22 sensor and the lower byte (the decimal part) is
' probably 0 all the time. No sensor here to verify with
' and datasheet is typical Chinese cut'n'paste stuff....

' Add the starbit stuff here !

' Read 16 bits of humidity data
FOR x = 15 to 0 step - 1
GOSUB GetBit
Humidity.0[x] = BitValue
NEXT x

' Read 16 bits of temperature data
FOR x = 15 to 0 step - 1
GOSUB GetBit
Temperature.0[x] = BitValue
NEXT x

' Read 8 bits of checksum data
FOR x = 7 to 0 step - 1
GOSUB GetBit
Checksum.0[x] = BitValue
NEXT x

' Checksum is the 8 bit sum of the 4 databytes
' this could be calculated and verified against
' the recieved checksum here.
RETURN

GetBit:
PulsIn PORTA.5, 1, PulsWidth
IF PulsWidth >= 9 AND PulsWidth <= 21 THEN BitValue = 0 ' if pulsewidth between 20 and 40uS then read as '0'
IF PulsWidth >= 29 AND PulsWidth <= 21 THEN BitValue = 1 ' if pulsewidth between 60 and 80uS then read as '1'
RETURN


/Henrik.

Scampy
- 24th February 2015, 15:40
Anyway, if you want to use the DHT11 instead of the DS18B20 what you want to do is make sure to scale the raw value so that they both give you the same value at a certain temperature. They might already DO that but you need to verify it. In the DS18B20 code your raw value seems to be the TempC variable while in the DHT11 code it's the temp variable.

At a known temperature (roughly) run the 18B20 code and output the TempC variable on the LCD. What does it say? Something like 225 for 22.5 degrees?
At the same temperature (roughly) run the DHT11 code and output the Temp variable on the LCD. What does it say? Something like 220 for 22.5 degrees? Or something else?

/Henrik.

Hi Henrik,

I've merged the two sections of code for some crude testing (I'll try you refined version for the DTH sensor later). Yes you are correct in that the DS18B20 results are stored in the variable TempC and the result is a three digit value as you summarized - ie 234 would be 23.4 degreed C. The DHT does the same, but the value for the result is in double figures - I've changed the variable for the result of reading the DHT to TempB.

Reading the values from the LCD using


lcdout $FE,$C0,#tempb," ",#tempc


I'm getting 19 for tempb (DTH sensor) and 205 (18B20 sensor) for tempc. I'm guessing that the DS18B20 is more accurate given the two sensors are side by side. I'm guessing if I multiply tempb by ten this should then in theory work - which it did.

I'll play about with the code, but at least I've now got something to work with