Last active
July 9, 2023 11:27
-
-
Save TG9541/f7f84c1e3cf8f9e7d9179a380e09fad4 to your computer and use it in GitHub Desktop.
Solar water heater control with transmission of collector and storage temperature via nRF24L01+
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\ W1209 temperature measurement with filter and noise suppression | |
\ © 2017 TG9541, refer to https://github.com/TG9541/W1209/blob/master/LICENSE | |
\ Note: W1209 thermostats may require adjustment, | |
\ especially when used outside the range of -5C to +20C | |
\ Refer to https://github.com/TG9541/W1209/wiki/W1209-Sensor | |
RAM | |
900 CONSTANT USENSMAX \ max temperature | |
NVM | |
#require @inter | |
$8000 CONSTANT DEFAULT \ Default value indicator (-32768) | |
\ note: adjust to specific sensor here if accuracy is required! | |
\ note: @inter accepts 2..n value pairs | |
\ interpolation table ADC digits * 32 -> temperature * 10 | |
CREATE dig2tem 14 , \ number of value pairs | |
1216 , 1100 , \ 1 1216 -> 11.0 C | |
1534 , 1000 , \ 2 | |
1982 , 900 , \ 3 | |
2560 , 800 , \ 4 | |
3296 , 700 , \ 5 | |
4320 , 600 , \ 6 | |
5634 , 500 , \ 7 | |
7370 , 400 , \ 8 | |
9632 , 300 , \ 9 | |
12382 , 200 , \ 10 | |
15522 , 100 , \ 11 | |
19012 , 0 , \ 12 | |
20768 , -50 , \ 13 | |
22466 , -100 , \ 14 22466 digits -> -10.0 C | |
: lpf32 ( n1 alpf -- n2 ) | |
\ low pass filter, multiplies n1 by 32, uses a as LPF memory | |
( alpf ) DUP >R @ DUP 32 / - + DUP R> ! | |
; | |
: unchatter ( n1 ahy -- n2 ) | |
\ remove chatter (+/- 0.5 digit window), divides n1 by 32 | |
\ ahy: hysteresis storage call address | |
SWAP 16 / OVER @ OVER SWAP - ABS 2- | |
0< IF DROP @ ELSE DUP ROT ! THEN | |
2/ | |
; | |
: ntctheta ( adc astr -- theta ) | |
\ filter noisy W1209 sensor input, result in astr @ | |
SWAP DUP USENSMAX < IF | |
OVER 2+ lpf32 \ low pass filter, "noise to digits", 32*ADC | |
dig2tem @inter \ interpolation: lpf32 digits to temperature | |
OVER 2+ 2+ lpf32 \ low pass filter, "smoothen", 32*theta | |
OVER 2+ 2+ 2+ unchatter \ remove chatter, divides by 32 | |
ELSE | |
DROP DEFAULT \ sensor error - default | |
THEN | |
SWAP ! \ store theta in astr | |
; | |
RAM |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\ changed $HW to erase buffer with spaces so that the count and | |
\ retries saved into mybuff typed correctly at RX end | |
#require ntctheta | |
\res MCU: STM8S103 | |
\res export BIT0 BIT1 BIT2 BIT3 BIT4 BIT5 BIT6 BIT7 | |
\res export INT_EXTI2 | |
\res export PA_ODR PA_DDR PA_CR1 | |
\res export PC_DDR PC_CR1 | |
\res MCU: nRF24L01 | |
\res export R.SETUP_RETR R.STATUS R.OBSERVE_TX R.DYNPD R.FEATURE | |
1 CONSTANT _COL \ PA1 low side KTY10 solar collector | |
2 CONSTANT _STO \ PA2 low side KTY10 storage tank | |
3 CONSTANT _REL \ PA3 Relais | |
4 CONSTANT _AIN \ PC4/AIN2 ref=2000R to VCC | |
800 CONSTANT MAXTEMP \ safe storage temp | |
\ helper word: create BSET/BRES instructions | |
#require ]B! | |
#require BL | |
NVM | |
VARIABLE VALCOL 6 ALLOT \ collector temperature struct | |
\ theta \ temperature [0.1°C] | |
\ adc.lpf \ LPF memory for ADC value | |
\ tem.lpf \ LPF memory for temperature | |
\ hyst.lpf \ unchatter memory | |
VARIABLE VALSTO 6 ALLOT \ storage temperature struct | |
\ theta \ temperature [0.1°C] | |
\ adc.lpf \ LPF memory for ADC value | |
\ tem.lpf \ LPF memory for temperature | |
\ hyst.lpf \ unchatter memory | |
VARIABLE CSDIFF \ collector-storage diff (>0: col is warmer) | |
VARIABLE PUMPON \ current pump activation state | |
VARIABLE THRESH \ threshold value | |
VARIABLE HYSTER \ hysteresis value | |
VARIABLE LIMIT \ temperature Limit (with hysteresis) | |
: REL.on ( -- ) | |
[ 1 PA_ODR _REL ]B! | |
; | |
: GetAin ( -- n ) | |
2 ADC! ADC@ | |
; | |
: REL.off ( -- ) | |
[ 0 PA_ODR _REL ]B! | |
; | |
: SetCol ( -- ) | |
[ 1 PA_DDR _COL ]B! [ 0 PA_DDR _STO ]B! | |
; | |
: SetSto ( -- ) | |
[ 0 PA_DDR _COL ]B! [ 1 PA_DDR _STO ]B! | |
; | |
: measure ( -- ) \ background task | |
\ get collector an storage in odd and even cycles | |
GetAin ( ain ) TIM 1 AND 0= IF | |
SetSto \ switch to storage sensor | |
VALCOL ntctheta | |
ELSE | |
SetCol \ switch to collector sensor | |
VALSTO ntctheta | |
THEN | |
VALCOL @ VALSTO @ - CSDIFF ! | |
; | |
: $HW ( -- ) | |
mybuff P0_width $20 FILL \ erase buffer | |
$" Solar" COUNT ( --- a1 n1 ) | |
mybuff SWAP CMOVE | |
; | |
: @retries ( --- n1 ) | |
R.OBSERVE_TX nRF@1 \ get count of lost packets:retries | |
$0F AND \ mask off lost packets | |
; | |
: n>str ( n1 --- a1 n2 ) <# BL HOLD #S #> ; | |
: n>buff ( a1 n1 --- ) \ store n1 as a string at offset a1 in mybuff | |
n>str \ a1 a2 n2 | |
ROT mybuff + | |
SWAP | |
CMOVE | |
; | |
VARIABLE STATUS \ nRF24 STATUS from IRQ | |
\ interrupt service routine | |
HERE ] \ headerless code (keep xt on stack) | |
SAVEC | |
\ R@STATUS ( s ) | |
R.STATUS nRF@1 ( s ) | |
DUP STATUS @ OR STATUS ! ( s ) | |
DUP BIT4 AND IF | |
FlushTx \ FlushRx | |
THEN | |
\ DUP BIT5 AND IF | |
\ DEB.On \ Tx data sent | |
\ THEN | |
( s ) BIT6 AND IF | |
LED.On \ Rx data ready | |
\ RXBUFF 4 R_RX_PAYLOAD nrf>b DROP | |
RXBUFF 4 rx>b DROP | |
THEN | |
$70 R.STATUS nRF!1 | |
IRET | |
[ OVERT ( xt ) INT_EXTI2 ! \ set Port C int vector | |
: PAYLOAD.TX ( -- ) \ send n bytes as set by P0_width | |
mybuff P0_WIDTH b>tx DROP | |
_CE.HD _CE.LOW \ 10us minimum, using 130uS | |
; | |
HERE $05A3 , $B4C5 , $D6E7 , ( myP0Addr ) | |
: TxInit ( -- ) \ setup as primary transmitter | |
nRF24Init \ general setup | |
IrqInit \ enable nRF24 IRQ interrupt of PC.4 | |
\ set pipe0 RX/TX to same address | |
( myP0Addr ) LITERAL COUNT b>p0addr | |
\ configure ACK payload mode | |
( DPL_P0 ) $01 R.DYNPD nRF!1 | |
( EN_ACK_PAY ) $02 R.FEATURE nRF!1 \ see footnote ^d | |
4 SetPL_width \ make it work for SI24R1 | |
\ set TX power to 0dBm output, modulation to 250kbps | |
[ $0 BIT5 >HIGH BIT2 >HIGH BIT1 >HIGH ] LITERAL RfSetup | |
\ ARD: retransmit delay, ARC: retries | |
[ ( ARD: 250us* ) $80 ( ARC: n* ) 3 + ] LITERAL R.SETUP_RETR nRF!1 | |
$70 SetRF_CH | |
>Standby1 | |
Set.TX \ enter TX mode | |
." Device reset" cr | |
; | |
: sol.init ( -- ) | |
[ 0 PA_ODR _COL ]B! \ open drain output | |
[ 1 PA_DDR _COL ]B! | |
[ 0 PA_CR1 _COL ]B! | |
[ 0 PA_ODR _STO ]B! \ open drain output | |
[ 1 PA_DDR _STO ]B! | |
[ 0 PA_CR1 _STO ]B! | |
[ 1 PA_DDR _REL ]B! \ PA RELais output | |
[ 1 PA_CR1 _REL ]B! \ push-pull output | |
[ 0 PC_DDR _AIN ]B! \ PC4 AIN2 as input | |
[ 0 PC_CR1 _AIN ]B! \ no pull-up | |
0 PUMPON ! | |
50 THRESH ! | |
30 HYSTER ! | |
MAXTEMP LIMIT ! | |
[ ' measure ] LITERAL BG ! | |
; | |
: sol.act | |
VALCOL ? | |
VALSTO ? | |
CSDIFF @ DUP . CR | |
( cs-diff ) THRESH @ - \ apply activation threshold | |
( diff ) PUMPON @ 0= IF | |
HYSTER @ - THEN \ apply hysteresis if pump is off | |
MAXTEMP VALSTO @ < IF | |
( diff ) DROP -1 THEN \ limit storage temperature | |
( limdiff ) 0< IF | |
REL.off 0 PUMPON ! | |
ELSE | |
REL.on 1 PUMPON ! THEN | |
; | |
: Solar ( -- ) | |
TxInit | |
0 STATUS ! | |
sol.init | |
$HW BEGIN | |
8 VALCOL @ n>buff 16 VALSTO @ n>buff | |
PAYLOAD.TX | |
@retries DUP 27 SWAP n>buff DROP | |
100 ms | |
STATUS @ 0 STATUS ! | |
DUP BIT4 AND IF ." Max retries cleared" CR THEN | |
\ DUP BIT5 AND IF ." Tx data sent" CR THEN | |
DUP BIT6 AND IF ." Rx data ready" CR THEN | |
( s ) . SPACE RXBUFF 4 TYPE SPACE | |
LED.Off \ DEB.Off | |
sol.act | |
900 ms | |
?RX \ input on serial line | |
IF 32 = ELSE 0 THEN \ only if it is a space char do we exit | |
UNTIL | |
DROP | |
; | |
' Solar 'Boot ! | |
RAM |
Here is a simplified version without NRF24 using Chinese W1209 style NTC sensors sensors and ntctheta:
\ 20230709 simplified version for NTC w/o NRF24
#require VALUE
#require ntctheta
\res MCU: STM8S103
\res export PA_ODR PA_DDR PA_CR1
\res export PC_DDR PC_CR1
1 CONSTANT _COL \ PA1 low side W1209 NTC sensor solar collector
2 CONSTANT _STO \ PA2 low side W1209 NTC sensor storage tank
3 CONSTANT _REL \ PA3 Relais
4 CONSTANT _AIN \ PC4/AIN2 pull-up to VCC with Rref=20K
\ helper word: create BSET/BRES instructions
#require ]B!
NVM
\ ntctheta data structures for temperature measurement with LPF from W1209
VARIABLE VALCOL 6 ALLOT \ collector temperature struct
\ theta \ temperature [0.1°C]
\ adc.lpf \ LPF memory for ADC value
\ tem.lpf \ LPF memory for temperature
\ hyst.lpf \ unchatter memory
VARIABLE VALSTO 6 ALLOT \ storage temperature struct
\ theta \ temperature [0.1°C]
\ adc.lpf \ LPF memory for ADC value
\ tem.lpf \ LPF memory for temperature
\ hyst.lpf \ unchatter memory
\ Standard-Forth-like "changeable constants" in NVM
\ change with, e.g., NVM 20 TO HYSTER RAM
10 VALUE THRESH \ threshold value [0.1 deg C]
30 VALUE HYSTER \ hysteresis value
800 VALUE MAXTEMP \ temperature Limit (with hysteresis)
VARIABLE CSDIFF \ collector-storage diff (>0: collector is warmer)
VARIABLE PUMPON \ current pump activation state
: REL.on ( -- )
[ 1 PA_ODR _REL ]B!
;
: GetAin ( -- n )
2 ADC! ADC@
;
: REL.off ( -- )
[ 0 PA_ODR _REL ]B!
;
: SetCol ( -- )
[ 1 PA_DDR _COL ]B! [ 0 PA_DDR _STO ]B!
;
: SetSto ( -- )
[ 0 PA_DDR _COL ]B! [ 1 PA_DDR _STO ]B!
;
: measure ( -- ) \ background task
\ get collector an storage in odd and even cycles
GetAin ( ain ) TIM 1 AND 0= IF
SetSto \ switch to storage sensor
VALCOL ntctheta
ELSE
SetCol \ switch to collector sensor
VALSTO ntctheta
THEN
VALCOL @ VALSTO @ - CSDIFF !
;
: sol.init ( -- )
[ 0 PA_ODR _COL ]B! \ open drain output
[ 1 PA_DDR _COL ]B!
[ 0 PA_CR1 _COL ]B!
[ 0 PA_ODR _STO ]B! \ open drain output
[ 1 PA_DDR _STO ]B!
[ 0 PA_CR1 _STO ]B!
[ 1 PA_DDR _REL ]B! \ PA RELais output
[ 1 PA_CR1 _REL ]B! \ push-pull output
[ 0 PC_DDR _AIN ]B! \ PC4 AIN2 as input
[ 0 PC_CR1 _AIN ]B! \ no pull-up
0 PUMPON !
[ ' measure ] LITERAL BG !
;
: sol.act
PUMPON ?
VALCOL ?
VALSTO ?
CSDIFF @ DUP . CR
( cs-diff ) THRESH - \ apply activation threshold
( diff ) PUMPON @ 0= IF
HYSTER - THEN \ apply hysteresis if pump is off
MAXTEMP VALSTO @ < IF
( diff ) DROP -1 THEN \ limit storage temperature
( limdiff ) 0< IF
REL.off 0 PUMPON !
ELSE
REL.on 1 PUMPON ! THEN
;
: Solar ( -- )
hi
." PunpOn Collector Storage Difference" CR
sol.init
BEGIN
\ 640 ms
TIM $7F AND 0= IF sol.act THEN
?RX \ input on serial line
IF 32 = ELSE 0 THEN \ exit on console blank/space
UNTIL
DROP
;
' Solar 'Boot !
RAM
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is in extension of solar.fs using stm8ef-nRF24L01 (using additional code from PingInt). PongInt works as a simple receiver. Note that in order to free up an analog input PC3 instead of PC4 was used for the nRF24L01+ interrupt.
There is a project on Hack-A-Day to track the progress.