From 1267023131b648bfdcf372d15ab5bef0d13d7be1 Mon Sep 17 00:00:00 2001
From: Aleksander Mazur <19613255+OlekMazur@users.noreply.github.com>
Date: Wed, 13 Jul 2022 13:18:13 +0200
Subject: [PATCH] Initial commit, part 2 :)
---
1wire.asm | 162 +++++++++
1wire_HL.asm | 262 ++++++++++++++
1wire_ds2405.asm | 35 ++
1wire_ds2406.asm | 124 +++++++
1wire_temp.asm | 176 ++++++++++
LICENSE | 674 ------------------------------------
LICENSE.md | 675 ++++++++++++++++++++++++++++++++++++
Makefile | 27 ++
README.md | 683 +++++++++++++++++++++++++++++++++++-
api.sh | 37 ++
control.asm | 779 +++++++++++++++++++++++++++++++++++++++++
crc8.asm | 45 +++
firmware_5V.asm | 52 +++
firmware_hw5.asm | 51 +++
firmware_hw6.asm | 51 +++
firmware_nomem.asm | 46 +++
firmware_prod4k.asm | 52 +++
font.py | 88 +++++
header.asm | 19 +
i2c.asm | 102 ++++++
i2c_display.asm | 199 +++++++++++
i2c_eeprom.asm | 70 ++++
i2c_tmp75.asm | 80 +++++
img/LED_module.jpg | Bin 0 -> 34530 bytes
img/PCB2_A.jpg | Bin 0 -> 96530 bytes
img/PCB3_A.jpg | Bin 0 -> 130662 bytes
img/PCB4_A.jpg | Bin 0 -> 149221 bytes
img/PCB5_A.jpg | Bin 0 -> 146554 bytes
img/PCB_A.jpg | Bin 0 -> 77192 bytes
img/PCB_R.jpg | Bin 0 -> 91293 bytes
img/relays.jpg | Bin 0 -> 90696 bytes
input.asm | 360 +++++++++++++++++++
main.asm | 819 ++++++++++++++++++++++++++++++++++++++++++++
output.asm | 215 ++++++++++++
rom_data.asm | 52 +++
timer.asm | 95 +++++
36 files changed, 5354 insertions(+), 676 deletions(-)
create mode 100644 1wire.asm
create mode 100644 1wire_HL.asm
create mode 100644 1wire_ds2405.asm
create mode 100644 1wire_ds2406.asm
create mode 100644 1wire_temp.asm
delete mode 100644 LICENSE
create mode 100644 LICENSE.md
create mode 100644 Makefile
create mode 100755 api.sh
create mode 100644 control.asm
create mode 100644 crc8.asm
create mode 100644 firmware_5V.asm
create mode 100644 firmware_hw5.asm
create mode 100644 firmware_hw6.asm
create mode 100644 firmware_nomem.asm
create mode 100644 firmware_prod4k.asm
create mode 100755 font.py
create mode 100644 header.asm
create mode 100644 i2c.asm
create mode 100644 i2c_display.asm
create mode 100644 i2c_eeprom.asm
create mode 100644 i2c_tmp75.asm
create mode 100644 img/LED_module.jpg
create mode 100644 img/PCB2_A.jpg
create mode 100644 img/PCB3_A.jpg
create mode 100644 img/PCB4_A.jpg
create mode 100644 img/PCB5_A.jpg
create mode 100644 img/PCB_A.jpg
create mode 100644 img/PCB_R.jpg
create mode 100644 img/relays.jpg
create mode 100644 input.asm
create mode 100644 main.asm
create mode 100644 output.asm
create mode 100644 rom_data.asm
create mode 100644 timer.asm
diff --git a/1wire.asm b/1wire.asm
new file mode 100644
index 0000000..7f1fc11
--- /dev/null
+++ b/1wire.asm
@@ -0,0 +1,162 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018, 2021 Aleksander Mazur
+;
+; Procedury niszczą A, C, R6, R7
+
+;-----------------------------------------------------------
+; opóźnienie
+load_delay macro delay
+ifdef TUNE_1WIRE
+ mov R7, delay ; 24 cykle
+else
+ mov R7, #delay&_def ; 12 cykli
+endif
+endm
+
+;-----------------------------------------------------------
+; RESET - zwraca C=1 jeśli wystąpił błąd (nie ma żadnego czujnika)
+; niszczy C, R7
+ow_reset:
+ clr EA
+ clr OW_DQ ; reset pulse min. 480 µs
+ mov R7, #221 ; 12 cykli; łącznie impuls potrwa 480,69 µs
+ setb C ; 12 cykli
+ow_reset_pulse:
+ nop ; 12 cykli
+ nop ; 12 cykli
+ ;inc DPTR ; 24 cykle, a tylko 1 bajt
+ djnz R7, ow_reset_pulse ; 24*221=5304 cykli
+ setb OW_DQ ; 12 cykli; DS18B20 waits 15-60 µs
+ mov R7, #14 ; 12 cykli; łącznie poczekamy 15,73 µs
+ djnz R7, $ ; 24*14=336 cykli
+ load_delay ow_tRST
+ow_reset_check_present:
+ jnb OW_DQ, ow_reset_presence_pulse ; 24 cykle
+ djnz R7, ow_reset_check_present ; 24 cykle
+ sjmp ow_reset_return
+ow_reset_presence_pulse:
+ mov R7, #22 ; presence pulse musi trwać jeszcze co najmniej 47,74 µs
+ow_reset_check_present2:
+ jb OW_DQ, ow_reset_return ; 24 cykle
+ djnz R7, ow_reset_check_present2 ; 24 cykle
+ mov R7, #112 ; presence pulse może trwać jeszcze co najwyżej 243 µs
+ow_reset_check_present3:
+ jb OW_DQ, ow_reset_presence_finished ; 24 cykle
+ djnz R7, ow_reset_check_present3 ; 24 cykle
+ sjmp ow_reset_return ; błąd - za długi presence pulse
+ow_reset_presence_finished:
+ ; poczekajmy jeszcze co najmniej tyle, ile w najgorszym przypadku
+ ; musi pozostać czasu do zakończenia resetu, czyli > 480-15-60=405 µs
+ mov R7, #0 ; 416,7 µs
+ow_reset_sustain:
+ nop
+ djnz R7, ow_reset_sustain
+ clr C
+ow_reset_return:
+ setb EA
+ ret
+
+;-----------------------------------------------------------
+; początek cyklu zapisu/odczytu bitu na 1-wire
+; 96+24*ow_tLOW cykli, min. 5,4 µs
+; C = bit do wystawienia po impulsie 0
+; długość impulsu 0 = (ow_tLOW+1)*24 cykle
+; ow_tLOW > 0
+; ow_tLOW=1 -> 2,17 µs
+; ow_tLOW=12 -> 14,1 µs
+; niszczy R7
+ow_start_cycle:
+ load_delay ow_tLOW
+ clr EA ; 12 cykli
+ clr OW_DQ ; 12 cykli; start write time slot
+ djnz R7, $ ; ow_tLOW*24 cykle
+ mov OW_DQ, C ; 24 cykle
+ ret ; 24 cykle
+
+;-----------------------------------------------------------
+; wysłanie bitu z C na 1-wire
+; 192+24*(ow_tLOW+ow_tWR) cykli
+; niszczy R7
+ow_write_bit:
+ acall ow_start_cycle ; 24+96+24*ow_tLOW cykli
+ ; slave sampluje linię między 15 µs a 60 µs od początku slotu
+ ; cały slot trwa co najmniej 60 µs, max. 120 µs jeśli wysyłamy 0
+ load_delay ow_tWR
+ djnz R7, $ ; ow_tWR*24 cykle
+ setb OW_DQ ; 12 cykli; end write time slot
+ ; od clr OW_DQ w ow_start_cycle do teraz minęło (czyli cykl 1-wire trwał)
+ ; 84 + 24 * (ow_tLOW + ow_tWR) cykli
+ ; zatem dla przepisowych >60 µs -> ow_tLOW + ow_tWR = 52
+ setb EA ; 12 cykli
+ ret ; 24 cykle
+
+;-----------------------------------------------------------
+; odczyt bitu z 1-wire do C
+; 228+24*(ow_tLOW+ow_tDSO+ow_tRD) cykli
+; niszczy C, R7
+ow_read_bit:
+ setb C ; 12 cykli
+ acall ow_start_cycle ; 24+96+24*ow_tLOW cykli
+ load_delay ow_tDSO
+ djnz R7, $ ; ow_tDSO*24 cykle
+ ; master sampluje linię tuż przed upływem 15 µs od rozpoczęcia slotu
+ mov C, OW_DQ ; 12 cykli
+ ; od clr OW_DQ w ow_start_cycle do teraz minęło
+ ; 84 + 24 * (ow_tLOW + ow_tDSO) cykli
+ ; zatem dla przepisowych <15 µs -> ow_tLOW + ow_tDSO = 10
+ load_delay ow_tRD
+ djnz R7, $ ; ow_tRD*24 cykle
+ ; od clr OW_DQ w ow_start_cycle do teraz minęło (czyli cykl 1-wire trwał)
+ ; 108 + 24 * (ow_tLOW + ow_tDSO + ow_tRD) cykli
+ ; zatem dla przepisowych >60 µs -> ow_tLOW + ow_tDSO + ow_tRD = 51
+ setb EA ; 12 cykli
+ ret ; 24 cykle
+
+;-----------------------------------------------------------
+; odczyt bajtu z 1-wire do akumulatora
+; niszczy A, C, R6, R7
+ow_read:
+ mov R6, #8 ; 12 cykli
+ow_read_loop:
+ acall ow_read_bit ; 24 cykle + 2712 cykli
+ rrc A ; 12 cykli
+ djnz R6, ow_read_loop ; 24 cykle
+ ret ; 24 cykle
+
+;-----------------------------------------------------------
+; wysłanie bajtu z akumulatora na 1-wire
+; niszczy A, C, R6, R7
+ow_write:
+ mov R6, #8 ; 12 cykli
+ow_write_loop:
+ rrc A ; 12 cykli; bit do wysłania wysunięty do C
+ acall ow_write_bit ; 24 cykle + 2760 cykli
+ djnz R6, ow_write_loop ; 24 cykle
+ ret ; 24 cykle
+
+;-----------------------------------------------------------
+; nakładka na ow_write
+; wypisuje B bajtów spod DPTR
+; niszczy A, B, C, R6, R7, DPTR
+ow_write_bytes_next:
+ inc DPTR
+ow_write_bytes:
+ clr A
+ movc A, @A + DPTR
+ acall ow_write
+ djnz B, ow_write_bytes_next
+ ret
diff --git a/1wire_HL.asm b/1wire_HL.asm
new file mode 100644
index 0000000..a2923a3
--- /dev/null
+++ b/1wire_HL.asm
@@ -0,0 +1,262 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018, 2022 Aleksander Mazur
+;
+; Obsługa czujników na magistrali 1-wire
+; Bazuje na niskopoziomowych procedurach z 1wire.asm i i2c.asm
+
+;===========================================================
+; Stałe
+
+; komendy 1-wire
+ds_search_rom equ 0F0h
+ds_skip_rom equ 0CCh
+ds_match_rom equ 055h
+
+;===========================================================
+; Procedury
+
+ifdef TUNE_1WIRE
+; Opóźnia działanie o ow_SEARCH_DELAY
+; Na ten czas włącza silną jedynkę na magistrali 1-wire
+; Niszczy C, R7
+owhl_search_delay:
+ load_delay ow_SEARCH_DELAY
+ cjne R7, #0, owhl_search_delay_nz
+ ret
+owhl_search_delay_nz:
+ifdef OW_PARASITE
+ clr OW_PWR
+endif
+ djnz R7, $
+ifdef OW_PARASITE
+ setb OW_PWR
+endif
+ ret
+endif
+
+;-----------------------------------------------------------
+; Znajduje pierwszy albo następny w kolejności czujnik na magistrali.
+; Na wejściu magistrala powinna znajdować się w stanie tuż po resecie!
+; Na wyjściu, znaleziony czujnik jest wybrany na magistrali (po SEARCH ROM)
+; i można (a nawet trzeba) kontynuować komunikację z nim.
+; Wejście:
+; global_ow_diffpos - 0 przy enumeracji pierwszego czujnika,
+; albo pozycja bitu (1-64) z dwoma możliwościami przy kontynuacji.
+; global_ow_id - tablica 8 bajtów z ID ostatnio znalezionego czujnika.
+; Zawartość nieistotna przy szukaniu pierwszego czujnika.
+; Przy szukaniu kolejnego czujnika musi tu pozostać ID czujnika
+; znalezionego ostatnio.
+; Niszczy: A, B, C, R1, R2(CRC), R4, R5, R6, R7
+; Zwraca:
+; C=0 jeśli sukces. ID znalezionego czujnika jest w global_ow_id.
+; Jeśli global_ow_diffpos <> 0, to ID do kupy z global_ow_diffpos
+; pozwala znaleźć następny czujnik przy kolejnym zawołaniu.
+; Wołający musi zapewnić nietykalność tym zmiennym do tego czasu.
+; Jeśli global_ow_diffpos = 0, to to już jest ostatni czujnik,
+; a kolejne zawołanie będzie szukać od początku.
+; C=1 jeśli błąd. W R4 (local_bit) jest zwracany kod błędu,
+; a konkretnie liczba bitów ID, które udało się poprawnie wczytać,
+; od 0 do 64. Wartość 64 oznacza, że wystąpił błąd CRC8.
+local_ptr equ R1 ; wskaźnik do miejsca na aktualnie składany bajt w ID
+local_bit equ R4 ; numer pozycji bieżącego bitu
+local_nextpos equ R5 ; wartość global_ow_diffpos dla następnego wywołania
+local_byte equ R6 ; składany bajt, który trafi do @local_ptr
+
+owhl_enum_next:
+ mov A, #ds_search_rom
+ acall ow_write
+ mov local_ptr, #global_ow_id
+ clr A
+ mov CRC, A
+ mov local_bit, A
+ mov local_nextpos, A
+ ;mov local_byte, A ; nie trzeba inicjować, bo i tak wpychamy tam wszystkie 8 bitów, zanim umieszczamy w @local_ptr
+owhl_enum_next_bit:
+ inc local_bit
+ clr A
+ifdef TUNE_1WIRE
+ acall owhl_search_delay
+endif
+ acall ow_read_bit ; czytamy bit niezanegowany
+ rlc A
+ifdef TUNE_1WIRE
+ acall owhl_search_delay
+endif
+ acall ow_read_bit ; czytamy bit zanegowany
+ rlc A
+ ; tu C=0, bo z wyzerowanego akumulatora wyjeżdżają zera z lewej strony
+ifdef TUNE_1WIRE
+ acall owhl_search_delay
+ clr C ; przywracamy C=0, bo owhl_search_delay mogło je zniszczyć
+endif
+ ; w A mamy jedną z 4 możliwości:
+ ; 00 - oba zera - trzeba będzie wybrać 0 albo 1
+ ; 01 - zero
+ ; 10 - jedynka
+ ; 11 - brak czujników o ID takim, jak znaleziony do tej pory (błąd)
+ jz owhl_enum_select_bit ; oba zera - trzeba wybrać
+ dec A
+ ; teraz w A:
+ ; 00 - zero
+ ; 01 - jedynka
+ ; 10 - brak czujników (błąd)
+ ; wciąż C=0
+ rrc A
+ jz owhl_enum_selected_bit ; bit jest w C
+ ; były dwie jedynki - błąd
+ ; cofamy licznik, bo nie udało się wczytać tego bitu
+ dec local_bit
+owhl_enum_next_error:
+ mov A, local_byte
+ mov @local_ptr, A
+owhl_setC_ret:
+ setb C
+ ret
+owhl_enum_select_bit:
+ ; procedura szukania dostała oba bity wyzerowane, czyli są na
+ ; magistrali czujniki mające na tej pozycji numeru seryjnego
+ ; zarówno 0, jak i 1, i musimy sobie wybrać, w którą gałąź teraz idziemy
+ mov A, local_bit
+ cjne A, global_ow_diffpos, owhl_enum_select_bit_not_at_diffpos
+ ; wybieramy 1, jeśli local_bit == global_ow_diffpos
+owhl_enum_select_1:
+ setb C
+ sjmp owhl_enum_selected_bit
+owhl_enum_select_bit_not_at_diffpos:
+ ; wybieramy 0, jeśli local_bit > global_ow_diffpos
+ jnc owhl_enum_select_bit_zero
+ ; index < pos -> wybieramy taki sam bit, jak ostatnio na tej pozycji
+ ; local_ptr wskazuje na bajt zawierający m.in. bit bieżącej pozycji - musimy wydobyć z niego właściwy bit
+ mov A, local_bit ; indeks bitu liczony od 1
+ dec A
+ anl A, #00000111b ; numer bitu liczony od 0 do 7 w ramach bieżącego bajtu
+ mov B, A
+ acall control_get_mask_1bit
+ ; w A mamy maskę na interesujący nas bit
+ anl A, @local_ptr
+ jnz owhl_enum_select_1
+ clr C
+owhl_enum_select_bit_zero:
+ ; zapamiętujemy ostatnie miejsce, gdzie wybraliśmy 0, żeby następnym razem wybrać tam 1
+ mov A, local_bit
+ mov local_nextpos, A
+owhl_enum_selected_bit:
+ ; znaleźliśmy (lub wybraliśmy) kolejny bit numeru seryjnego jakiegoś czujnika
+ ; znaleziony bit jest w C
+ acall ow_write_bit
+ ; wybrany bit jest częścią numeru właśnie wybieranego czujnika
+ mov A, local_byte
+ rrc A
+ mov local_byte, A
+ ; czy mamy już cały bajt?
+ mov A, local_bit
+ anl A, #00000111b
+ jnz owhl_enum_next_bit
+ ; mamy cały bajt
+ mov A, local_byte
+ mov @local_ptr, A
+ inc local_ptr
+ acall do_CRC8
+ cjne local_bit, #64, owhl_enum_next_bit
+ ; znaleźliśmy cały numer seryjny jakiegoś czujnika
+ ; czujnik ten jest teraz wybrany na magistrali - można z nim gadać przez 1-wire
+ ; sprawdźmy CRC ROM-code
+ mov A, CRC
+ jnz owhl_enum_next_error ; błąd CRC; nie cofamy licznika, bo bit #64 udało się wczytać; dzięki temu stan licznika bitów określa jednoznacznie, czy był błąd CRC
+ ; sukces
+ mov global_ow_diffpos, local_nextpos
+ clr C
+ ret
+
+;-----------------------------------------------------------
+; Wybiera czujnik o ID składającym się z:
+; - family code z global_ow_id[0]
+; - kolejnych 6 bajtach ID z EEPROM (i2c_shin, ACK, i2c_shin, ACK, i2c_shin, ACK, i2c_shin, ACK, i2c_shin, ACK, i2c_shin, i2c_NAK, i2c_stop)
+; - CRC8 wyliczonym dla w/w danych
+; Jeśli flag_overwrite_ow_id jest ustawiona, ID czujnika (7 bajtów poza pierwszym,
+; który musi być zadany przed wywołaniem) jest dodatkowo umieszczane w global_ow_id.
+; Niszczy A, B, C, R1, CRC, R6, R7.
+; Zwraca C=0, jeśli sukces, a C=1, jeśli wystąpił błąd.
+; W każdym przypadku funkcja kończy odczyt z EEPROM (eeprom_read_stop).
+owhl_match_rom_from_eeprom:
+ acall ow_reset
+ jc eeprom_read_stop ; magistrala 1-wire nie funguje
+ mov A, #ds_match_rom
+ acall ow_write
+ ; wysyłamy family code i rozpoczynamy liczenie CRC
+ mov R1, #global_ow_id
+ mov A, @R1
+ mov CRC, #0
+ acall do_CRC8
+ acall ow_write
+ mov B, #6
+ sjmp owhl_match_rom_from_eeprom_start
+owhl_match_rom_from_eeprom_ack:
+ acall i2c_ACK
+owhl_match_rom_from_eeprom_start:
+ ; wysyłamy kolejne bajty z EEPROM i uaktualniamy CRC
+ acall i2c_shin
+ifdef MATCH_ON_SEARCH_FAILURE
+ acall owhl_match_rom_maybe_overwrite
+endif ;MATCH_ON_SEARCH_FAILURE
+ acall do_CRC8
+ acall ow_write
+ djnz B, owhl_match_rom_from_eeprom_ack
+ acall eeprom_read_stop ; koniec odczytu z EEPROM
+ ; wysyłamy CRC
+ mov A, CRC
+ifdef MATCH_ON_SEARCH_FAILURE
+ acall owhl_match_rom_maybe_overwrite
+endif ;MATCH_ON_SEARCH_FAILURE
+ acall ow_write
+ clr C
+ ret
+
+ifdef MATCH_ON_SEARCH_FAILURE
+owhl_match_rom_maybe_overwrite:
+ jnb flag_overwrite_ow_id, owhl_dont_overwrite_ow_id
+ inc R1
+ mov @R1, A
+owhl_dont_overwrite_ow_id:
+ ret
+endif ;MATCH_ON_SEARCH_FAILURE
+
+;-----------------------------------------------------------
+; Wybiera czujnik o ID składającym się z:
+; - family code z global_ow_id[0]
+; - kolejnych 6 bajtach ID z EEPROM (i2c_shin, ACK, i2c_shin, ACK, i2c_shin, ACK, i2c_shin, ACK, i2c_shin, ACK, i2c_shin, i2c_NAK, i2c_stop)
+; - CRC8 wyliczonym dla w/w danych
+; a następnie odczytuje jego scratchpad do local_scratchpad2.
+; Jako, że family code bierzemy z pierwszego czujnika (global_ow_id),
+; musi to być para tego samego modelu.
+; Niszczy A, B, C, R1, CRC, R6, R7.
+; Zwraca C=0, jeśli sukces, a C=1, jeśli wystąpił błąd.
+; W każdym przypadku funkcja kończy odczyt z EEPROM (eeprom_read_stop).
+owhl_read_second_scratchpad:
+ifdef MATCH_ON_SEARCH_FAILURE
+ clr flag_overwrite_ow_id
+endif ;MATCH_ON_SEARCH_FAILURE
+ acall owhl_match_rom_from_eeprom
+ jc owhl_read_second_scratchpad_ret
+ ; w tym miejscu powinniśmy mieć wybrany czujnik na magistrali 1-wire
+ mov R1, #local_scratchpad2
+ acall owhl_read_scratchpad
+ ; konwersja statusu z A na C
+ jnz owhl_setC_ret
+ clr C
+owhl_read_second_scratchpad_ret:
+ ret
diff --git a/1wire_ds2405.asm b/1wire_ds2405.asm
new file mode 100644
index 0000000..28dd2d8
--- /dev/null
+++ b/1wire_ds2405.asm
@@ -0,0 +1,35 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2018, 2020, 2021 Aleksander Mazur
+;
+; Obsługa układów DS2405 na magistrali 1-wire
+; Bazuje na niskopoziomowych procedurach z 1wire.asm
+
+;-----------------------------------------------------------
+; Odczytuje stan układu DS2405 i wyrzuca go na port szeregowy.
+; Na magistrali musi być już wybrany czujnik - przez SEARCH ROM lub MATCH ROM,
+; przy czym SEARCH ROM nie zmienia stanu wyjścia (otwarty dren tranzystora z kanałem N),
+; a MATCH ROM przełącza na przeciwny niż był.
+; Procedura generuje kilka slotów odczytu 1-wire.
+; Niszczy A, B, C, R6, R7
+owhl_read_info_ds2405:
+ acall ow_read
+ ; same jedynki -> 1, w przeciwnym razie -> 0
+ add A, #1
+ ; jeśli były same jedynki, to dodanie 1 przekręca akumulator i ustawia bit przeniesienia (C)
+ clr A
+ addc A, #'0'
+ ajmp write_char
diff --git a/1wire_ds2406.asm b/1wire_ds2406.asm
new file mode 100644
index 0000000..4f501ac
--- /dev/null
+++ b/1wire_ds2406.asm
@@ -0,0 +1,124 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018, 2020 Aleksander Mazur
+;
+; Obsługa układów DS2406 na magistrali 1-wire
+; Bazuje na niskopoziomowych procedurach z 1wire.asm
+
+;===========================================================
+; Stałe
+
+; komendy 1-wire układów DS2406
+ds_channel_access equ 0F5h
+
+;===========================================================
+; Procedury
+
+;-----------------------------------------------------------
+; Kasuje lacie wszystkim układom DS2406
+; Niszczy A, B, C, R6, R7
+owhl_clear_latches_ds2406:
+ ; kasujemy lacie wszystkim układom DS2406
+ ; wysyłamy SKIP ROM - CHANNEL ACCESS
+ ; zakładamy, że reszta urządzeń na magistrali zignoruje komendę
+ acall ow_reset
+ jc owhl_ret
+ mov DPTR, #owhl_clear_latches_ds2406_seq
+ mov B, #owhl_clear_latches_ds2406_seq_end - owhl_clear_latches_ds2406_seq
+ ajmp ow_write_bytes
+ ;mov A, #ds_skip_rom
+ ;acall ow_write
+ ;mov A, #ds_channel_access
+ ;acall ow_write
+ ;mov A, #11000100b ; Channel Control Byte 1: Activity Latch Reset, read PIO-A
+ ;acall ow_write
+ ;mov A, #11111111b ; Channel Control Byte 2
+ ;ajmp ow_write
+owhl_clear_latches_ds2406_seq:
+ db ds_skip_rom
+ db ds_channel_access
+ db 11000100b ; Channel Control Byte 1: Activity Latch Reset, read PIO-A
+ db 11111111b ; Channel Control Byte 2
+owhl_clear_latches_ds2406_seq_end:
+
+;-----------------------------------------------------------
+; Odczytuje stan lacia układu DS2406 i wyrzuca go na port szeregowy.
+; Na magistrali musi być już wybrany czujnik (przez SKIP ROM lub SEARCH ROM lub MATCH ROM)
+; - procedura wysyła od razu rozkaz CHANNEL_ACCESS.
+; Niszczy A, B, C, R6, R7
+owhl_read_info_ds2406:
+ ; obsługa DS2406 jako wejście
+ mov DPTR, #owhl_read_info_ds2406_seq
+ mov B, #owhl_read_info_ds2406_seq_end - owhl_read_info_ds2406_seq
+ acall ow_write_bytes
+ ;mov A, #ds_channel_access
+ ;acall ow_write
+ ;mov A, #01000100b ; Channel Control Byte 1: read PIO-A
+ ;acall ow_write
+ ;mov A, #11111111b ; Channel Control Byte 2
+ ;acall ow_write
+ acall ow_read ; Channel Info Byte -> ACC
+ ; BIT 7 - Supply Indication (0 = no supply)
+ ; BIT 6 - Number of Channels (0 = channel A only)
+ ; BIT 5 - PIO-B Activity Latch
+ ; BIT 4 - PIO-A Activity Latch
+ ; BIT 3 - PIO-B Sensed Level
+ ; BIT 2 - PIO-A Sensed Level
+ ; BIT 1 - PIO-B Channel Flip-Flop Q
+ ; BIT 0 - PIO-A Channel Flip-Flop Q
+ rlc A ; teraz mamy bit7 w C
+ rlc A ; teraz mamy bit6 w C (flaga obecności PIO-B)
+ ; a interesujące nas bity 5,4,3,2 mamy w górnej połowie A (przesunięte w lewo o 2 bity)
+ swap A ; a teraz w dolnej połowie A
+ anl A, #00001111b
+ ; Teraz A zawiera stan układu:
+ ; bit 0 - stan portu PIO-A
+ ; bit 1 - stan portu PIO-B lub wartość nieokreślona, jeśli układ nie ma PIO-B
+ ; bit 2 - stan lacia PIO-A
+ ; bit 3 - stan lacia PIO-B lub wartość nieokreślona, jeśli układ nie ma PIO-B
+ ; pozostałe bity wyzerowane
+ ; C określa, czy port PIO-B istnieje.
+ ;-----------------------------------------------------------
+ ; Wyjście: A[*][,B[*]]
+ ; gdzie: A to stan PIO-A, B to stan PIO-B (0 albo 1)
+ ; gwiazdka oznacza, że lać był zatrzaśnięty
+ ; człon ,B[*] występuje gdy C=1
+ ; Niszczy A, B, C
+ jnc write_info_ds2406_pio ; jeśli nie ma PIO-B, to obsługujemy tylko PIO-A
+ push ACC
+ acall write_info_ds2406_pio
+ mov A, #',' ; oddzielamy przecinkiem stan PIO-A od PIO-B
+ acall write_char
+ pop ACC
+ ; przerabiamy status tak, żeby dane o PIO-B były w miejscach danych o PIO-A
+ rr A
+write_info_ds2406_pio:
+ mov B, A
+ ; wypisujemy info o PIO dostępną w miejscach przeznaczonych na PIO-A
+ ; liczą się tylko bity 0 i 2 z A
+ ; wypisujemy stan portu - bit 0
+ anl A, #1
+ add A, #'0'
+ acall write_char
+ ; jeśli lać był zatrzaśnięty, piszemy np. gwiazdkę
+ jnb B.2, owhl_ret
+ mov A, #'*'
+ ajmp write_char
+owhl_read_info_ds2406_seq:
+ db ds_channel_access
+ db 01000100b ; Channel Control Byte 1: read PIO-A
+ db 11111111b ; Channel Control Byte 2
+owhl_read_info_ds2406_seq_end:
diff --git a/1wire_temp.asm b/1wire_temp.asm
new file mode 100644
index 0000000..8930616
--- /dev/null
+++ b/1wire_temp.asm
@@ -0,0 +1,176 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018, 2021 Aleksander Mazur
+;
+; Obsługa czujników temperatury na magistrali 1-wire
+; Bazuje na niskopoziomowych procedurach z 1wire.asm
+
+;===========================================================
+; Stałe
+
+; komendy 1-wire czujników temperatury DS18B20, DS18S20, DS1820
+ds_convert_t equ 044h
+ds_read_scratchpad equ 0BEh
+ds_write_scratchpad equ 04Eh
+
+;===========================================================
+; Procedury
+
+;-----------------------------------------------------------
+; Zleca pomiar temperatury czujnikom zewnętrznym (1-wire)
+; niszczy A, B, C, R6, R7
+; Zwraca C=0, jeśli sukces; C=1, jeśli wystąpił błąd
+; Jeśli sukces, to:
+; jeśli OW_PARASITE, to linia 1-wire zostaje w stanie silnej jedynki;
+; wołający musi odliczyć OW_PARASITE przerwań zegarowych i wyłączyć silną jedynkę,
+; zanim zacznie używać magistrali
+; jeśli nie OW_PARASITE, to linia 1-wire zostaje z włączonym zasilaniem;
+; wołający musi wyłączyć zasilanie, jak skończy używać magistrali
+owhl_start_measuring:
+ ; wyłączamy silną jedynkę (gdyby tak była włączona) - bo będziemy gadać po 1-wire
+ ; albo włączamy zasilanie 1-wire
+ setb OW_PWR
+ ; wysyłamy SKIP ROM - WRITE SCRATCHPAD
+ acall ow_reset
+ jc start_measuring_1wire_ret
+ ;mov A, #ds_skip_rom
+ ;acall ow_write
+ ;mov A, #ds_write_scratchpad
+ ;acall ow_write
+ ;clr A
+ ;acall ow_write ; wysyłamy T_H
+ ;acall ow_write ; wysyłamy T_L
+ ;mov A, #01111111b ; configuration register
+ ; dociera do DS18B20; DS18S20 przyjmuje tylko 2 bajty;
+ ; zakładamy, że reszta urządzeń na magistrali zignoruje komendę
+ ;acall ow_write
+ mov DPTR, #owhl_temp_setup_seq
+ mov B, #owhl_temp_setup_seq_end - owhl_temp_setup_seq
+ acall ow_write_bytes
+ ; wysyłamy SKIP ROM - CONVERT T
+ acall ow_reset
+ jc start_measuring_1wire_ret
+ mov A, #ds_skip_rom
+ acall ow_write
+ mov A, #ds_convert_t
+ acall ow_write
+ ;mov DPTR, #owhl_temp_convert_seq
+ ;mov B, #owhl_temp_convert_seq_end - owhl_temp_convert_seq
+ ;acall ow_write_bytes
+ifdef OW_PARASITE
+ clr OW_PWR ; włączamy silną jedynkę na 1-wire
+endif
+ clr C
+start_measuring_1wire_ret:
+ ret
+owhl_temp_setup_seq:
+ db ds_skip_rom
+ db ds_write_scratchpad
+ db 0 ; T_H
+ db 0 ; T_L
+ db 01111111b ; configuration register
+owhl_temp_setup_seq_end:
+;owhl_temp_convert_seq:
+; db ds_skip_rom
+; db ds_convert_t
+;owhl_temp_convert_seq_end:
+
+;-----------------------------------------------------------
+; Odczytuje scratchpad z czujnika temperatury 1-wire.
+; Na magistrali musi być już wybrany czujnik (przez SKIP ROM lub SEARCH ROM lub MATCH ROM)
+; - procedura wysyła od razu rozkaz READ SCRATCHPAD.
+; Wejście: R1 - miejsce na scratchpad (o rozmiarze ds_scratchpad_size).
+; Procedura wypełnia podaną tablicę pod R1 i ustawia A (0=OK, nie-0=błąd). A, nie C.
+; Niszczy A, B, C, R1, CRC, R6, R7.
+owhl_read_scratchpad:
+ mov A, #ds_read_scratchpad
+ acall ow_write
+ mov CRC, #0
+ mov B, #ds_scratchpad_size
+owhl_read_scratchpad_byte:
+ acall ow_read
+ mov @R1, A
+ acall do_CRC8
+ inc R1
+ djnz B, owhl_read_scratchpad_byte
+ mov A, CRC
+ ret
+
+;-----------------------------------------------------------
+; Wyłuskuje temperaturę z odczytanego scratchpadu czujnika 1-wire.
+; Wejście: global_ow_id - family code, R1 - początek odczytanego scratchpadu.
+; Procedura ustawia local_temp_h:local_temp_l oraz C (0=OK, 1=błąd).
+; Niszczy A, B, C, R1.
+; Jest to czysto obliczeniowa procedura - nie robi żadnych operacji
+; na magistrali 1-wire.
+owhl_get_temperature_from_scratchpad:
+ mov A, global_ow_id
+ifndef SKIP_DS18S20
+ cjne A, #10h, owhl_get_temperature_not_ds18s20
+ ; DS18S20 lub DS1820 - T MSB|LSB ma 1 bit po przecinku, większa precyzja przy pomocy COUNT REMAIN
+ inc R1 ; T MSB
+ mov A, @R1
+ rrc A ; teraz mamy bit znaku w C
+ dec R1 ; T LSB
+ mov A, @R1
+ rrc A ; w A mamy część całkowitą temperatury
+ mov local_temp_h, A
+ clr A
+ rrc A
+ mov local_temp_l, A
+ mov A, R1
+ add A, #7 ; COUNT PER C
+ mov R1, A
+ mov A, @R1
+ cjne A, #10h, owhl_get_temperature_temp_success ; COUNT PER C miał być zahardkodowany jako 10h
+ ;mov A, #10h
+ dec R1 ; COUNT REMAIN
+ clr C
+ subb A, @R1
+ mov B, #16
+ mul AB ; zeruje C
+ subb A, #40h ; -0.25 stopnia C
+ mov local_temp_l, A
+ mov A, local_temp_h
+ subb A, #0
+ add A, B
+ sjmp owhl_get_temperature_temp_finish
+owhl_get_temperature_not_ds18s20:
+endif
+ cjne A, #28h, owhl_get_temperature_not_ds18b20
+ ; DS18B20 - T MSB|LSB ma 4 bity po przecinku
+ mov A, @R1 ; T LSB
+ anl A, #00001111b
+ swap A
+ mov local_temp_l, A
+ mov A, @R1 ; T LSB
+ swap A
+ anl A, #00001111b
+ mov B, A
+ inc R1
+ mov A, @R1 ; T MSB
+ swap A
+ anl A, #11110000b
+ orl A, B
+owhl_get_temperature_temp_finish:
+ mov local_temp_h, A ; dla DS18S20: l_temp_h + B - C
+owhl_get_temperature_temp_success:
+ clr C
+ ret
+owhl_get_temperature_not_ds18b20:
+ setb C
+owhl_ret:
+ ret
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index f288702..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..2fb2e74
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,675 @@
+### GNU GENERAL PUBLIC LICENSE
+
+Version 3, 29 June 2007
+
+Copyright (C) 2007 Free Software Foundation, Inc.
+
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+### Preamble
+
+The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom
+to share and change all versions of a program--to make sure it remains
+free software for all its users. We, the Free Software Foundation, use
+the GNU General Public License for most of our software; it applies
+also to any other work released this way by its authors. You can apply
+it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you
+have certain responsibilities if you distribute copies of the
+software, or if you modify it: responsibilities to respect the freedom
+of others.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the
+manufacturer can do so. This is fundamentally incompatible with the
+aim of protecting users' freedom to change the software. The
+systematic pattern of such abuse occurs in the area of products for
+individuals to use, which is precisely where it is most unacceptable.
+Therefore, we have designed this version of the GPL to prohibit the
+practice for those products. If such problems arise substantially in
+other domains, we stand ready to extend this provision to those
+domains in future versions of the GPL, as needed to protect the
+freedom of users.
+
+Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish
+to avoid the special danger that patents applied to a free program
+could make it effectively proprietary. To prevent this, the GPL
+assures that patents cannot be used to render the program non-free.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+### TERMS AND CONDITIONS
+
+#### 0. Definitions.
+
+"This License" refers to version 3 of the GNU General Public License.
+
+"Copyright" also means copyright-like laws that apply to other kinds
+of works, such as semiconductor masks.
+
+"The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of
+an exact copy. The resulting work is called a "modified version" of
+the earlier work or a work "based on" the earlier work.
+
+A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user
+through a computer network, with no transfer of a copy, is not
+conveying.
+
+An interactive user interface displays "Appropriate Legal Notices" to
+the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+#### 1. Source Code.
+
+The "source code" for a work means the preferred form of the work for
+making modifications to it. "Object code" means any non-source form of
+a work.
+
+A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+The Corresponding Source need not include anything that users can
+regenerate automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same
+work.
+
+#### 2. Basic Permissions.
+
+All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+You may make, run and propagate covered works that you do not convey,
+without conditions so long as your license otherwise remains in force.
+You may convey covered works to others for the sole purpose of having
+them make modifications exclusively for you, or provide you with
+facilities for running those works, provided that you comply with the
+terms of this License in conveying all material for which you do not
+control copyright. Those thus making or running the covered works for
+you must do so exclusively on your behalf, under your direction and
+control, on terms that prohibit them from making any copies of your
+copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the
+conditions stated below. Sublicensing is not allowed; section 10 makes
+it unnecessary.
+
+#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such
+circumvention is effected by exercising rights under this License with
+respect to the covered work, and you disclaim any intention to limit
+operation or modification of the work as a means of enforcing, against
+the work's users, your or third parties' legal rights to forbid
+circumvention of technological measures.
+
+#### 4. Conveying Verbatim Copies.
+
+You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+#### 5. Conveying Modified Source Versions.
+
+You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these
+conditions:
+
+- a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+- b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under
+ section 7. This requirement modifies the requirement in section 4
+ to "keep intact all notices".
+- c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+- d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+#### 6. Conveying Non-Source Forms.
+
+You may convey a covered work in object code form under the terms of
+sections 4 and 5, provided that you also convey the machine-readable
+Corresponding Source under the terms of this License, in one of these
+ways:
+
+- a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+- b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the Corresponding
+ Source from a network server at no charge.
+- c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+- d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+- e) Convey the object code using peer-to-peer transmission,
+ provided you inform other peers where the object code and
+ Corresponding Source of the work are being offered to the general
+ public at no charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal,
+family, or household purposes, or (2) anything designed or sold for
+incorporation into a dwelling. In determining whether a product is a
+consumer product, doubtful cases shall be resolved in favor of
+coverage. For a particular product received by a particular user,
+"normally used" refers to a typical or common use of that class of
+product, regardless of the status of the particular user or of the way
+in which the particular user actually uses, or expects or is expected
+to use, the product. A product is a consumer product regardless of
+whether the product has substantial commercial, industrial or
+non-consumer uses, unless such uses represent the only significant
+mode of use of the product.
+
+"Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to
+install and execute modified versions of a covered work in that User
+Product from a modified version of its Corresponding Source. The
+information must suffice to ensure that the continued functioning of
+the modified object code is in no case prevented or interfered with
+solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or
+updates for a work that has been modified or installed by the
+recipient, or for the User Product in which it has been modified or
+installed. Access to a network may be denied when the modification
+itself materially and adversely affects the operation of the network
+or violates the rules and protocols for communication across the
+network.
+
+Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+#### 7. Additional Terms.
+
+"Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders
+of that material) supplement the terms of this License with terms:
+
+- a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+- b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+- c) Prohibiting misrepresentation of the origin of that material,
+ or requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+- d) Limiting the use for publicity purposes of names of licensors
+ or authors of the material; or
+- e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+- f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions
+ of it) with contractual assumptions of liability to the recipient,
+ for any liability that these contractual assumptions directly
+ impose on those licensors and authors.
+
+All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions; the
+above requirements apply either way.
+
+#### 8. Termination.
+
+You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+#### 9. Acceptance Not Required for Having Copies.
+
+You are not required to accept this License in order to receive or run
+a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+#### 10. Automatic Licensing of Downstream Recipients.
+
+Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+#### 11. Patents.
+
+A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+A contributor's "essential patent claims" are all patent claims owned
+or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+A patent license is "discriminatory" if it does not include within the
+scope of its coverage, prohibits the exercise of, or is conditioned on
+the non-exercise of one or more of the rights that are specifically
+granted under this License. You may not convey a covered work if you
+are a party to an arrangement with a third party that is in the
+business of distributing software, under which you make payment to the
+third party based on the extent of your activity of conveying the
+work, and under which the third party grants, to any of the parties
+who would receive the covered work from you, a discriminatory patent
+license (a) in connection with copies of the covered work conveyed by
+you (or copies made from those copies), or (b) primarily for and in
+connection with specific products or compilations that contain the
+covered work, unless you entered into that arrangement, or that patent
+license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+#### 12. No Surrender of Others' Freedom.
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under
+this License and any other pertinent obligations, then as a
+consequence you may not convey it at all. For example, if you agree to
+terms that obligate you to collect a royalty for further conveying
+from those to whom you convey the Program, the only way you could
+satisfy both those terms and this License would be to refrain entirely
+from conveying the Program.
+
+#### 13. Use with the GNU Affero General Public License.
+
+Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+#### 14. Revised Versions of this License.
+
+The Free Software Foundation may publish revised and/or new versions
+of the GNU General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in
+detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies that a certain numbered version of the GNU General Public
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that numbered version or
+of any later version published by the Free Software Foundation. If the
+Program does not specify a version number of the GNU General Public
+License, you may choose any version ever published by the Free
+Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions
+of the GNU General Public License can be used, that proxy's public
+statement of acceptance of a version permanently authorizes you to
+choose that version for the Program.
+
+Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+#### 15. Disclaimer of Warranty.
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+#### 16. Limitation of Liability.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
+CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
+NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
+LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
+TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
+PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+#### 17. Interpretation of Sections 15 and 16.
+
+If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+END OF TERMS AND CONDITIONS
+
+### How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively state
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper
+mail.
+
+If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands \`show w' and \`show c' should show the
+appropriate parts of the General Public License. Of course, your
+program's commands might be different; for a GUI interface, you would
+use an "about box".
+
+You should also get your employer (if you work as a programmer) or
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. For more information on this, and how to apply and follow
+the GNU GPL, see .
+
+The GNU General Public License does not permit incorporating your
+program into proprietary programs. If your program is a subroutine
+library, you may consider it more useful to permit linking proprietary
+applications with the library. If this is what you want to do, use the
+GNU Lesser General Public License instead of this License. But first,
+please read .
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..412c1ae
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+# Thermostat firmware
+#
+# Copyright (c) 2022 Aleksander Mazur
+
+VARIANTS=$(wildcard firmware_*.asm)
+HEX_FILES=$(patsubst %.asm,%.hex,$(VARIANTS))
+BIN_FILES=$(patsubst %.asm,%.bin,$(VARIANTS))
+LST_FILES=$(patsubst %.asm,%.lst,$(VARIANTS))
+ASM_FILES=$(wildcard *.asm)
+SRC_FILES=$(filter-out $(VARIANTS),$(ASM_FILES))
+
+.PHONY: all clean
+
+all: $(BIN_FILES) $(HEX_FILES)
+ ./api.sh
+
+clean:
+ rm $(BIN_FILES) $(HEX_FILES) font.asm
+
+%.hex: %.asm $(SRC_FILES) font.asm
+ asem -i /usr/local/share/asem-51/1.3/mcu $<
+
+%.bin: %.hex
+ hexbin $< $@
+
+font.asm: font.py
+ ./$< > $@
diff --git a/README.md b/README.md
index ffd6198..4b6cd44 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,681 @@
-# thermostat-firmware
-Multi-channel differential thermostat and time controller AT89C2051/AT89C4051
+Multi-channel differential thermostat and time controller
+=========================================================
+
+This is a firmware for AT89C4051-based on-off controller with up to 8 relay outputs.
+It periodically measures temperature on all digital thermometers connected to
+1-wire network (DS18B20 & DS18S20, parasite power supported), reports found
+sensors along with their temperatures via UART and applies them to formulas
+held in EEPROM (AT24C02) in order to calculate output state.
+
+It works in a weekly cycle and allows flexible configuration.
+It can be used for HVAC, (solar) water heating, as a time controller
+(e.g. starting washing machine cycle),
+as watchdog for supervisor device (e.g. a router), all at once.
+
+Supported peripheral devices
+----------------------------
+
+| Bus | Device | Support |
+| ------ | ------- | ------- |
+| 1-wire | DS18B20 | Detection, temperature measurement & reporting, output control |
+| 1-wire | DS18S20 | Detection, temperature measurement & reporting, output control |
+| 1-wire | DS1820 | Detection, temperature measurement & reporting, output control |
+| 1-wire | DS2405 | Detection, GPIO state reporting |
+| 1-wire | DS2406 | Detection, GPIO state reporting |
+| I²C | AT24C02 | Configuration storage |
+| I²C | TMP75 | Temperature measurement & reporting |
+| I²C | [7-seg 4-digit LED display module] | Clock & temperature display |
+
+Principle of operation
+----------------------
+
+When not interrupted (or not connected to supervisor), the controller
+wakes up each 8 seconds and performs following operations (simplified):
+1. Initialize temperature measurement by internal sensor TMP75 on the I²C bus.
+2. Initialize temperature measurement by all sensors on the 1-wire bus.
+3. Clear control masks.
+4. Execute time control settings suitable for current day/time
+ (turn on, off, or toggle on/off configured relays).
+5. Wait until all sensors finish their measurements.
+6. Read temperature of internal sensor and report it.
+7. Enumerate all 1-wire devices using SEARCH ROM routine and for each found device:
+ - if family code is 28h (DS18B20) or 10h (DS18S20, DS1820) -- read its scratchpad
+ and continue as described below under *Proceed with thermometer*.
+ - if family code is 12h (DS2406) -- read and report PIO level and latch status of channel A and B, if present.
+ - if family code is 05h (DS2405) -- read PIO level 8 times and report 1 if all ones, 0 otherwise.
+8. Check if watchdog has just expired and switch relays configured as
+ watchdog on or off, accordingly.
+9. Iterate over all sensors configured in EEPROM and for each sensor
+ missing on the 1-wire bus during step 7:
+ - either turn on configured relays if marked as *critical*,
+ - or turn off configured relays if not marked as *critical*.
+10. Iterate over all formulas configured in EEPROM and apply results
+ to relay control masks.
+11. Apply relay control masks to the relay output port.
+
+### Proceed with thermometer
+
+For each enumerated 1-wire thermal sensor, after reading its scratchpad
+successfully, the controller proceeds as follows:
+1. Decode temperature out of scratchpad bytes and report it.
+2. Enumerate all sensor-related settings in EEPROM matching given sensor
+ and for each match:
+ - if it refers to another sensor (differential control) -- select the
+ other sensor using MATCH ROM, read its scratchpad, decode temperature
+ and subtract it from decoded temperature of the basic sensor;
+ use subtracted temperature instead of absolute one in further computations.
+ - find temperature threshold and hysteresis appropriate for current day/time.
+ - compare actual temperature (absolute or differential) agaist the threshold
+ and threshold minus hysteresis and establish required action according
+ to heating/colling flag, that is: whether to switch relays on, off or
+ leave them alone (within hysteresis).
+ - apply action to direct (relays) and indirect (intermediate) control masks.
+ - mark entry as used (sensor present) so that it is not treated as
+ missing in step 9 above.
+
+### Logic
+
+The same relays may be refered to by many setting blocks. There even
+may be many setting blocks for the same sensor. The controller uses
+separate masks for switching relays on and off. In case of a conflict,
+switching on takes precedence over switching off. So by default it
+applies OR function. In order to use AND function there is a separate
+pair of indirect control masks which refer to 8 virtual relays.
+Each sensor-related block of settings can control both a mask of relays
+and one particular virtual relay. During step 10 (above) each block
+of indirect control formulas performs AND on all masked virtual relays
+and forwards result to relay control masks.
+
+Configuration in EEPROM
+-----------------------
+
+Structure of configuration data in EEPROM (max. 256 B) follows.
+
+| Address | Bytes | Description |
+| -------:| ------:| ----------- |
+| 0 | 1 | Address of timer daily program for Sunday |
+| 1 | 1 | Address of timer daily program for Monday |
+| 2 | 1 | Address of timer daily program for Tuesday |
+| 3 | 1 | Address of timer daily program for Wednesday |
+| 4 | 1 | Address of timer daily program for Thursday |
+| 5 | 1 | Address of timer daily program for Friday |
+| 6 | 1 | Address of timer daily program for Saturday |
+| 7 | 1 | Watchdog relays mask |
+| 8 | 1 | mn = count of *functions* (high nibble, m) and *formulas* (low nibble, n) |
+| 9 | m*16 | *Functions* |
+| 9+m*16 | n*3 | *Formulas* |
+| 9+m*16+n*3 | 0 | End |
+
+### Function structure
+
+| Offset | Bytes | Description |
+| -------:| ------:| ----------- |
+| 0 | 6 | Middle part of 1-wire sensor ID (no family code, no CRC-8) to match |
+| 6 | 1 | 0 for absolute temperature control, otherwise -- in case of differential control -- address of middle part of related 1-wire sensor ID (the temperature of which should be subtracted from this one) |
+| 7 | 1 | Flags (see below) |
+| 8 | 1 | Relay mask |
+| 9 | 1 | Address of thermal daily program for Sunday |
+| 10 | 1 | Address of thermal daily program for Monday |
+| 11 | 1 | Address of thermal daily program for Tuesday |
+| 12 | 1 | Address of thermal daily program for Wednesday |
+| 13 | 1 | Address of thermal daily program for Thursday |
+| 14 | 1 | Address of thermal daily program for Friday |
+| 15 | 1 | Address of thermal daily program for Saturday |
+
+#### Function flags
+
+| Bit | Description |
+| ---:| ----------- |
+| 7 | Cooling (1) or heating (0) |
+| 6 | Critical function -- if set and no matching sensor is present, relays will be switched on |
+| 5 | Display flag -- if set, temperature will be shown on attached display module |
+| 4 | Reserved |
+| 3 | Indirect control -- if set, bits 2-0 contain index of virtual relay to control (along with relay mask) |
+| 2-0 | Number of virtual relay, meaningful if bit 3 is set |
+
+### Formula structure
+
+| Offset | Bytes | Description |
+| -------:| ------:| ----------- |
+| 0 | 1 | Mask of virtual relays to check |
+| 1 | 1 | Mask of virtual relays to control (cascade) |
+| 2 | 1 | Mask of actual relays to control |
+
+If all masked virtual relays (from mask @ 0) are to be turned on,
+virtual relays from mask @ 1 and actual relays from mask @ 2 will be turned on.
+
+If all masked virtual relays (from mask @ 0) are to be turned off,
+virtual relays from mask @ 1 and actual relays from mask @ 2 will be turned off.
+
+### Daily program
+
+Daily programs are divided into 2 parts. First part contains a list of
+time ranges. Each range is given by its beginning: hour and minute in BCD
+(2 bytes). The last range has additionally the most significant bit of
+hour (first byte) set, so it must be masked out. Ranges must be sorted
+in ascending order. Beginning of next range is the end of previous range.
+Beginning of the first range is the end of the last range.
+
+Immediately after the first part goes the second part which contains
+3-byte long control block for each range present in the first part.
+Format of these control blocks differ between timer and thermal programs.
+
+#### Control block of timer program
+
+| Offset | Bytes | Description |
+| -------:| ------:| ----------- |
+| 0 | 1 | Mask of relays to switch on |
+| 1 | 1 | Mask of relays to switch off |
+| 2 | 1 | Mask of relays to toggle on/off once |
+
+#### Control block of thermal program
+
+| Offset | Bytes | Description | Format |
+| -------:| ------:| ----------- | ------ |
+| 0 | 2 | Temperature in °C | fixed-point 8.8 bits |
+| 2 | 1 | Hysteresis to be subtracted from temperature | fixed-point 4.4 bits |
+
+## Example
+
+Assume we have just 3 sensors and 4 devices:
+* Room temperature sensor (ID FF0F31641408), which should control central heating system (relay #1)
+* Temperature sensor at one of solar panel collectors (ID FF0318C11708)
+* Temperature sensor inside potable water tank (ID FFAF44B31608)
+* A pump which pumps a heat transfer fluid through panels and heat exchanger inside the storage tank (relay #4)
+* A buzzer (output #7)
+* Watchdog reset output (relay #2, normally closed)
+
+For simplicity let's use the same settings for all days of the week.
+
+First we'll set up a timer program to turn off all unused outputs (and used ones too).
+It will have just 1 time range starting at 00:00 and turning off everything but output #0
+(because P1.0 serves as 1-wire parasite power control):
+```
+80 00 00 FE 00
+```
+
+Now let's create a thermal program for central heating.
+
+| From | Temperature | Hysteresis |
+| ----- | -----------:| ----------:|
+| 07:00 | 20.5 °C | 0.5 °C |
+| 07:30 | 18.5 °C | 0.5 °C |
+| 19:30 | 19.5 °C | 0.5 °C |
+| 21:30 | 20.5 °C | 0.5 °C |
+| 22:00 | 19.5 °C | 0.5 °C |
+```
+07 00 07 30 19 30 21 30 A2 00
+14 80 08 12 80 08 13 80 08 14 80 08 13 80 08
+```
+
+We want to run the solar water heating pump when:
+- difference between panels and tank is more than 12 °C,
+ but only when panels exceed 40 °C,
+- or when panels exceed 96 °C,
+- or when stored water exceeds 96 °C.
+
+For this purpose we need next 3 thermal programs: for detecting whether
+12 °C, 40 °C and 96 °C is exceeded. Let's use 2 °C of hysteresis in each case.
+```
+80 00 0C 00 20
+80 00 28 00 20
+80 00 60 00 20
+```
+
+Now 5 *functions*.
+First goes the heating configuration: ID of room sensor, 00 for absolute
+temperature control, all flags zeroed (heating program, not critical, no display,
+no indirect control), relays mask = 02 (output #1), xx need to be replaced
+with the address of appropriate thermal program shown above.
+```
+FF0F31641408 00 00 02 xxxxxxxxxxxxxx
+```
+Next one checks if solar panels have more than 40 °C.
+This alone is not enough for switching on any device, so relay mask is 00,
+but we use virtual relay #0.
+Note that this (and all remaining thermal programs) will be cooling,
+not heating (we're cooling the panels, not heating the tank).
+```
+FF0318C11708 00 88 00 xxxxxxxxxxxxxx
+```
+Next one is monitoring if the difference between solar panels and water in the tank
+is more than 12 °C.
+Let's use next free virtual relay (#1).
+yy needs to be replaced with the address of a *function* where ID
+of the sensor in the storage tank is given.
+```
+FF0318C11708 yy 89 00 xxxxxxxxxxxxxx
+```
+Next one is a safety measure against heat transfer fluid exceeding 96 °C.
+This one switches the pump (and a buzzer) on directly (mask 90).
+Let it be a critical function so the pump and buzzer are switched on
+also when the sensor is broken or unreachable.
+```
+FF0318C11708 00 C0 90 xxxxxxxxxxxxxx
+```
+Now similar safety measure, but against water in the tank exceeding 96 °C.
+Let's additionally set the display flag so a display module shows
+the temperature of potable hot water (only).
+```
+FFAF44B31608 00 E0 90 xxxxxxxxxxxxxx
+```
+
+In order to actually switch the pump on under normal conditions we need
+one *formula* to combine the state of virtual relays #0 and #1 (mask 03)
+in order to control the pump at output #4 (mask 10).
+```
+03 00 10
+```
+
+Put it all together and we get 137 B to write into EEPROM:
+```
+61 61 61 61 61 61 61 04 51 FF 0F 31 64 14 08 00
+00 02 6B 6B 6B 6B 6B 6B 6B FF 03 18 C1 17 08 00
+88 00 66 66 66 66 66 66 66 FF 03 18 C1 17 08 49
+89 00 84 84 84 84 84 84 84 FF 03 18 C1 17 08 00
+C0 90 5C 5C 5C 5C 5C 5C 5C FF AF 44 B3 16 08 00
+E0 90 5C 5C 5C 5C 5C 5C 5C 03 00 10 80 00 60 00
+20 80 00 00 FE 00 80 00 28 00 20 07 00 07 30 19
+30 21 30 A2 00 14 80 08 12 80 08 13 80 08 14 80
+08 13 80 08 80 00 0C 00 20
+```
+
+Supervisor interface (UART)
+---------------------------
+
+The controller uses UART (9600-8-N-1) for 2-way communication with a supervisor.
+
+### Output (controller -> supervisor)
+
+The controller periodically sends reports to supervisor.
+
+Examples:
+```
+02;14:00:00;T=21.5;28FF04053716004E=18.0625;28FFC4718316002D=18.3125;28FF0018C11700A8=47.3125;28FF581964140095=17.25;28FF0031641400C1=22.25;28FFA844B316000A=33.625;28FF000F3716004E=18.0625;FE&10|10=11;
+01;13:59:59;T=21.9375;28FFBF71B316042D!18;28FF0F71B316042D!20;28FF040F3716044E=18.3125;28FFAF44B316080A=!;28FF0018C11700A8=85;28FF581964140095=13.875;28FFC4718316002D=14.25;28FF0031641400C1=20.125;FE&10|00=11;
+```
+Syntax:
+```
+;::;((=)?;)*(E;)?&|=;
+```
+Where:
+| Part | Meaning |
+| ----------- | ------- |
+| \ | Carriage Return = ASCII #13. |
+| \ | Day of the week (00 = Sunday, 06 = Saturday). |
+| \ | Hour (00-23). |
+| \ | Minute (00-59). |
+| \ | Second (00-59). |
+| \ | ID of a device. For TMP75 this is just **T**. In other cases it is a 64-bit serial number of found 1-wire device, using byte order as discovered by SEARCH ROM routine (family code first, CRC-8 last). In case of SEARCH ROM error the ID is terminated with exclamation **!** followed by number of correctly discovered bits so far. Note that in case of EEPROM failure **E** is reported; it looks like a device with ID = **E** without value. |
+| \ | Temperature in °C, or exclamation **!** in case of an error (e.g. failed to read scratchpad). |
+| \ | Mask of all relays controlled automatically (= encountered in the configuration EEPROM). |
+| \ | Mask of relays to be switched off (0 = switch off, 1 = don't switch off). |
+| \ | Mask of relays to be switched on (1 = switch on, 0 = don't switch on). |
+| \ | Final state of relay outputs (after applying computed off & on masks). |
+| \ | Line Feed = ASCII #10. |
+
+### Input (supervisor -> controller)
+
+Supervisor can send any of the following commands to the controller
+anytime between \ and \ (that is, when the controller is idle).
+In case a command is received during sending report from the controller
+to supervisor, the controller continues to send its report, and then
+sends a dot (.) as a notification that a command has been skipped.
+Supervisor must wait for response before issuing next command.
+
+| Command | Description | Result |
+| ------- | ----------- | ------ |
+| I | Send I²C *START* | @ on success, ! on error |
+| S | Send I²C *STOP* | @ |
+| A | Send I²C *ACK* | @ |
+| N | Send I²C *NAK* | @ |
+| Wxx | Send byte xx (hex) to I²C | @ if acknowledged, ! on error |
+| R | Receive a byte from I²C | xx (received byte in hex) |
+| i | Do 1-wire *RESET* | @ on success, ! on error |
+| wxx | Send byte xx (hex) over 1-wire bus | @ |
+| r | Receive a byte from 1-wire bus | xx (received byte in hex) |
+| t | Restore 1-wire mode of DS1821 (16 pulses with power down) | @ on success, ! on error |
+| &xx | Switch off relays which have 0 in given mask xx (hex) | @ |
+| \|xx | Switch on relays which have 1 in given mask xx (hex) | @ |
+| ! | Wake up and perform next measuring/reporting/control cycle | a report |
+| \ | Reset watchdog | \ |
+| bxx | Read RAM byte at xx (hex) | yy (byte from RAM, hex) |
+| Bxx | Write byte xx (hex) to RAM at the address last used with **b** command (above) | @ |
+| E | Get address of configuration EEPROM on the I²C bus | xx (value of I2C_EEPROM_WR), or **!** if it's just A0 = the default |
+
+It is recommended that supervisor resets watchdog by sending space
+after each incoming report. Not doing this for `WATCHDOG_MAX` = 22 times
+in a row (normally ~3 minutes) causes toggling relays configured in
+EEPROM as watchdog.
+
+**b** and **B** commands are intended for reading and/or writing selected
+variables. Their locations in RAM are extracted from assembly listing
+at the end of the build process and provided in *firmware.h* header file.
+
+| Variable | Example address | Description |
+| ------------------------ | ---------------:| ----------------- |
+| API_global_rtcwd_weekday | 0x22 | Watchdog time - day of week (00-06) |
+| API_global_rtcwd_hours | 0x23 | Watchdog time - hour (00-23) |
+| API_global_rtcwd_minutes | 0x24 | Watchdog time - minute (00-59) |
+| API_global_rtcwd_seconds | 0x25 | Watchdog time - second (00-59) |
+| API_global_rtc_weekday | 0x26 | Current time - day of week (00-06) |
+| API_global_rtc_hours | 0x27 | Current time - hour (00-23) |
+| API_global_rtc_minutes | 0x28 | Current time - minute (00-59) |
+| API_global_rtc_seconds | 0x29 | Current time - second (00-59) |
+| API_global_clock_settings_index | 0x2B | Clock settings index |
+
+*Current time* is used in reports sent to UART and for applying
+appropriate part of configuration held in EEPROM. Supervisor should
+monitor time in reports received from UART and set *current time*
+variables accordingly as soon as it detects significant skew.
+
+If most significant bit of `API_global_rtcwd_weekday` is set, it means
+that watchdog was activated, and *watchdog time* holds a copy of
+*current time* made at that moment.
+When watchdog fires, the controller overwrites *watchdog time* and sets
+its most significant bit to 1 **only** if that bit was cleared.
+It is the responsibility of supervisor to clear that bit after reading
+*watchdog time*.
+
+Note that variables related to time use BCD format, so despite **b**
+and **B** commands use hexadecimal values, they look like decimal.
+
+*Clock settings index* is the address of part of settings in EEPROM
+which were applied last time for time control. This is used for
+switching on or off relays configured as "toggle" (i.e. start cycle of
+a washing machine). Supervisor needs to zero this byte whenever it changes
+`API_global_rtc_weekday`.
+
+## Examples
+
+Read scratchpad of 1-wire sensor with ID 28971DA80000000F:
+```
+iw55w28w97w1DwA8w00w00w00w0FwBErrrrrrrr
+```
+Write 4E,7F,7F,7F to its scratchpad:
+```
+iw55w28w97w1DwA8w00w00w00w0Fw4Ew7Fw7Fw7F
+```
+Read ROM -- makes sense when there is only 1 sensor on the 1-wire bus:
+```
+iw33rrrrrrrr
+```
+
+DS2406 - Channel Access (F5), read PIO-A, reset alarm, CRC-16
+```
+iw55w12w8Ew6Aw45w00w00w00wB5wF5wC5wFFr
+```
+
+Restore 1-wire mode of DS1821 (alone on the bus) persistently:
+```
+tiw0Cw41
+```
+Measure temperature with DS1821:
+```
+iwEEiwAAriwA0rriw41iwA0rr
+```
+The above reads 3 values: TEMP_READ, COUNT_REMAIN and COUNT_PER_C.
+
+temperature = TEMP_READ - 0.5 + (COUNT_PER_C - COUNT_REMAIN) / COUNT_PER_C
+
+Read 8 bytes from the beginning of AT24C02 (address A0 on the I²C bus):
+```
+IWA0W00IWA1RARARARARARARARNS
+```
+
+Write the 137 B of example configuration (above) to EEPROM
+(assuming I²C address A0):
+```
+IWA0W00W61W61W61W61W61W61W61W04S
+IWA0W08W51WFFW0FW31W64W14W08W00S
+IWA0W10W00W02W6BW6BW6BW6BW6BW6BS
+IWA0W18W6BWFFW03W18WC1W17W08W00S
+IWA0W20W88W00W66W66W66W66W66W66S
+IWA0W28W66WFFW03W18WC1W17W08W49S
+IWA0W30W89W00W84W84W84W84W84W84S
+IWA0W38W84WFFW03W18WC1W17W08W00S
+IWA0W40WC0W90W5CW5CW5CW5CW5CW5CS
+IWA0W48W5CWFFWAFW44WB3W16W08W00S
+IWA0W50WE0W90W5CW5CW5CW5CW5CW5CS
+IWA0W58W5CW03W00W10W80W00W60W00S
+IWA0W60W20W80W00W00WFEW00W80W00S
+IWA0W68W28W00W20W07W00W07W30W19S
+IWA0W70W30W21W30WA2W00W14W80W08S
+IWA0W78W12W80W08W13W80W08W14W80S
+IWA0W80W08W13W80W08W80W00W0CW00S
+IWA0W88W20S
+```
+Note that the controller just gives access to low-level I²C communications
+so supervisor is responsible for conformance with AT24C02 specifications,
+like writing each page of 8 bytes separately. Also, after sending each
+*STOP*, supervisor needs either to sleep for the time required by the EEPROM
+to finish programming the page (5 ms) or use acknowledge polling --
+**WA0** command will fail (return **!** instead of @) until internal
+write cycle is complete.
+
+Test [7-seg 4-digit LED display module]:
+```
+IW76W09S
+```
+
+Set RTC to Wednesday (3) noon (12:00) and reset *clock settings index*
+(assuming RAM locations as in the *Example address* above):
+```
+b26B03b27B12b28B00b29B00b2BB00
+```
+
+Build-time options
+------------------
+
+The firmware can be assembled using [asem-51].
+
+There are several options for defining how the peripherals are connected
+to the microcontroller and what features should be included in the firmware.
+
+### SDA, SCL
+Ports where I²C bus is connected to (with external pull-ups).
+If undefined, there is no I²C bus support.
+
+### I2C_EEPROM_WR
+Address (for writing = with least significat bit cleared) of AT24C02 on the I²C bus.
+
+### I2C_TEMP_WR
+Address (for writing) of TMP75 on the I²C bus.
+If undefined, there is no TMP75 support, what saves 97 B.
+
+### I2C_DISPLAY_WR
+Address (for writing) of [7-seg 4-digit LED display module] on the I²C bus.
+
+![Display]
+
+If undefined, there is no display module support, what saves 201 B.
+
+### OW_PARASITE
+If defined, then 1-wire bus is parasite-powered
+(OW_PWR=0 enables strong pull-up on the bus).
+
+If undefined, 1-wire devices are powered independently from the data line
+(OW_PWR=1 turns on separate power supply for 1-wire devices).
+
+Value is the time of temperature measurement in 8/225 s. Set it to 21 for ~750 ms.
+
+### OW_PWR
+Port which controls power of 1-wire devices; see OW_PARASITE.
+
+### OW_DQ
+Data line of the 1-wire bus.
+
+### RELAY_PORT
+8-bit port which controls relays.
+
+### CONTROL_NEGATIVE
+If defined, 0 switches on a relay, otherwise (by default) 1 switches on a relay.
+
+Note that this affects only the interface between the controller and a relay;
+it doesn't affect neither the UART API nor configuration in EEPROM,
+where 1 is always on and 0 is always off.
+
+### AT89C4051
+Whether we have 4kB of program memory. If defined, wider jump instructions are used.
+
+### SKIP_DS18S20
+If defined, cuts off DS18S20 support, what saves 43 B.
+
+### SKIP_DS1821
+If defined, cuts off support for 't' command specific for DS1821, what saves 19 B.
+
+Meaningless when OW_PARASITE is defined.
+
+### SKIP_DS2406
+If defined, cuts off DS2406 support, what saves 72 B.
+
+### SKIP_CTRL_TEMP
+If defined, cuts off temperature control, what saves 179 B.
+
+Watchdog and time control remains, but thermostat works as if all thermometers are missing on the bus.
+
+### SKIP_UART
+If defined, cuts off UART support (both input & output), what saves 506 B.
+
+### TUNE_1WIRE
+If defined, enables run-time tuning of delays used by 1-wire master, what takes 24 B more.
+
+The following parameters may be tuned (with the help of **b** and **B** commands):
+
+| Parameter | Default value | Description |
+| --------- | -------------:| ----------- |
+| tRST | 24 | Timeout after pulling line low in order to reset and waiting 15 µs before presence pulse from devices on the bus |
+| tLOW | 1 | Time of pulling the line low by us in order to start a read or write cycle |
+| tWR | 51 | Delay after sending value of a bit during write cycle |
+| tDSO | 9 | Delay after pulling the line low before sampling the line |
+| tRD | 41 | Delay after sampling the line |
+| `SEARCH_DELAY` | 0 | Extra delay before read/write cycles during SEARCH ROM routine |
+
+This is experimental feature, so addresses of these parameters are not part the API and actual addresses in RAM must be checked in the listing.
+
+| Constraint | Reason |
+| ---------- | ------ |
+| tLOW + tWR = 52 | Write cycle taking at least 60 µs |
+| tLOW + tDSO + tRD = 51 | Read cycle taking at least 60 µs |
+| tLOW + tDSO = 10 | Sampling the line before 15 µs |
+
+Values are in DJNZ execution times, that is 24 / 22118400 Hz = 1.085 µs.
+
+### CONSERVATIVE_CONTROL
+If defined, output control masks (both for switching relays on and off)
+are combined with values computed previously, before application to actual output ports.
+
+This avoids switching off (or on) devices unnecessarily in case of glitches
+on the 1-wire bus (or unstable bus) at the price of extended response time.
+
+### MATCH_ON_SEARCH_FAILURE
+If defined, in case of SEARCH ROM error (unstable 1-wire bus)
+the controller tries to read temperatures of known sensors
+using IDs stored in EEPROM, with MATCH ROM command instead.
+
+This workaround works for DS18B20 only because family code is not held
+in EEPROM so a hardcoded 28h is used.
+
+This extra routine takes 64 B.
+
+Hardware variants
+-----------------
+
+### Example 1
+
+Let's say we want to connect:
+- a typical opto-isolated 4-channel relay module (using inverted logic,
+ so we need CONTROL_NEGATIVE) to P1.7-4,
+- a buzzer -- directly to P1.3,
+- AT24C02 & TMP75 to P3.5 (SDA) and P3.4 (SCL),
+- a network of DS18B20 & DS18S20 parasite-powered thermometers to P3.7,
+ with BC557-keyed strong pull-up controlled via P3.2.
+
+![Relays] ![PCB top] ![PCB bottom]
+
+```
+CONSERVATIVE_CONTROL equ 1
+CONTROL_NEGATIVE equ 1
+RELAY_PORT equ P1
+OW_PWR equ P3.2
+OW_PARASITE equ 21
+OW_DQ equ P3.7
+SDA equ P3.5
+SCL equ P3.4
+I2C_EEPROM_WR equ 10100000b
+I2C_TEMP_WR equ 10010000b
+```
+
+This creates 2046 B of firmware, so it even fits AT89C2051, but note
+that contrary to AT89C4051 that part misses brown-out reset.
+
+### Example 2
+
+Instead of opto-isolated relay module we may use ULN2003 + up to 7 relays.
+
+![PCB2] ![PCB3] ![PCB4] ![PCB5]
+
+```
+CONSERVATIVE_CONTROL equ 1
+RELAY_PORT equ P1
+OW_PWR equ P1.0
+OW_PARASITE equ 21
+OW_DQ equ P3.7
+SDA equ P3.5
+SCL equ P3.4
+I2C_EEPROM_WR equ 10100000b
+I2C_TEMP_WR equ 10010000b
+TUNE_1WIRE equ 1
+```
+
+2041 B this time.
+
+### Example 3
+
+No parasite power, but capable of resetting DS1821 to 1-wire mode
+from standalone thermostat mode (using **t** command).
+Both I²C and 1-wire buses on P1, so single resistor ladder provides
+all the pull-ups.
+
+```
+CONSERVATIVE_CONTROL equ 1
+RELAY_PORT equ P1
+OW_PWR equ P1.0
+OW_DQ equ P1.1
+SDA equ P1.2
+SCL equ P1.3
+I2C_EEPROM_WR equ 10100000b
+I2C_TEMP_WR equ 10010000b
+```
+
+2027 B.
+
+License
+=======
+
+This file is part of Thermostat Firmware.
+
+Thermostat Firmware is free software: you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Thermostat Firmware is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the [GNU General Public License]
+along with Thermostat Firmware. If not, see .
+
+[7-seg 4-digit LED display module]: https://www.elektroda.pl/rtvforum/topic117391.html
+[Display]: img/LED_module.jpg
+[PCB top]: img/PCB_A.jpg
+[PCB bottom]: img/PCB_R.jpg
+[Relays]: img/relays.jpg
+[PCB2]: img/PCB2_A.jpg
+[PCB3]: img/PCB3_A.jpg
+[PCB4]: img/PCB4_A.jpg
+[PCB5]: img/PCB5_A.jpg
+[GNU General Public License]: LICENSE.md
+[asem-51]: http://plit.de/asem-51
diff --git a/api.sh b/api.sh
new file mode 100755
index 0000000..d549cee
--- /dev/null
+++ b/api.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# This file is part of Thermostat Firmware.
+#
+# Thermostat Firmware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Thermostat Firmware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Thermostat Firmware. If not, see .
+#
+# Copyright (c) 2022 Aleksander Mazur
+#
+# Ekstraktor API sterownika
+
+API_H=firmware.h
+
+[ -f $API_H ] && cp $API_H /tmp/api0.$$.h
+for f in `egrep -L '^SKIP_UART\s+NUMBER\s+[0]*1\s+' firmware_*.lst`; do
+ awk '/; \/API\// { gsub(":", "", $6); print "#define\tAPI_" $6 "\t0x" $3; }' "$f" > /tmp/api1.$$.h
+ if [ -f /tmp/api0.$$.h ]; then
+ diff -u /tmp/api0.$$.h /tmp/api1.$$.h || exit 1
+ fi
+ mv /tmp/api1.$$.h /tmp/api0.$$.h
+done
+if [ -f $API_H ]; then
+ echo API OK
+ rm /tmp/api0.$$.h
+else
+ mv /tmp/api0.$$.h $API_H
+fi
diff --git a/control.asm b/control.asm
new file mode 100644
index 0000000..aef8e21
--- /dev/null
+++ b/control.asm
@@ -0,0 +1,779 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2018, 2021 Aleksander Mazur
+;
+; Logika sterowania przekaźnikami
+; Używa procedur obsługi EEPROM z i2c_eeprom.asm
+; Procedury używają zmiennych control_*
+
+; Schemat wywołań:
+; control_init_rtc
+; control_watchdog
+; control_temperature
+; control_temperature
+; control_temperature
+; control_temperature
+; ...
+; control_missing
+; control_indirect_masks
+; control_apply_direct_masks
+
+;===========================================================
+
+; Stałe opisujące strukturę nastaw w EEPROM
+ctl_offset_watchdog equ 7 ; bajt z maską przekaźników sterowanych przez watchdoga
+ctl_offset_count equ 8 ; bajt z liczbą zdefiniowanych funkcji (młodsze pół) i formuł (starsze pół). Funkcje zaczynają się zaraz za
+ctl_offset_functions equ 9 ; offset definicji pierwszej funkcji - zaraz za ctl_offset_count
+ctl_offset_f_flags equ 7 ; offset bajtu z flagami względem początku funkcji
+ctl_offset_f_daily equ 9 ; offset tablicy offsetów programów dobowych na każdy dzień tygodnia (7 bajtów)
+; Flagi funkcji w EEPROM
+ctl_flag_cooling equ 80h ; flaga oznaczająca, że program służy do chłodzenia, a nie grzania
+ctl_flag_critical equ 40h ; flaga oznaczająca, że w przypadku braku (awarii) czujnika należy włączyć przekaźniki sterowane bezpośrednio
+ctl_flag_display equ 20h ; flaga oznaczająca, że temperaturę należy wyświetlać na wyświetlaczu
+ctl_flag_formula equ 08h ; flaga oznaczająca, że w 3 najmłodszych bitach jest numer wynikowego bitu sterowania pośredniego
+
+;===========================================================
+; API
+
+;-----------------------------------------------------------
+; Obsługuje przekaźniki watchdoga
+; Wejście: flaga F0 określa, czy watchdog wyexpirował (wtedy włączamy przekaźniki)
+; Zwraca C=1, jeśli wystąpił błąd odczytu z pamięci EEPROM; C=0, jeśli sukces
+; Niszczy A, B, C, R7
+control_watchdog:
+ mov B, #ctl_offset_watchdog
+ acall eeprom_read_byte_at
+ jc control_watchdog_ret
+ ; maska przekaźników watchdoga jest w A
+ orl control_mask_all_used, A
+ ; jeśli przekaźniki mają zostać wyłączone, to po prostu nie ruszamy masek innych niż global_mask_all_used
+ jb F0, control_turn_on
+control_watchdog_ret:
+ ret
+
+;-----------------------------------------------------------
+; Inicjalizuje maski sterowania i realizuje program zegarowy
+; Niszczy A, B, C, R1, R6, R7, F0
+control_init_rtc:
+ifdef I2C_DISPLAY_WR
+ clr flag_display_used
+ clr flag_display_missing
+endif
+ ; zerujemy maski sterowania
+ clr A
+ mov R1, #control_mask_start
+control_init_loop:
+ mov @R1, A
+ inc R1
+ cjne R1, #control_mask_end, control_init_loop
+;control_rtc:
+ ; offset 7 offsetów programu zegarowego = 0
+ clr A
+ acall control_read_daily_program
+ jc control_ret
+ ; mamy wczytany control_settings_block,
+ ; a w B mamy indeks pozycji użytego programu dobowego
+ ; do porównania z, a następnie do umieszczenia w
+ ; global_clock_settings_index
+ mov R1, #control_settings_block
+ mov A, @R1
+ ; pierwszy bajt nastaw - maska przekaźników do załączenia
+ acall control_turn_on
+control_rtc_loop:
+ mov A, @R1
+ ; drugi bajt nastaw - maska przekaźników do wyłączenia
+ orl control_mask_all_used, A
+ inc R1
+ cjne R1, #control_settings_block + 3, control_rtc_loop
+ ; trzeci bajt nastaw - maska przekaźników do chwilowego załączenia
+ xch A, B
+ cjne A, global_clock_settings_index, control_rtc_switch_on
+ ; A musiało być równe global_clock_settings_index, więc C=0
+control_ret:
+ ret
+control_rtc_switch_on:
+ ; A = nowy global_clock_settings_index
+ ; B = maska do załączenia
+ mov global_clock_settings_index, A
+ xch A, B
+ ;clr C ; tak naprawdę nie potrzebujemy wyniku w C
+ ;ajmp control_turn_on
+;-----------------------------------------------------------
+; Aplikuje maskę przekaźników (podaną w A) w celu załączenia
+control_turn_on:
+ orl control_mask_direct_or, A
+ifdef CONSERVATIVE_CONTROL
+ orl control_mask_prev_or, A
+endif
+; Aplikuje maskę przekaźników (podaną w A) w celu pozostawienia obecnego stanu bez zmian (tj. anuluje wyłączanie)
+control_turn_neutral:
+ orl control_mask_direct_and, A
+ifdef CONSERVATIVE_CONTROL
+ orl control_mask_prev_and, A
+endif
+control_ret2:
+ ret
+; Jeśli celem jest wyłączenie przekaźników, to nie trzeba nic wołać,
+; bo taki jest domyślny stan po control_init. Trzeba tylko pamiętać
+; (w każdym z 3 przypadków) o zor'owaniu maski z control_mask_all_used.
+; Aplikacja masek wg programu zegarowego i watchdoga musi pomijać
+; CONSERVATIVE_CONTROL
+
+;-----------------------------------------------------------
+; Realizuje programy, których czujniki nie zostały odnalezione.
+; W przypadku programu krytycznego włączamy jego przekaźniki,
+; a niekrytycznego - wyłączamy (tj. w każdym przypadku dołączamy maskę
+; do control_mask_all_used). Aktualizujemy też flagi sterowania pośredniego.
+; Niszczy A, B, C, R0, R1, R2(CRC), R3, R6, R7
+control_missing:
+ acall control_iterate_functions
+ jz control_ret2 ; nie ma żadnych funkcji w EEPROM lub wystąpił błąd
+ mov R0, A ; R0 = liczba funkcji w EEPROM (>0)
+ mov R3, #0 ; R3 = bieżący indeks funkcji liczony od 0
+ifdef I2C_DISPLAY_WR
+ ; zaczynamy szukanie następnej funkcji z włączoną flagą ctl_flag_display
+ ; o numerze większym od obecnej (display_func_curr)
+ mov A, display_func_curr
+ inc A
+ mov display_func_next, A
+ clr flag_display_found_next
+endif
+control_missing_loop:
+ifndef I2C_DISPLAY_WR
+ acall control_get_used_ptr
+ anl A, @R1
+ ; 0 - funkcja nie została użyta
+ jnz control_missing_next
+endif
+ ; czytamy flagi
+ acall control_read_flags
+ jc control_missing_next
+ acall i2c_shin
+ ; A = flagi
+ mov R2, A
+ acall i2c_ACK_shin ; zakładamy, że ctl_offset_f_flags + 1 = ctl_offset_f_relays
+ mov R6, A ; R6 = maska bezpośredniego sterowania przekaźnikami
+ orl control_mask_all_used, A ; nie powinno zaszkodzić nawet, jeśli funkcja była użyta
+ acall eeprom_read_stop
+ mov A, R2
+ifdef I2C_DISPLAY_WR
+ jnb ACC.5, control_missing_no_display ; C = ACC.5 (ctl_flag_display)
+ ; wyświetlanie temperatury wynikającej z tej funkcji jest włączone
+ jb flag_display_found_next, control_missing_no_display
+ ; czy R3 >= display_func_next?
+ mov A, R3
+ cjne A, display_func_next, control_missing_cont
+control_missing_cont:
+ jc control_missing_no_display
+ ; znaleźliśmy nowy numer
+ mov display_func_next, A
+ setb flag_display_found_next
+control_missing_no_display:
+ acall control_get_used_ptr
+ anl A, @R1
+ ; 0 - funkcja nie została użyta
+ jnz control_missing_next
+ mov A, R3
+ cjne A, display_func_curr, control_missing_another
+ ; mieliśmy właśnie wyświetlić tę temperaturę, ale brakuje czujnika
+ ; być może wypadałoby jeszcze sprawdzić, czy ctl_flag_display jest ustawiona...
+ setb flag_display_missing
+control_missing_another:
+ mov A, R2
+endif
+ mov C, ACC.6 ; C = ACC.6 (ctl_flag_critical)
+ ; obliczamy maskę sterowania pośredniego
+ acall control_calc_direct_mask
+ mov R7, A ; R7 = maska sterowania pośredniego
+ ; wyznaczamy akcję do wykonania na maskach - kompatybilną z
+ ; control_calc_direct, właściwą dla control_apply_action,
+ ; czyli:
+ ; - jeśli funkcja jest krytyczna -> -1 - należy załączyć przekaźniki
+ ; - w przeciwnym przypadku -> 0 - należy wyłączyć przekaźniki
+ ; flaga ctl_flag_critical jest w C
+ clr A
+ subb A, #0
+ ; wykonujemy akcję
+ acall control_apply_action
+control_missing_next:
+ inc R3
+ djnz R0, control_missing_loop
+ifdef I2C_DISPLAY_WR
+ jb flag_display_found_next, control_missing_next_ok
+ ; jeśli nie znalazła się następna funkcja z włączoną flagą ctl_flag_display,
+ ; to następnym razem skanujemy od początku
+ mov display_func_next, #-1
+control_missing_next_ok:
+ ; jeśli flag_display_used=1, to znaczy, że wysłaliśmy już temperaturę na wyświetlacz
+ ; jeśli flag_display_missing=1, to znaczy, że brakuje temperatury czujnika,
+ ; którego temperatura miała teraz znaleźć się na wyświetlaczu
+ jb flag_display_used, control_display_used
+ jb flag_display_missing, control_display_used
+ ; jeśli obie flagi są 0, to pokazujemy zegarek
+ bcall display_clock
+control_display_delay:
+ setb flag_display_used
+ ; wyświetlacz nie reaguje na ustawianie jasności wysyłane od razu;
+ ; po sukcesie i2c_shout R7=0 i wtedy opóźnienie wynosi
+ ; 256*(12+24)/22118400 = 5/12 ms
+ djnz R7, control_display_delay
+control_display_used:
+ clr A
+ jnb flag_display_used, control_display_unused
+ ; jeśli mamy coś na wyświetlaczu (temperaturę albo zegarek),
+ ; to włączamy wyświetlacz na 2/4, w przeciwnym razie
+ ; - kiedy brakuje wszystkich czujników, których temperaturę
+ ; mieliśmy wyświetlać - wyłączamy wyświetlacz
+ mov A, #2
+control_display_unused:
+ ; A = flag_display_used ? 3 (3/4 jasności) : 0 (wygaś)
+ bcall display_dim
+ mov display_func_curr, display_func_next
+ jnb flag_display_missing, control_ret2
+ ; jeszcze piśnięcie, jeśli nie wyświetliliśmy nic z powodu braku czujnika
+ bjmp display_beep
+else
+ ret
+endif
+
+ifndef SKIP_CTRL_TEMP
+;-----------------------------------------------------------
+; Realizuje programy przypisane do czujnika o ID w global_ow_id
+; Jego temperatura ma być dana w local_temp_h:local_temp_l
+; Zapalamy odpowiednie bity w control_missing_sensors.
+; Niszczy A, B, C, F0, R0, R1, R2(CRC), R3, R4(local_temp_h), R5(local_temp_l), R6, R7
+; Niszczy też local_scratchpad1 i local_scratchpad2 oraz control_settings_block.
+control_temperature:
+ acall control_iterate_functions
+ jz control_ret2 ; nie ma żadnych funkcji w EEPROM lub wystąpił błąd
+ ; zachowujemy temperaturę, bo programy różnicowe ją niszczą
+ mov local_scratchpad1 + 0, local_temp_h
+ mov local_scratchpad1 + 1, local_temp_l
+ mov R0, A ; R0 = liczba funkcji w EEPROM (>0)
+ mov R3, #0 ; R3 = bieżący indeks funkcji liczony od 0
+control_temperature_loop:
+ ; sprawdzamy, czy funkcja pod indeksem R3 jest zdefiniowana dla czujnika, którego ID mamy w global_ow_id
+ acall control_get_function_address
+ mov B, A
+ acall eeprom_read_start
+ jc control_temperature_next
+ ; w EEPROM ma pasować 6 bajtów poczynając od drugiego w global_ow_id (tj. środek, bez family code i CRC8)
+ mov R1, #(global_ow_id + 1)
+control_temperature_match:
+ acall i2c_shin
+ mov B, A
+ mov A, @R1
+ cjne A, B, control_temperature_stop
+ acall i2c_ACK
+ inc R1
+ cjne R1, #(global_ow_id + 7), control_temperature_match
+ ; cały ID pasuje; następny i2c_shin odczyta bajt spod ctl_offset_f_diff
+ ; czytamy zatem bajt diff
+ acall i2c_shin
+ jnz control_temperature_diff
+control_temperature_abs:
+ acall i2c_ACK
+ ; odtwarzamy zachowaną temperaturę
+ ; (jakiś program różnicowy mógł ją zniszczyć w poprzedniej iteracji)
+ mov local_temp_h, local_scratchpad1 + 0
+ mov local_temp_l, local_scratchpad1 + 1
+ sjmp control_temperature_known
+control_temperature_diff:
+ ; program różnicowy - funkcja wymaga różnicy temperatur 2 czujników
+ ; odjemną już mamy, odjemnik musimy pobrać z czujnika
+ acall eeprom_read_stop
+ mov B, A ; B = adres ID czujnika, którego temperaturę musimy odjąć od local_temp
+ acall eeprom_read_start
+ jc control_temperature_next
+ ; zainicjowaliśmy odczyt z EEPROM, możemy pobrać scratchpad z czujnika
+ acall owhl_read_second_scratchpad
+ ; odczyt z EEPROM jest zatrzymany
+ jc control_temperature_next
+ ; odejmujemy temperatury
+ acall control_temp_diff
+ jc control_temperature_next
+ ; zaczynamy odczyt z EEPROM od miejsca, w którym przerwaliśmy
+ acall control_read_flags
+ jc control_temperature_next
+control_temperature_known:
+ ; tutaj wchodzimy w stanie po ACK lub po zainicjowaniu odczytu
+ ; w tym bloku (od control_temperature_known do control_temperature_next)
+ ; używamy rejestrów R2 i R6, więc nie używamy procedur obsługi 1-wire
+ ; (które też ich używają)
+ acall i2c_shin
+ mov R2, A ; R2 = flagi
+ acall i2c_ACK_shin
+ push ACC ; maska bezpośredniego sterowania przekaźnikami - na stos
+ ; musimy przerwać odczyt, żeby pobrać dane z właściwego programu dobowego
+ acall eeprom_read_stop
+ifdef I2C_DISPLAY_WR
+ mov A, R2
+ jnb ACC.5, control_temperature_displayed ; ctl_flag_display
+ bcall display_temperature
+control_temperature_displayed:
+endif
+ acall control_get_function_address
+ add A, #ctl_offset_f_daily
+ acall control_read_daily_program
+ pop ACC
+ jc control_temperature_next
+ ; mamy nastawioną temperaturę w control_settings_block
+ ; pierwsze 2 bajty to wartość nadająca się od razu do porównania z local_temp_h:local_temp_l
+ ; trzeci bajt to histereza: starsza połówka to wartość całkowita, a młodsza - po przecinku
+ ; bierzemy odpowiedzialność za wykonanie funkcji - oznaczymy ją
+ ; jako użytą (przez ustawienie bitu w control_missing_sensors),
+ ; więc control_missing nic już z nią nie zrobi
+ mov R6, A ; R6 = maska bezpośredniego sterowania przekaźnikami
+ orl control_mask_all_used, A
+ ; obliczamy maskę sterowania pośredniego
+ mov A, R2
+ acall control_calc_direct_mask
+ mov R7, A ; R7 = maska sterowania pośredniego
+ ; wyznaczamy akcję do wykonania na maskach
+ acall control_calc_direct
+ ; wykonujemy akcję
+ acall control_apply_action
+ ; oznaczamy funkcję jako użytą
+ acall control_get_used_ptr
+ orl A, @R1
+ mov @R1, A
+control_temperature_next:
+ inc R3
+ djnz R0, control_temperature_loop
+ ret
+control_temperature_stop:
+ acall eeprom_read_stop
+ sjmp control_temperature_next
+
+endif ;SKIP_CTRL_TEMP
+
+;-----------------------------------------------------------
+; Przelicza maski sterowania pośredniego (control_mask_indirect_*)
+; przy pomocy formuł w EEPROM i aktualizuje maski bezpośrednio
+; sterujące przekaźnikami (control_mask_direct_*)
+; Niszczy A, B, C, R0, R1, R6, R7
+control_indirect_masks:
+ acall control_iterate_functions
+ ; A może być 0, ale tutaj to nie problem, natomiast w razie C=1 R0 jest niezdefiniowane
+ jc control_ret3
+ ; mamy "count" w R0, liczba funkcji jest w starszej połówce, a liczba formuł - w młodszej
+ ; w A mamy liczbę funkcji
+ swap A
+ add A, #ctl_offset_functions
+ mov B, A ; B = adres pierwszego bajtu za definicjami funkcji, czyli początek formuł
+ mov A, R0
+ anl A, #00001111b
+ jz control_ret3 ; nie ma żadnych formuł
+ mov R0, A ; R0 = liczba formuł (> 0)
+ ; teraz czytamy R0 3-bajtowych bloków z EEPROM poczynając od adresu w B
+ acall eeprom_read_start
+ jnc control_indirect_formula_no_ack
+control_ret3:
+ ret
+ ; nie możemy zacząć od ACK, ale przed każdym następnym odczytem musimy potwierdzić poprzedni
+ ; ale ostatniego znowu nie możemy potwierdzić, bo zamiast tego musimy wysłać NAK (robi to eeprom_read_stop)
+control_indirect_formula:
+ acall i2c_ACK
+control_indirect_formula_no_ack:
+ ; czytamy 3 bajty definicji formuły do control_settings_block
+ acall control_read_settings_block
+ ; formuła w control_settings_block; kolejne bajty to:
+ ; 0: maska warunku (do zastosowania do control_mask_indirect_{and,or})
+ ; 1: maska sterowania pośredniego (sterowanie pośrednie kaskadowe)
+ ; 2: maska sterowania bezpośredniego (sterowanych przekaźników)
+ ; w A mamy gratis trzeci bajt formuły, czyli maskę sterowanych przekaźników
+ orl control_mask_all_used, A
+ mov R6, A ; R6 = maska bezpośredniego sterowania przekaźnikami
+ clr A ; 0 -> akcja = wyłącz
+ mov R7, A
+ mov R1, #control_mask_indirect_or ; control_mask_indirect_or musi być tuż przed control_mask_indirect_and!
+control_indirect_formula_loop:
+ ; w pierwszym przebiegu pętli R1=control_mask_indirect_or i C=0 (po control_read_settings_block)
+ ; i wtedy ewentualnie akcja = włącz (czyli A|=-1)
+ ; w drugim przebiegu pętli R1=control_mask_indirect_and i C=1 (po cjne niżej)
+ ; i wtedy ewentualnie akcja = zostaw (czyli A|=+1)
+ ; czy w control_mask_indirect_{and,or} na pozycjach maskowanych przez warunek formuły są same jedynki?
+ mov A, control_settings_block
+ cpl A
+ orl A, @R1
+ ; A = control_mask_indirect_{and,or} OR NOT control_settings_block
+ inc A
+ ; A=0 -> są same jedynki
+ jnz control_indirect_skip
+ ; A=0; musimy zamienić C=0 na -1 a C=1 na +1
+ rlc A
+ rl A
+ dec A
+ ; i orujemy z akcją w R7
+ orl A, R7
+ mov R7, A
+control_indirect_skip:
+ inc R1
+ cjne R1, #control_mask_indirect_or + 2, control_indirect_formula_loop
+ mov A, R7
+ mov R7, control_settings_block + 1 ; R7 = maska sterowania pośredniego
+ acall control_apply_action
+ djnz R0, control_indirect_formula
+ ; koniec
+ ajmp eeprom_read_stop
+
+;-----------------------------------------------------------
+; Aplikuje obliczone maski bezpośrednio sterujące przekaźnikami
+; (control_mask_direct_*)
+; Niszczy A
+control_apply_direct_masks:
+ ; RELAY_PORT &= control_mask_direct_and | ~control_mask_all_used | control_mask_direct_or [| control_mask_prev_and]
+ mov A, control_mask_all_used
+ cpl A
+ orl A, control_mask_direct_and
+ orl A, control_mask_direct_or
+ifdef CONSERVATIVE_CONTROL
+ ; wyłączamy tylko to, co i poprzednio chcieliśmy wyłączyć
+ orl A, control_mask_prev_and
+endif
+ ; zera na pozycjach, które należy wyłączyć
+ifdef CONTROL_NEGATIVE
+ cpl A
+ orl RELAY_PORT, A
+else
+ anl RELAY_PORT, A
+endif
+ ; RELAY_PORT |= control_mask_direct_or & control_mask_all_used [& control_mask_prev_or]
+ mov A, control_mask_all_used
+ anl A, control_mask_direct_or
+ifdef CONSERVATIVE_CONTROL
+ ; włączamy tylko to, co i poprzednio chcieliśmy włączyć
+ anl A, control_mask_prev_or
+endif
+ ; jedynki na pozycjach, które należy włączyć
+ifdef CONTROL_NEGATIVE
+ cpl A
+ anl RELAY_PORT, A
+else
+ orl RELAY_PORT, A
+endif
+ifdef CONSERVATIVE_CONTROL
+ mov control_mask_prev_or, control_mask_direct_or
+ mov control_mask_prev_and, control_mask_direct_and
+endif
+ ; koniec
+ ret
+
+;===========================================================
+; Procedury wewnętrzne
+
+ifndef SKIP_CTRL_TEMP
+;-----------------------------------------------------------
+; Wyznacza akcję do wykonania w wyniku porównania bieżącej temperatury
+; z local_temp_h:local_temp_l z nastawioną temperaturą podaną
+; w control_settings_block (odczytaną z programu temperaturowego)
+; przy uwzględnieniu flagi chłodzenia podanej w najstarszym bicie R2.
+; Zwraca w A:
+; -1 - należy załączyć przekaźniki
+; +1 - należy zostawić przekaźniki w obecnym stanie
+; 0 - należy wyłączyć przekaźniki
+; Niszczy A, B, C
+control_calc_direct:
+ ; najpierw obliczymy wynik, jak gdyby to był program ogrzewania, a potem ewentualnie go zanegujemy
+ ; obliczamy T_bieżąca - T_nastawiona
+ clr C
+ mov A, local_temp_l
+ subb A, control_settings_block + 1
+ mov B, A
+ ; B = LSB wyniku
+ mov A, local_temp_h
+ subb A, control_settings_block + 0
+ ; A = MSB wyniku
+ mov C, ACC.7 ; teraz C=0, jeśli wyszła liczba nieujemna, czyli T_bieżąca - T_nastawiona >= 0
+ jnc control_calc_direct_heat_on_off
+ ; teraz dodamy do (ujemnego) wyniku histerezę i jeśli zostanie na minusie, to T_bieżąca >= T_nastawiona - T_histereza
+ ; histereza jest zapisana na jednym bajcie, fixed point 4.4
+ push ACC
+ mov A, control_settings_block + 2
+ swap A
+ anl A, #11110000b
+ add A, B ; interesuje nas tylko bit przeniesienia z LSB
+ mov A, control_settings_block + 2
+ swap A
+ anl A, #00001111b
+ pop B
+ addc A, B
+ rlc A ; teraz C=0, jeśli wyszła liczba nieujemna, czyli T_bieżąca - T_nastawiona + T_histereza >= 0
+ jc control_calc_direct_heat_on_off
+ ; pozycja neutralna
+ mov A, #1
+ ; w tym przypadku negowanie nie ma znaczenia, nie musimy więc sprawdzać flagi chłodzenia
+ ret
+control_calc_direct_heat_on_off:
+ ; C=0 -> wyłączamy ogrzewanie
+ ; C=1 -> załączamy ogrzewanie
+ clr A
+ subb A, #0
+ ; w A mamy 0 albo -1
+ ; sprawdzamy, czy to może jednak program chłodzenia
+ mov B, R2
+ jnb B.7, control_ret4 ; ctl_flag_cooling
+ ; negujemy wynik
+ cpl A ; -1 -> 0, 0 -> -1
+control_ret4:
+ ret
+
+endif ;SKIP_CTRL_TEMP
+
+;-----------------------------------------------------------
+; Aplikuje akcję zwróconą przez control_calc_direct (w A) na maskach
+; sterowania bezpośredniego (w R6) i pośredniego (w R7).
+control_apply_action:
+ ; 0 - wyłączamy przekaźniki - nie trzeba nic więcej robić
+ jz control_ret5
+ ; -1 - należy załączyć przekaźniki
+ ; +1 - należy zostawić przekaźniki w obecnym stanie
+ rlc A ; rozróżniamy po najstarszym bicie A
+ ; C=1 - należy załączyć przekaźniki
+ ; C=0 - należy zostawić przekaźniki w obecnym stanie
+ ; blokujemy wyłączenie
+ mov A, R6
+ orl control_mask_direct_and, A
+ mov A, R7
+ orl control_mask_indirect_and, A
+ jnc control_ret5
+ ; wymuszamy załączenie
+ mov A, R6
+ orl control_mask_direct_or, A
+ mov A, R7
+ orl control_mask_indirect_or, A
+control_ret5:
+ ret
+
+;-----------------------------------------------------------
+; Wczytuje z EEPROM 3-bajtowy blok nastaw programu dobowego właściwych
+; dla aktualnego czasu zegarowego.
+; Wejście: A - offset (w EEPROM) początku 7-bajtowej tablicy offsetów
+; programów na każdy dzień tygodnia.
+; Zwraca C=0, gdy udało się wczytać parametry do control_settings_block.
+; Wówczas zwraca też indeks pozycji w użytym programie dobowym (w B).
+; Zwraca C=1, gdy wystąpił błąd komunikacji z EEPROM lub nie ma bloku
+; nastaw właściwego dla bieżącego czasu.
+; Zostawia magistralę I2C w stanie wolnym.
+; Niszczy A, B, C, R1, R6, R7, F0
+control_read_daily_program:
+ add A, global_rtc_weekday
+ ; w A jest adres offsetu programu na bieżący dzień tygodnia
+ mov B, A
+ acall eeprom_read_byte_at
+ jc control_ret6
+ ; w A jest offset programu na bieżący dzień tygodnia
+ ; albo 0, jeśli takowy nie jest określony
+ jnz control_read_daily_program_not_empty
+ setb C
+control_ret6:
+ ret
+control_read_daily_program_not_empty:
+ mov B, A
+ acall eeprom_read_start
+ jc control_ret6
+ ; jesteśmy w bloku godzin/minut BCD (po 2 bajty) zakończonego
+ ; terminatorem - pojedyncznym bajtem FF
+ setb F0 ; faza pętli: 1 - szukamy indeksu po godzinie; 0 - szukamy końca programu (nie zwiększamy już R6)
+ clr A
+ mov R1, A ; indeks elementu programu, aktualizowany aż do końca
+ mov R6, A ; indeks elementu programu właściwego dla bieżącej godziny i minuty
+control_read_daily_program_entry:
+ ; wczytujemy godzinę (z flagą końca na najstarszym bicie) i minutę początkową elementu programu
+ acall i2c_shin ; A = godzina (z flagą)
+ ; zachowujemy godziny (z flagą) i odczytujemy minuty
+ mov B, A
+ acall i2c_ACK_shin ; A = minuta
+ ; jesteśmy na kolejnej pozycji programu (liczona od 1)
+ inc R1
+ ; jeśli znaleźliśmy już właściwą pozycję, to teraz tylko pomijamy pozostałe
+ jnb F0, control_read_daily_program_dont_cmp
+ ; odejmujemy od czasu podanego na bieżącej pozycji programu bieżący czas zegarowy
+ setb C ; dzięki ustawionemu tutaj bitowi pożyczki, końcowo flaga będzie ustawiona również przy czasach równych sobie
+ clr EA ; blokujemy przerwania, żeby stan zegara nie zmienił się w trakcie
+ subb A, global_rtc_minutes
+ mov A, B ; A = godzina
+ anl A, #00111111b ; dziesiątkom godzin wystarczy zakres 0-3
+ subb A, global_rtc_hours
+ setb EA ; zegarek może sobie chodzić dalej
+ ; jeśli C=1, to bieżący czas jest równy lub późniejszy niż czas początkowy bieżącego elementu programu
+ ; jeśli C=0, to właśnie przejechaliśmy właściwy element programu; zostawimy R6 = R1 - 1, a R1 pojedzie dalej
+ mov F0, C
+control_read_daily_program_dont_cmp:
+ jnb F0, control_read_daily_program_leave_pos
+ inc R6
+control_read_daily_program_leave_pos:
+ jb B.7, control_read_daily_program_end ; flaga końca oznacza, że przetworzyliśmy ostatnią pozycję z czasem
+ acall i2c_ACK ; potwierdzamy odczyt bajtu z minutami, będziemy czytać bajt z godzinami z następnej pozycji
+ sjmp control_read_daily_program_entry
+control_read_daily_program_end:
+ ; jesteśmy w stanie przed i2c_ACK/NAK
+ ; R1 = liczba pozycji programu (na pewno > 0)
+ ; R6 = indeks pozycji programu, której powinniśmy użyć, liczony od 1
+ mov A, R6
+ jnz control_read_daily_program_start
+ ; jeśli mamy użyć pozycji 0, to znaczy, że na pierwszej pozycji już była za późna godzina; w takim wypadku użyjemy ostatniej pozycji
+ mov A, R1
+ ; w A jest indeks pozycji programu, której mamy użyć, liczony od 1
+ ; czyli tyle 3-bajtowych bloków musimy wczytać, zapamiętując tylko ostatni z nich
+ mov R6, A
+control_read_daily_program_start:
+ ; w R6, ale też w A jest indeks pozycji w programie liczony od 1
+ ; zachowujemy go w B
+ mov B, A
+control_read_daily_program_block:
+ ; w każdym kroku pętli jesteśmy w stanie przed i2c_ACK/NAK
+ ; w R6 jest liczba pozostałych obrotów pętli
+ ; czytamy 3 bajty pod local_settings_block
+ acall i2c_ACK
+ acall control_read_settings_block
+ djnz R6, control_read_daily_program_block
+ ; sukces
+ clr C
+ ajmp eeprom_read_stop
+
+;-----------------------------------------------------------
+; Pobiera liczbę definicji funkcji w EEPROM do A.
+; W razie niepowodzenia zwraca A=0 i C=1.
+; W razie powodzenia zwraca też cały bajt spod ctl_offset_count w R0.
+; Niszczy A, B, C, R0, R7
+control_iterate_functions:
+ mov B, #ctl_offset_count
+ acall eeprom_read_byte_at
+ jc control_ret0
+ ; mamy "count" w A, liczba funkcji jest w starszej połówce
+ mov R0, A
+ swap A
+ anl A, #00001111b
+ ret
+control_ret0:
+ clr A
+ ret
+
+;-----------------------------------------------------------
+; Oblicza adres w EEPROM funkcji pod indeksem danym w R3.
+; Zwraca go w A.
+control_get_function_address:
+ ; A = R3 * 0x10 + offset początku funkcji
+ mov A, R3
+ swap A
+ add A, #ctl_offset_functions
+ ret
+
+;-----------------------------------------------------------
+; Oblicza wskaźnik na bit informujący o tym, czy funkcja o indeksie
+; danym w R3 została użyta.
+; Zwraca:
+; R1 - wskaźnik na bajt, gdzie jest przechowywany owy bit
+; A - maska w w/w bajcie wyłuskująca owy bit
+; Niszczy A, B, C, R1
+control_get_used_ptr:
+ mov A, R3
+ mov B, #8
+ div AB
+ ; A = indeks bajtu
+ ; B = indeks bitu w ramach bajtu
+ add A, #control_missing_sensors
+ mov R1, A
+; Oblicza maskę, która ma zapalony tylko jeden bit.
+; Wejście: B = indeks bitu do zapalenia jako jedyny w masce
+; Wyjście: A = maska
+; Niszczy A, B
+control_get_mask_1bit:
+ ; przerabiamy indeks bitu na maskę
+ mov A, #1
+ ; obracamy jedynkę w lewo o jeden raz więcej, niż trzeba (od 1 do 8 obrotów)
+ ;inc B ; równie dobrze możemy obrócić akumulator 256 razy zamiast 0
+control_get_mask_1bit_loop:
+ rl A
+ djnz B, control_get_mask_1bit_loop
+ ; cofamy ostatni nadmiarowy obrót
+ ;rr A
+ ret
+
+;-----------------------------------------------------------
+; Oblicza maskę sterowania pośredniego na podstawie bajtu flag (ctl_offset_f_flags)
+; Wejście: A = wartość bajtu spod ctl_offset_f_flags danej funkcji
+; Wyjście: A = maska (z jednym bitem lub cała wyzerowana)
+; Niszczy A, B
+control_calc_direct_mask:
+ jnb ACC.3, control_ret0 ; ctl_flag_formula
+ ; najmłodsze 3 bity akumulatora to numer bitu w masce
+ anl A, #00000111b
+ mov B, A
+ ; obliczamy właściwą maskę
+ sjmp control_get_mask_1bit
+
+;-----------------------------------------------------------
+; Inicjuje odczyt bajtu z flagami funkcji o indeksie danym w R3.
+; Niszczy A, B, C, R7
+control_read_flags:
+ acall control_get_function_address
+ add A, #ctl_offset_f_flags
+ mov B, A
+ ajmp eeprom_read_start
+
+ifndef SKIP_CTRL_TEMP
+;-----------------------------------------------------------
+; Dekoduje temperaturę z local_scratchpad2, odejmuje ją od temperatury
+; trzymanej w 2 pierwszych bajtach local_scratchpad1 (MSB, potem LSB),
+; a wynik umieszcza w local_temp_h:local_temp_l.
+; Niszczy A, C, R1
+; Zwraca C=0, jeśli sukces, a C=1, jeśli wystąpił błąd dekodowania
+; temperatury z local_scratchpad2.
+control_temp_diff:
+ mov R1, #local_scratchpad2
+ acall owhl_get_temperature_from_scratchpad ; nadpisuje l_temp_{h:l}
+ jc control_ret7
+ ;mov A, #'-'
+ ;acall write_char
+ ;acall write_temperature
+ ; odejmujemy temperatury: stara minus nowa
+ mov A, local_scratchpad1 + 1 ; stara temp_l
+ clr C
+ subb A, local_temp_l
+ mov local_temp_l, A
+ mov A, local_scratchpad1 + 0 ; stara temp_h
+ subb A, local_temp_h
+ mov local_temp_h, A
+ ;mov A, #'='
+ ;acall write_char
+ ;acall write_temperature
+ clr C
+control_ret7:
+ ret
+
+endif
+
+;-----------------------------------------------------------
+; Odczytuje z EEPROM 3 bajty do control_settings_block
+; używając sekwencji i2c_shin, i2c_ACK, i2c_shin, i2c_ACK, i2c_shin
+; Niszczy A, C, R1, R7
+; Zwraca w A ostatnio wczytany bajt (tj. control_settings_block + 2)
+; Zwraca C=0
+control_read_settings_block:
+ mov R1, #control_settings_block
+ sjmp control_read_settings_no_ack
+control_read_settings_byte:
+ ; potwierdzamy odczyt poprzedniego bajtu i czytamy następny bajt pod @R1
+ acall i2c_ACK
+control_read_settings_no_ack:
+ acall i2c_shin
+ mov @R1, A
+ inc R1
+ cjne R1, #(control_settings_block + 3), control_read_settings_byte
+ ; w ostatnim kroku liczby są równe, więc C=0
+ ret
diff --git a/crc8.asm b/crc8.asm
new file mode 100644
index 0000000..7f1050b
--- /dev/null
+++ b/crc8.asm
@@ -0,0 +1,45 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2018 Aleksander Mazur
+;
+; Obliczanie CRC-8 zgodnie z 1-wire
+
+;-----------------------------------------------------------
+; uaktualnia CRC8 w zmiennej CRC bajtem z akumulatora
+; niszczy C, R7
+; używa stosu
+; zajmuje 19 bajtów, jeśli CRC jest rejestrem Rx
+; w innym wypadku 22 bajty
+do_CRC8:
+ ; mamy 8 bitów do przeliczenia
+ mov R7, #8
+do_CRC8_loop:
+ push ACC
+ ; C = (x ^ CRC) & 1;
+ xrl A, CRC
+ rrc A
+ ; CRC = (C << 7) | ((C ? CRC ^ 0x18 : CRC) >> 1);
+ mov A, CRC
+ jnc do_CRC8_zero
+ xrl A, #18h
+do_CRC8_zero:
+ rrc A
+ mov CRC, A
+ ; x >>= 1;
+ pop ACC
+ rr A
+ djnz R7, do_CRC8_loop
+ ret
diff --git a/firmware_5V.asm b/firmware_5V.asm
new file mode 100644
index 0000000..ca095ef
--- /dev/null
+++ b/firmware_5V.asm
@@ -0,0 +1,52 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018, 2021, 2022 Aleksander Mazur
+
+$include (header.asm)
+
+; sterowanie konserwatywne - z uwzględnieniem poprzednio wyliczonych masek sterujących przekaźnikami
+CONSERVATIVE_CONTROL equ 1
+CONTROL_NEGATIVE equ 1
+
+; przekaźniki
+RELAY_PORT equ P1
+
+; 1-wire
+OW_PWR equ P3.2 ; One Wire Power Key - 0 włącza zasilanie 1W
+OW_PARASITE equ 21 ; One Wire Parasite Power - jeśli zdefiniowane, to jest to czas pomiaru temp. na 1-wire w cyklach pętli głównej (8/225 s), a 0 na OW_PWR włącza silną jedynkę na linii danych magistrali 1W zamiast +5V na trzecim przewodzie zasilającym urządzenia magistrali 1-wire
+OW_DQ equ P3.7 ; One Wire DQ - linia danych magistrali 1W
+
+; I2C
+SDA equ P3.5
+SCL equ P3.4
+; EEPROM - AT24C02 na I2C
+; adres 000 -> A0, A1
+I2C_EEPROM_WR equ 10100000b
+; czujnik temperatury wewnętrznej - TMP75 na I2C
+; adres 000 -> 90, 91
+I2C_TEMP_WR equ 10010000b
+; wyświetlacz Philips I2C 4*7seg
+;I2C_DISPLAY_WR equ 76h
+
+;SKIP_DS18S20 equ 1
+;SKIP_DS2406 equ 1
+;SKIP_DS2405 equ 1
+;SKIP_DS1821 equ 1
+;SKIP_CTRL_TEMP equ 1
+TUNE_1WIRE equ 1
+;AT89C4051 equ 1 ; mamy 2kB ROMu
+
+$include (main.asm)
diff --git a/firmware_hw5.asm b/firmware_hw5.asm
new file mode 100644
index 0000000..c86ec21
--- /dev/null
+++ b/firmware_hw5.asm
@@ -0,0 +1,51 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018 Aleksander Mazur
+
+$include (header.asm)
+
+; sterowanie konserwatywne - z uwzględnieniem poprzednio wyliczonych masek sterujących przekaźnikami
+CONSERVATIVE_CONTROL equ 1
+
+; przekaźniki
+RELAY_PORT equ P1
+
+; 1-wire
+OW_PWR equ P1.0 ; One Wire Power Key - 0 włącza zasilanie 1W
+OW_PARASITE equ 21 ; One Wire Parasite Power - jeśli zdefiniowane, to jest to czas pomiaru temp. na 1-wire w cyklach pętli głównej (8/225 s), a 0 na OW_PWR włącza silną jedynkę na linii danych magistrali 1W zamiast +5V na trzecim przewodzie zasilającym urządzenia magistrali 1-wire
+OW_DQ equ P3.7 ; One Wire DQ - linia danych magistrali 1W
+
+; I2C
+SDA equ P3.5
+SCL equ P3.4
+; EEPROM - AT24C02 na I2C
+; adres 000 -> A0, A1
+I2C_EEPROM_WR equ 10100000b
+; czujnik temperatury wewnętrznej - TMP75 na I2C
+; adres 000 -> 90, 91
+I2C_TEMP_WR equ 10010000b
+; wyświetlacz Philips I2C 4*7seg
+;I2C_DISPLAY_WR equ 76h
+
+;SKIP_DS18S20 equ 1
+;SKIP_DS2406 equ 1
+;SKIP_DS2405 equ 1
+;SKIP_DS1821 equ 1
+;SKIP_CTRL_TEMP equ 1
+TUNE_1WIRE equ 1
+;AT89C4051 equ 1 ; mamy 2kB ROMu
+
+$include (main.asm)
diff --git a/firmware_hw6.asm b/firmware_hw6.asm
new file mode 100644
index 0000000..385b3bc
--- /dev/null
+++ b/firmware_hw6.asm
@@ -0,0 +1,51 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018, 2021 Aleksander Mazur
+
+$include (header.asm)
+
+; sterowanie konserwatywne - z uwzględnieniem poprzednio wyliczonych masek sterujących przekaźnikami
+CONSERVATIVE_CONTROL equ 1
+
+; przekaźniki
+RELAY_PORT equ P1
+
+; 1-wire
+OW_PWR equ P1.0 ; One Wire Power Key - 1 włącza zasilanie 1W
+;OW_PARASITE equ 21 ; One Wire Parasite Power - jeśli zdefiniowane, to jest to czas pomiaru temp. na 1-wire w cyklach pętli głównej (8/225 s), a 0 na OW_PWR włącza silną jedynkę na linii danych magistrali 1W zamiast +5V na trzecim przewodzie zasilającym urządzenia magistrali 1-wire
+OW_DQ equ P1.1 ; One Wire DQ - linia danych magistrali 1W
+
+; I2C
+SDA equ P1.2
+SCL equ P1.3
+; EEPROM - AT24C02 na I2C
+; adres 000 -> A0, A1
+I2C_EEPROM_WR equ 10100000b
+; czujnik temperatury wewnętrznej - TMP75 na I2C
+; adres 000 -> 90, 91
+I2C_TEMP_WR equ 10010000b
+; wyświetlacz Philips I2C 4*7seg
+;I2C_DISPLAY_WR equ 76h
+
+;SKIP_DS18S20 equ 1
+;SKIP_DS2406 equ 1
+;SKIP_DS2405 equ 1
+;SKIP_DS1821 equ 1
+;SKIP_CTRL_TEMP equ 1
+;TUNE_1WIRE equ 1
+;AT89C4051 equ 1 ; mamy 2kB ROMu
+
+$include (main.asm)
diff --git a/firmware_nomem.asm b/firmware_nomem.asm
new file mode 100644
index 0000000..84b53b2
--- /dev/null
+++ b/firmware_nomem.asm
@@ -0,0 +1,46 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018, 2021, 2022 Aleksander Mazur
+
+$include (header.asm)
+
+; sterowanie konserwatywne - z uwzględnieniem poprzednio wyliczonych masek sterujących przekaźnikami
+CONSERVATIVE_CONTROL equ 1
+CONTROL_NEGATIVE equ 1
+
+; przekaźniki
+RELAY_PORT equ P3 ; P3.7 idzie na bazę BC547
+
+; 1-wire
+OW_PWR equ P3.0 ; One Wire Power Key - 0 włącza zasilanie 1W
+OW_PARASITE equ 21 ; One Wire Parasite Power - jeśli zdefiniowane, to jest to czas pomiaru temp. na 1-wire w cyklach pętli głównej (8/225 s), a 0 na OW_PWR włącza silną jedynkę na linii danych magistrali 1W zamiast +5V na trzecim przewodzie zasilającym urządzenia magistrali 1-wire
+OW_DQ equ P3.1 ; One Wire DQ - linia danych magistrali 1W
+
+;SKIP_DS18S20 equ 1
+SKIP_DS2406 equ 1
+SKIP_DS2405 equ 1
+;SKIP_DS1821 equ 1
+;SKIP_CTRL_TEMP equ 1
+SKIP_UART equ 1
+;TUNE_1WIRE equ 1
+;AT89C4051 equ 1 ; mamy 2kB ROMu
+
+$include (main.asm)
+
+rom_data:
+ db 0,0,0,0
+
+END
diff --git a/firmware_prod4k.asm b/firmware_prod4k.asm
new file mode 100644
index 0000000..28d9ef9
--- /dev/null
+++ b/firmware_prod4k.asm
@@ -0,0 +1,52 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018 Aleksander Mazur
+
+$include (header.asm)
+
+; sterowanie konserwatywne - z uwzględnieniem poprzednio wyliczonych masek sterujących przekaźnikami
+CONSERVATIVE_CONTROL equ 1
+
+; przekaźniki
+RELAY_PORT equ P1
+
+; 1-wire
+OW_PWR equ P1.0 ; One Wire Power Key - 0 włącza zasilanie 1W
+OW_PARASITE equ 21 ; One Wire Parasite Power - jeśli zdefiniowane, to jest to czas pomiaru temp. na 1-wire w cyklach pętli głównej (8/225 s), a 0 na OW_PWR włącza silną jedynkę na linii danych magistrali 1W zamiast +5V na trzecim przewodzie zasilającym urządzenia magistrali 1-wire
+OW_DQ equ P3.7 ; One Wire DQ - linia danych magistrali 1W
+
+; I2C
+SDA equ P3.5
+SCL equ P3.4
+; EEPROM - AT24C02 na I2C
+; adres 000 -> A0, A1
+I2C_EEPROM_WR equ 10100000b
+; czujnik temperatury wewnętrznej - TMP75 na I2C
+; adres 000 -> 90, 91
+I2C_TEMP_WR equ 10010000b
+; wyświetlacz Philips I2C 4*7seg
+I2C_DISPLAY_WR equ 76h
+
+;SKIP_DS18S20 equ 1
+;SKIP_DS2406 equ 1
+;SKIP_DS2405 equ 1
+;SKIP_DS1821 equ 1
+;SKIP_CTRL_TEMP equ 1
+TUNE_1WIRE equ 1
+AT89C4051 equ 1 ; mamy 4kB ROMu
+MATCH_ON_SEARCH_FAILURE equ 1
+
+$include (main.asm)
diff --git a/font.py b/font.py
new file mode 100755
index 0000000..09d2416
--- /dev/null
+++ b/font.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# This file is part of Thermostat Firmware.
+#
+# Thermostat Firmware is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Thermostat Firmware is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Thermostat Firmware. If not, see .
+#
+# Copyright (c) 2013, 2017, 2021 Aleksander Mazur
+#
+# Generator fontu dla wyświetlacza 7-segmentowego
+
+font = \
+'XXX| X|XXX|XXX|X X|XXX|XXX|XXX|XXX|XXX|XXX|X |XXX| X|XXX|XXX|' \
+'X X| X| X| X|X X|X |X | X|X X|X X|X X|X |X | X|X |X |' \
+'X X| X|XXX|XXX|XXX|XXX|XXX| X|XXX|XXX|XXX|XXX|X |XXX|XXX|XXX|' \
+'X X| X|X | X| X| X|X X| X|X X| X|X X|X X|X |X X|X |X |' \
+'XXX| X|XXX|XXX| X|XXX|XXX| X|XXX|XXX|X X|XXX|XXX|XXX|XXX|X |'
+rowlen = 16 * 4
+
+# segmenty:
+# a
+# f b
+# g
+# e c
+# d
+
+# offsety znaków odpowiadających segmentom w foncie powyżej
+seg_ofs = {
+ 'a' : 0*rowlen+1,
+ 'b' : 1*rowlen+2,
+ 'c' : 3*rowlen+2,
+ 'd' : 4*rowlen+1,
+ 'e' : 3*rowlen+0,
+ 'f' : 1*rowlen+0,
+ 'g' : 2*rowlen+1
+}
+
+# przypisanie segmentów do bitów - cecha układu
+# (sposobu podłączenia linii wyświetlacza do portu mikroprocesora)
+map_bit_to_seg = [
+ '.', #bit7
+ 'g', #bit6
+ 'a', #bit5
+ 'f', #bit4
+ 'b', #bit3
+ 'e', #bit2
+ 'c', #bit1
+ 'd' #bit0
+]
+
+#aktywnosc = "10" # segmenty włączane zerem
+aktywnosc = "01" # segmenty włączane jedynką
+
+for i in range(0, 10):
+ x_ofs = i * 4
+ result = ''
+ for m in map_bit_to_seg:
+ if m in seg_ofs:
+ r = aktywnosc[int(font[x_ofs + seg_ofs[m]] != ' ')]
+ else:
+ r = aktywnosc[0]
+ result = result + r
+ print('db\t%sb' % result)
+
+print()
+
+equs = {
+ '.': 'KROPKA',
+ 'g': 'MYSLNIK',
+}
+
+for (seg, equ) in equs.items():
+ try:
+ x = 1 << (7 - map_bit_to_seg.index(seg))
+ print('%s\tequ\t0%02Xh' % (equ, x))
+ except:
+ pass
diff --git a/header.asm b/header.asm
new file mode 100644
index 0000000..cc45943
--- /dev/null
+++ b/header.asm
@@ -0,0 +1,19 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2014 Aleksander Mazur
+
+$nomod51
+$include (89c2051.mcu)
diff --git a/i2c.asm b/i2c.asm
new file mode 100644
index 0000000..810d2be
--- /dev/null
+++ b/i2c.asm
@@ -0,0 +1,102 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2006, 2018, 2022 Aleksander Mazur
+;
+; Obsługa I2C / Two-Wire w trybie MASTER
+;
+; na podstawie "Interfacing AT24CXX Serial EEPROMs with AT89CX051 MCU" firmy Atmel
+
+;-----------------------------------------------------------
+; START na I2C
+; zwraca C=1 jeśli wystąpił błąd
+; niszczy A, C
+i2c_start:
+ setb SCL
+ setb SDA
+ jnb SCL, i2c_error
+ jb SDA, i2c_start_cont
+ ;push ACC
+ mov A, #9
+i2c_reset_loop:
+ clr SCL
+ i2c_delay
+ setb SCL
+ i2c_delay
+ jb SDA, i2c_start_cont_pop
+ djnz ACC, i2c_reset_loop
+ ;pop ACC
+i2c_error:
+ setb C
+ ret
+i2c_start_cont_pop:
+ ;pop ACC
+i2c_start_cont:
+ clr C
+ i2c_delay
+ clr SDA
+ i2c_delay
+i2c_clr_SCL_ret:
+ clr SCL
+ ret
+
+;-----------------------------------------------------------
+; Wysyła bajt z akumulatora na I2C
+; Niszczy A, C, R7
+; Zwraca C=1 w razie błędu, C=0 po sukcesie
+i2c_shout:
+ mov R7, #8
+i2c_shout_bit:
+ rlc A
+ mov SDA, C
+ i2c_delay
+ setb SCL
+ i2c_delay
+ clr SCL
+ djnz R7, i2c_shout_bit
+ setb SDA
+ i2c_delay
+ setb SCL
+ i2c_delay
+ mov C, SDA
+ sjmp i2c_clr_SCL_ret
+
+;-----------------------------------------------------------
+; ACK na I2C
+i2c_ACK:
+ clr SDA
+ i2c_delay
+ setb SCL
+ i2c_delay
+ sjmp i2c_clr_SCL_ret
+
+;-----------------------------------------------------------
+; NAK na I2C
+i2c_NAK:
+ setb SDA
+ i2c_delay
+ setb SCL
+ i2c_delay
+ sjmp i2c_clr_SCL_ret
+
+;-----------------------------------------------------------
+; STOP na I2C
+i2c_stop:
+ clr SDA
+ i2c_delay
+ setb SCL
+ i2c_delay
+ setb SDA
+ ret
diff --git a/i2c_display.asm b/i2c_display.asm
new file mode 100644
index 0000000..85504c4
--- /dev/null
+++ b/i2c_display.asm
@@ -0,0 +1,199 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2021 Aleksander Mazur
+;
+; Obsługa modułu wyświetlacza Philips 3118 108 5218.1
+; SW V1.5 Wk:235.4 BPM T10794V 0 311910334571
+; schemat: https://www.elektroda.pl/rtvforum/topic117391.html
+;
+; adres: 0x76
+; zapis:
+; 0, 20, 59: pokaż godzinę 20:59 z migającym dwukropkiem
+; 1, 20, 59: pokaż godzinę 20:59 ze świecącym dwukropkiem
+; 3, 0xFF, 0xFF, 0xFF, 0xFF: zaświeć segmenty, które mają _jedynkę_
+; 5, 1: zaświeć czerwoną diodę
+; 5, 0: zgaś czerwoną diodę
+; 6, 0: piskaj po 5 razy w kółko
+; 6, 1: piśnij raz krótko
+; 6, 2: piszcz ciągle
+; 8, 0: ustaw jasność na 0% (2.4 mA)
+; 8, 1: ustaw jasność na 25% (27 mA)
+; 8, 2: ustaw jasność na 50% (55 mA)
+; 8, 3: ustaw jasność na 75% (82 mA)
+; 8, 4: ustaw jasność na 100% (104 mA)
+; 9: testuj wyświetlacz (L11.5, potem wszystko świeci i piszczy)
+; (jasność dotyczy też czerwonej diody obok wyświetlacza)
+
+I2C_DISPLAY_TIME equ 0
+I2C_DISPLAY_SEG equ 3
+I2C_DISPLAY_BUZZ equ 6
+I2C_DISPLAY_DIM equ 8
+
+;-----------------------------------------------------------
+; Wysyła na I2C kod fontu reprezentujący cyfrę przekazaną w A.
+; Jeśli R6 = 1, to dodatkowo zapala kropkę dziesiętną po cyfrze.
+; Zwraca C=0 jeśli sukces, C=1 jeśli wystąpił błąd na magistrali I2C.
+; Niszczy A, C, R7
+display_digit:
+ add A, #charset - display_digit_rel
+ movc A, @A + PC ; w momencie sumowania PC pokazuje na display_digit_rel
+display_digit_rel:
+ cjne R6, #1, display_digit_cont
+ orl A, #KROPKA
+display_digit_cont:
+ sjmp i2c_shout
+
+charset:
+$include (font.asm)
+
+;-----------------------------------------------------------
+; Wyświetla temperaturę przekazaną w local_temp_h:local_temp_l
+; o ile w R3 jest właściwy numer funkcji (równy display_func_next).
+; Wtedy też aktualizuje display_func_next i ustawia flag_display_used.
+; local_temp_h - przed przecinkiem
+; local_temp_l - po przecinku
+; w kodzie uzupełnieniowym do dwóch.
+; Niszczy: A, B, C, R6, R7
+display_temperature:
+ mov A, R3
+ cjne A, display_func_next, display_ret
+ setb flag_display_used
+ mov B, #I2C_DISPLAY_SEG
+ bcall display_start
+ jc display_ret
+ ; zaczynamy
+ mov A, local_temp_h
+ jnb ACC.7, display_temp2
+ ; minus
+ mov A, #MYSLNIK
+ bcall i2c_shout
+ jc display_i2c_stop
+ ; negujemy local_temp_h:local_temp_l
+ clr A
+ clr C
+ subb A, local_temp_l
+ push ACC ; zanegowaną część po przecinku odkładamy na stos - tylko, jeśli liczba jest ujemna
+ clr A
+ subb A, local_temp_h
+display_temp2:
+ ; w A jest liczba do wyświetlenia - bez znaku
+ mov R6, #0
+display_temp_loop1:
+ ; odkładamy na stos cyfry rozwinięcia dziesiętnego liczby z A (od końca)
+ mov B, #10
+ div AB
+ push B
+ inc R6
+ jnz display_temp_loop1
+ ; R6 = liczba cyfr odłożonych na stos
+ mov B, R6
+display_temp_loop2:
+ pop ACC
+ bcall display_digit
+ ;jc display_i2c_stop ; stąd nie ma wyjścia - musimy zdjąć ze stosu wszystko odłożone w loop1
+ djnz R6, display_temp_loop2
+ ; R6 = 0
+ ; B = zapamiętana liczba cyfr odłożonych na stos przez loop1
+ ; i zdjętych przez loop2
+ mov R6, B
+ mov B, local_temp_l
+ mov A, local_temp_h
+ jnb ACC.7, display_temp3
+ pop B ; zdejmujemy ze stosu bezwzględną wartość liczby po przecinku
+ inc R6 ; bo wypisaliśmy wcześniej minusa
+display_temp3:
+ ; R6 = liczba użytych cyfr wyświetlacza
+ ; B = liczba po przecinku do wyświetlenia - bez znaku
+display_temp_loop3:
+ cjne R6, #4, display_temp_loop3_cont
+ sjmp display_i2c_stop
+display_temp_loop3_cont:
+ mov A, #10
+ mul AB
+ ; B = cyfra do pokazania
+ ; A = reszta do obsługi w kolejnej iteracji
+ xch A, B
+ inc R6 ; R6 > 1
+ bcall display_digit
+ jc display_i2c_stop ; to możnaby pominąć
+ sjmp display_temp_loop3
+
+;-----------------------------------------------------------
+; Zaczyna gadać z wyświetlaczem: wysyła adres i bajt podany w B
+; Niszczy A, C, R7
+; Jeśli C=0, to się udało i trzeba wysłać następne bajty, a potem stop
+; Jeśli C=1, to wystąpił błąd (i jest już po stopie)
+display_start:
+ bcall i2c_start
+ jc display_ret
+ mov A, #I2C_DISPLAY_WR
+ bcall i2c_shout
+ jc display_i2c_stop ; to możnaby pominąć
+ mov A, B
+ bcall i2c_shout
+ jc display_i2c_stop
+display_ret:
+ ret
+
+;-----------------------------------------------------------
+; Piszczy raz
+; Niszczy A, B, C, R7
+display_beep:
+ mov B, #I2C_DISPLAY_BUZZ
+ bcall display_start
+ jc display_ret
+ mov A, #1
+ bcall i2c_shout
+ sjmp display_i2c_stop
+
+;-----------------------------------------------------------
+; Ustawia jasność wyświetlacza na podaną w A (0-4)
+; Niszczy A, B, C, R7
+; Zwraca C=1 w razie błędu, C=0 po sukcesie
+display_dim:
+ push ACC
+ mov B, #I2C_DISPLAY_DIM
+ bcall display_start
+ pop ACC
+ jc display_ret
+ bcall i2c_shout
+display_i2c_stop:
+ bjmp i2c_stop
+
+;-----------------------------------------------------------
+; Wyświetla zegarek
+; Niszczy A, B, C, R1, R7
+display_clock:
+ mov B, #I2C_DISPLAY_TIME
+ bcall display_start
+ jc display_ret
+ mov R1, #global_rtc_hours
+display_clock_loop:
+ ; @R1 z kodu BCD na zwykłą liczbę
+ mov A, @R1
+ swap A
+ anl A, #00001111b
+ mov B, #10
+ mul AB
+ mov R7, A
+ mov A, @R1
+ anl A, #00001111b
+ add A, R7
+ bcall i2c_shout
+ jc display_i2c_stop
+ inc R1
+ cjne R1, #global_rtc_minutes+1, display_clock_loop
+ bjmp i2c_stop
diff --git a/i2c_eeprom.asm b/i2c_eeprom.asm
new file mode 100644
index 0000000..8121ff1
--- /dev/null
+++ b/i2c_eeprom.asm
@@ -0,0 +1,70 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018 Aleksander Mazur
+;
+; Obsługa pamięci EEPROM na magistrali I2C
+; Bazuje na niskopoziomowych procedurach z i2c.asm
+
+;===========================================================
+; API
+
+;-----------------------------------------------------------
+; Inicuje odczyt z EEPROM spod adresu w B
+; Niszczy A, C, R7
+; Zwraca C=1 jeśli wystąpił błąd; wówczas magistrala I2C jest pozostawiona w stanie wolnym
+; (wołający nie musi już nic z nią robić).
+; Zwraca C=0 po sukcesie; wówczas magistrala I2C pozostaje w stanie wybranego czujnika.
+; Wołający powinien zrobić i2c_read. Następnie:
+; - jeśli chce odczytać następny bajt: i2c_ack i powrót do i2c_read
+; - jeśli chce zakończyć: i2c_nak i i2c_stop
+eeprom_read_start:
+ acall i2c_start
+ jc eeprom_read_from_ret
+ mov A, #I2C_EEPROM_WR
+ acall i2c_shout
+ jc i2c_stop
+ mov A, B
+ acall i2c_shout
+ jc i2c_stop
+ acall i2c_start
+ jc i2c_stop
+ mov A, #(I2C_EEPROM_WR or 1)
+ acall i2c_shout
+ jc i2c_stop
+eeprom_read_from_ret:
+ ret
+
+;-----------------------------------------------------------
+; Kończy odczyt z EEPROM po udanym wywołaniu eeprom_read_start
+; (tj. gdy C=0) i po odczytaniu co najmniej jednego bajtu
+eeprom_read_stop:
+ acall i2c_NAK
+ ajmp i2c_stop
+
+;-----------------------------------------------------------
+; Czyta jeden bajt z EEPROM spod adresu w B
+; Niszczy A, C, R7
+; Zwraca C=1, jeśli wystąpił błąd, a C=0 po sukcesie - wówczas
+; odczytany bajt jest w A.
+; Magistrala I2C jest wolna.
+eeprom_read_byte_at:
+ acall eeprom_read_start
+ jc eeprom_read_byte_at_ret
+ acall i2c_shin
+ acall eeprom_read_stop
+ clr C
+eeprom_read_byte_at_ret:
+ ret
diff --git a/i2c_tmp75.asm b/i2c_tmp75.asm
new file mode 100644
index 0000000..d3aca2b
--- /dev/null
+++ b/i2c_tmp75.asm
@@ -0,0 +1,80 @@
+; This file is part of Thermostat Firmware.
+;
+; Thermostat Firmware is free software: you can redistribute it and/or
+; modify it under the terms of the GNU General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+;
+; Thermostat Firmware is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with Thermostat Firmware. If not, see .
+;
+; Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018 Aleksander Mazur
+
+; Obsługa czujnika temperatury TMP75 na magistrali I2C
+; Bazuje na niskopoziomowych procedurach z i2c.asm
+
+;-----------------------------------------------------------
+; Zleca pomiar temperatury czujnikowi wewnętrznemu (I2C)
+; niszczy A, C, R6, R7
+; Zwraca C=0, jeśli sukces; C=1, jeśli wystąpił błąd
+int_sensor_start_measuring:
+ bcall i2c_start
+ jc int_sensor_start_measuring_ret
+ mov A, #I2C_TEMP_WR
+ bcall i2c_shout
+ jc int_sensor_error
+ ; Pointer Register ma wskazywać na Configuration Register (1)
+ mov A, #1
+ bcall i2c_shout
+ jc int_sensor_error
+ ; wpisujemy:
+ ; OS=1 (One Shot)
+ ; R=11 (Converter Resolution = 12-bit czyli 0.0625 st. C, czas pomiaru 220 ms typ.)
+ ; F=11 (Fault Queue = 6 kolejnych błędów zgłasza alarm - i tak tego nie używamy)
+ ; POL=0 (Polarity: pin alarmu aktywny zerem - i tak go nie używamy)
+ ; TM=0 (Thermostat Mode = Comparator Mode, a nie Interrupt Mode)
+ ; SD=1 (Shut Down po bieżącym pomiarze)
+ mov A, #11111001b
+ bcall i2c_shout
+int_sensor_error:
+ bcall i2c_stop
+int_sensor_start_measuring_ret:
+ ret
+
+;-----------------------------------------------------------
+; Odczytuje wynik pomiaru z czujnika wewnętrznego (I2C)
+; niszczy A, C, local_temp_h, local_temp_l, R6, R7
+; Zwraca C=0, jeśli sukces; C=1, jeśli wystąpił błąd
+; Jeśli C=0, to wynik pomiaru jest zwracany w rejestrach local_temp_h:local_temp_l
+; local_temp_h = część całkowita (przed przecinkiem)
+; local_temp_l = licznik części ułamkowej (mianownik=256)
+int_sensor_read_temp:
+ bcall i2c_start
+ jc int_sensor_read_temp_ret
+ mov A, #I2C_TEMP_WR
+ bcall i2c_shout
+ jc int_sensor_read_temp_stop
+ ; Pointer Register ma wskazywać na Temperature Register (0)
+ clr A
+ bcall i2c_shout
+ jc int_sensor_read_temp_stop
+ bcall i2c_start
+ jc int_sensor_read_temp_stop
+ mov A, #(I2C_TEMP_WR or 1)
+ bcall i2c_shout
+ jc int_sensor_read_temp_stop
+ bcall i2c_shin
+ mov local_temp_h, A ; część całkowita
+ bcall i2c_ACK_shin
+ mov local_temp_l, A ; część ułamkowa
+ bcall i2c_NAK
+ clr C
+int_sensor_read_temp_stop:
+ bcall i2c_stop
+int_sensor_read_temp_ret:
+ ret
diff --git a/img/LED_module.jpg b/img/LED_module.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..11e5bc861f817be14e94ad9cf64a51825f471c97
GIT binary patch
literal 34530
zcmbTe1yo$k)+XGz28ZD87Tn$48n*;@cY*{9-q5(aySoQ>cL+|<1cwB|-@NzU`M&kf
znprbbr>o9$cI~RJy}M6!)p@G=b@g=zfT18GF9U#rf&$1v4#4X!!MwD$jU@meFV6@-
z1iS?_NJIks1;7HJAm}Xup#Fv5E};I6>mg!jC>Q_?QswIgKoWom503y3hlqfHfP{pIjEeIP6$J&A
z01F!(hlG%fl!TC&n4FrGj+~O2ikO(5mw}m$or{Z$jE-NJk3)!+lZ)f65-21jBvces
z{CDs0IVgxJIR3B0Yaaj;5l{~WF_sbljR^&V3H3S%AcM3M4&u=_r~W%YLBqhp!6P6d
zA)`PPnlauy1p^HW0|y5S3(@w6DLHMO*LboKPj%q=XftZi)F+&w(K
zynTE_Lc_j>M?^*?Bqk-Nq^6~36ciQ}mz0*3S2Q#>HMg|3wRa4B8yp%Q866v+UszmP
zURhmR-`(3kI6ON3adLWfeRF$v|M2+q`^~O5JO2~@W!V4EE=-7B(6F#Dun2EI*M-U>81USFGABl=n;}@=}>l`v3HPWz`S4&c0iu9k9(?7RJ4c0FmGfY!m?tXALX(go{zt$9Z-6GE7B!)_
zTLL03zS$#igf=L$?Kny)$4zx)YKTHI1D4#C?@Y$krxbBY*o5{AexY!MR-Bh@0=@H+
zHF@7%o`pEJC(dEmjO}a`xvWw~CtQ!K96pJgSd2%olK*-NA4%J)o!x{nPmpS)C!e4+
zlpBjf7Jz3l+KHA=e^XO7HWP+wy+}GjcJ!NNyx|B43D%u5Wb}0>)KZP2I)HA;e4br3y@keTG
z%(OO<^Z~2qw7jrp9Gn0S{B8Mv?gb}rOH$z-iNEUeGeZfpipKKEcYnSx>k`m9E_{kuqrjuE
zPZj5@hrXW17|cMCbyU)0TmoH3k#x<3w#O_A-Rdzq=o{AYMOIytCgOd5EU9OGzz=R=
zox^FqN)&v&GbPN##HiUIoSOy8+}50?`BH91hEZ}L#;t^^QdA3uRCiDT&GoP|Lqfvf
z&W#uB-M7MHYJWzUR+~QI>hZ0Pc%tA+gE~t0_{?3c-r=hR!%PM*A
z1FbS$pXYLH8{}vAgXBzc{X-pRgc+UF)j??dIK!rG6t>nA9EtsT5_IEouo(&58ZIGs
z`DUn3-)DS%p5)YARk@%jL48&@k6Gc=ARE&WF+n#e1-(zSTe7|`uwk0p76i+VX?
zm@3^9aBm+hh8>86EU-9Q`~}v%Op+KCYNgmv8w!4RrkNZtSK&WTChT8+K$VjQlMh{J
zj*3!#vYu0c$6Qavh3ew58YDh?2FxRe{svtjw^%;|i<~G@w7(vYo#$h&sYL9|xK*#2
zo%GyxmQn3UU^b53_V5#E5KM$XotOb{;++@dVZV0JZ8^E&-+cxgs*q|qh`|`BuX#d=
z0%s*s=;CNvx9=?dNxL70XGGkaMpszkK)&qM{Q`A2rPut0pI{HX^UY(lnn_noO|IZT
zL7U3jyAM~zdfKJw${-fKR>8UjtRu
z!eAKAM1U!^5lM$jjtb9nyxj~}^@*4;a#C)UZ={(i7_7$`HrhXoR9YqFHxlKOz~;6J
zoIfJY+F2kOrZxx?wl!B`6$gM83%utdH-?N_F{k+q36;afD~k3RIe`^Ch_{sk3)C2)
zX6q%2;+>A@Ze@
zpDcvTT@0(wOt~pLKH#>AXm5*Aw!c&GluNbwsi|P0*n%46gEKmC{!q?}Fbcs#HcCl
zLk6@G;&^?Z*d*P
zn4%INRS`H)18$b93SpwAPo_DwKe=d39_U+*=$DERTb7uPoG9-QV}donaop*e@F!=$
zVPx>gzBrW1XRT3EDkLeMuGd7kN>Ix)R!5J=NL8aU3Z5I)x!1^f_%V
zG$k0w@gCS@ng2&9@6fxNp%d3~&h3HRoUk4RHjWB6gFJKLOeg={;l)oKep(v5=-+nC
zsZr>X2EW7FKa*|NG8q=ZEApqPaUdA#0G-M;!nrLEL#<0
zw9mwQH7engA8@Ql+scd%)Niav_Zp8?O&zU>*k9INPyWTOZ@0(6EL(#2g
zpTmisAMWI!`)ReYEVDoOjOb?>7{2_Z8NJ7mKa%O?QV9{3D?e718u}c=d@Adtv{=po
zGPuFjOA|C_XJpX%I{ufSvTojD-`OqRTlln5>GHrFtPp-U0V+vOw0V*pw{8x%q@vga
z{5+GZO9r>19(Swk+g^(jV#Eadlth|?%ddaoU}~j(I^Yqmlm;GdvQJrUqVmI(+OjGE
z+avdsnj5g}VwVuvJ}h^#_^YmIV*fNWE@4NcTTY+DdcIe*?1cG=&
zFolQmLe=N$UkY*WPB@)^zj!Y;hl%?|U4ND&62|zha^-=A3=VpnOnUB9c
z^$JM&?t`_=V`*|l8jZy0&||S5R%vHqS9_AMSw+dWh=s;q%L`7g(Th0s)hV@pa5i|P
z)F5_R_5PW0P@B4tS4iKITWb)Qj)tO`W5q=)SQ6>;RBtpKSuAZ;@CvZ$`kd|4UqcS&
z9y4`zGmiB74A&bx59e}}OboN!>4re3g7dR?&xS=SUER;Y7Ai)M)z`B!RlD9cha}}`
z51Bm~y6cC`1>JZNmfh^pVqQq!8FKGWo-fjFd(K?eZG1+8CuK#cz!{044-=bvot0ap
z+usK2Hml60{amG;XW8D{wOw@zt(2dtW!uR#H#eGmf!}}BaTF*B&7VB)-Km{FoN${q
z^Ry9zQc;yO0<{+Ro9(92t_m&p)8>DK-ja}tr8H5oor!VU^XJ%}lb!7SEL;!N!9;F0
zf!%sI?WmpKrSqTWFje6%!PBi%2}qJB$JaXM#wh}~bW^|QD$F*I6GGKu8rIlE-OG#V
zU_*AB@zo;|Vn6O-;)XAFeM+t}w{z&&;2u0XDE_+R>}J%{9#{RKI(PlnmTaB$A=mz*4cBVcR+A{PV4&vEcD&s_5a8H-
zR`Y9XqCYk|5y)%KcRI!N9WC0xjbg|pV^Z2w(3#DrWPKjgK2*#KcR}_7@s$xZxE_Z1N~14hZDP9OeS+Ow
zO;f(vt|ZR`TxyBk^zH89w$#Ux-6>zXsJl+5M_H3rkQTETK75zb6#rtVXysNwD*tKG
zR{HTQL(ZWY$u!fPM>W5_&*2@<43JcD
zj7)#e{ZB-}A~|>rxdy3Lp|`p9ikWBs0=b@iKc`tQR@KTlsrWO+^or2wT#j8^A;M2K
z+)eL>aV8e{HC%uvU~%q|XLvxm&0x_|G9Iat_h@1eEab!|-HN~SQ02n6?pjG8xGwOQ
zGI0es34^ucma!bJ-_vANZ^%(b0#((XB<(vM?n(vu$*GH}Ga)A+j(W(V&|pe8B3k2e
z{|#SVZmVB~cVjd1u{|^!@mOMj%V;<&XC+FTaEAMm66IY%3Q*E>1G;b#3N=y
z1$19&Ug9^Uk#G(HnwPM1`<34(rY!D=N%Q#`(5mYgt+ODgq9#NB>
zFUayy$ZU^MGe1hBt!54jva?I4HDPJv6Sg(8){TLNb5}npL*t2R
zzDHaZ2~Hj{Ls0$L(7rYMQ2p@&7lx>eM`0=kXuj~*IH?Bj{U0spnD!|iWlNsZ$$;i$
zH(SZ9EG;Mc;~X17thH9snlCx;Yz({y_SRsXXDC0|9wlJLZXVAEy%)0buo7_2Xi6UQ
z{G1K+K2~G;+L0DALR+R-6KT$&*dFJXV@Fb85<50UDziUIt`WxQ>=mPdC2w!b+Dt%)
z7)a>r4RR+k;{XT%mNHoQ_POcS`bKaIPo>&sv7IQqkoq
zTVMUd^gyRR2D3n{P5NPk<qa8q-xla>IfP68Ev47^g6QX{tW&XB;#)PbX5`4tcD^b*ebI7}hlP
zUeU+LDfp{Iq<8p_q~o;X_f=x--T-S-TCUi&r)9Y|tjDpnobhoG?yEl=tnMm-5G!Pa
zN9b^d)w;~0ls9=kOqHk01CH{s;7xiNAUO0BqZ#6JJz;f8D#`AelYI<
z5xJ-Ji_GweS7fbN7h#}-@%D4btNTilMu(IZD5k}~=Lj>Pz=)eGBJd9>{0cZfw6#um
z87oz0j2Im)LSXX8z~WX%ikj=CD55O`bu$p~mPMgI*gACjlwFNdX36qTQDC9|a+$KW
zxwjKcCrdVNPBS36(f7rPy5Q1i--!7_L_9ly=UU$d)(eFylqM}{xMQ-D3fyGlKZTlR
zysDQs-G>Ebf^3Ty8DLG&c*zvx)*B3q0eP&O<{2h&WkU+@sk5#bx-Dn2jqimeGjDI+s%+V2Nss0uS-Yqa{@hJ(17hrFR%
z9E)_%qr8NW+jm#B?H<(^F+<|5t=!SxtM>4sAJ(7pm1|3Y#$_c9Uy{CaG-P4wA7-d1
zdQ^Ne_#;ManYI8gY@5H|RyKAv1yk>1Xq5U-{!w=J`X
zwkNbf++~KWT|z0NUF_eL9$`m=n-UW3yN)fSiNlN=~O-POX3nC)_5=-jRfR5eJVBjAeE#9TqCH1$p?=2bhCGxhhb
zAe4#v9RX*hiLj`nNEUR&h}1*&Fh#jUZGn;|>-k*9>+IVSklgqRK;(Sr5q+3q_{A%F
z^*HhuM&U=TWsHeGK8bws-}zY8E!%Ywcr60n*jIqxY!7nGU1#8*qv-t4T&69hXd?Ro
z9x8UX7J}mN9rZib=+E7vhYCVScH2j)B7UXzTxTe#;@0Z1en~FGYr#xLA
z;rrvJn2VN{AAQW~pxoGSd0CjqMArjraC>^4)unAm@waR_rEw_08IrSe((La@tba=5
z0Jx>LeiVG6;wC7FGNaMDCKc
zCI6`R@4qdG`l?QYvK++#kbhvL`2$fVq%|%H`!fttrw;$Q9R1QSjT4mny7syUT_-Oo
z`B6*p@tdGY2Vq7jC1&RA>JGu5AehI?-T4itK`@S)
zwTU?dS3xkdE2M!Cy!s|L{TDue!&d)bXb1%0xN2%hLTtl;4OyzhS>O>|pN!Y1=>Hjlw~-aMS`q&eV`d43GuL1C#-30CIpSzyn|d
zum`vUSRiLdh{O$`0m+y6Uz8{Or@R`ZlqsZ?4ZsXiLJHsnZ~&P6Qy%d489;O(;lF(A
zX35F%j|d7?0sw&9eto^60RRvZ0f3kI*VjM!udgo!5Vma-0O)i4ukucL007?|L>~WN
zGU{9a06i1{Xc_!3nQ1Bj&>9W^;IBEGxSIU)K5xVvv?YWAye_3?h?Kl1p
z0si&hWD^Dq21NEK!R1q+wn0E->L0CKPi;sT(m%LGG@y?&Lg>#cRz#Tj1p?Q3SxMeq$~uWyAhdG
ziy#F+nQ~Zx8qAznr7IF?V`KtzD$OG=H7bUID84)Ro#2aMXJ|VctOtswR2lz|&1JHU
z={r`c7~LF9dvED{GeuGaDY<3zkcLep4DwVN2}H@h8zgZtu`zjaeWzGC12S=j2|Dwy
zza7;HWYp+UYZ#4*HiAzC(4e5bWr9~FsNoG{f?>nOiqvS-E78%~bMDRIq$>H!L
z8Iu7HSm2WT3DHXFaG5K_%Lw(N;YY-=p1wD}Kf$65`cyB1fI1igH|s1%>4DCH9Hb@6
zoS-qHR!1*@FhLVnB#8tzBS(^zCC`n&Q7N%+{kqT0sas8bY{u|(Q%lM|#}Ep0Fcu^U
zivU}{$cNC%mE4~6%T`Y;j~o#;?<)aSp@N3e9yPKDB{mxluUbh;`e{m}YfW|@;cA|<#@ILwr67v?Cx5iYU@)BAK1qrz-@w`$iex`+`7d)6
z*r3y{kVF^5Q}%usUOFf!igQwf
z=pE)y%I{y)V6`+sF_UViq=e&VDlM*lFlx8W5q&%wlSAeQb5uUY{i%j3B;1!Ze)(2=;c`tswtTx&B%i$_xHEQ=o7*9!G{b*RV
z%FLO$f2^ZDAN@)MX*Eg+?r#{pBREk}WaKPn^)7{(8aivTY?@FcoK?`7w*RRsNOMBw
z`ZC5S8834c9$HG~d7>q85;L~+&LO+iVS~PdI#gp?pFc5`lRvLq{LHp{%FRXt#&c+8
z#dc1JE+H#}l4cA{9byZ8OS-D$Pl^zt`i-eZD=)JmoKW{vUdu+^E{EO9JKeX#txzAT
zCs5_W<-M)Acql;*^==490c-I3b~(2hTZHTEgLcYKsqwSK27RBFK*sKAusT(HEr?vA
zd5~}I#PB+DjM}K1W!5yJq2K~ZSD7`CHtKwO%I);Ku;X!2=)qtFkRp{|cZc(fZBQpg
zp|K?er$sexwcG5pSuL8mg7Y06;p)|oimE~!IdM#m1thtzd?80N`Qwe`H&nOpWa-a_
z#f~kk-+3PYw7<9zXWeSzWEf(^te>Gu6?{L_=U}@(KjzEHagL@)WQR~?+#uEd*OS&s
zb@&{AN^Ob(U6~aW>7cmNk#|=!d)kh-MjbIlE^WcU_r(UuU(>3^+DWF=eU6vGH5&oV
z?B>YviHIiZBy^!_e9=YUc^b&g)Dm`LS-3WM96f^lfQL-TO?6}2;TBz1;(3J2s&SSqpo4CYuk2&pyug6O=V-OY<<661*>27d
zzc`!k*cDjBU#a&8BZj;pJhZBP;j86|B5o_Wm?Cry49iJX0&7Hwj-E5`rmInI*3Q1i
z#UT9PZvAV?u=S7}uu*&ZJ?8qk`|iRqFn2=p#y7zFH(syyUm|Ag^&o%co|I?z=b=B~
zOa%HG)`EtPS3u>AayRE)&%2eB@l1+?B&x&qtV{*gs9}&^Pl2%(czw8HK83b+;NEeV7fOZ2tvzC63-ov3>%Jr{oXK7PI4H5B0w(|8(}
zd=0bY6l0Ntd0QP$5tU*(&Sj=pvb^^DJM8Iz>$^Dt4vo!@(Vz0L2Oo}yBG-XWD$Qkfl3)ACn^|AhJOT^&d
zBsWp!;r4KQ(j-^^jyXVtsCJys-uT$)ePYb_>#pua!}C!Be9Jg@Bu-3xOl_osvq!W3mu#WBtCiXlm3Zo^HD#5X;x4>%BHmcO
zovxgVF-}WR8Y@37)=)5kmcWRxK+>=;ZXUx+4H_02*<@M4ayWQ0pGq@~dKX479&xAo
zQ81akd42r6Q04K1rVU2dLJFAiIC-o6u4c2}F1HqmKQpq^0*eB8gP5wLyO_c<8BZrA
z;ZI}f32czp3h27!Q#Wn*P?rgpPRT_|*4XJ*6TS`uk{L&;Z)t;KuuH5ql|ws}U<+y$
zty3$|IT{&yNGM|S_K*l7N{6|^rsw6wo4U|31qHTl?tYIFU4Od07Rn`3FO4
zX&?vjIqZ{Z!})Tk|IjHcD6QO|W=*P!Qf>*iPDxUVj4iM{=ny(#dDPHY#)%k6aHEtC
z{xH1xLVnZ@5Td3p3+t$oUJKArh(VQobL0m?fUajz_IGG#fGV$#TSxzp_y_+!0
z;HE<=CD$L-1l!^M9N?MfX$NVU2knx$jCx`MmH{bzhI!i8^YB8;Bo-gP8lA5M9iuZ7B8vK=vp-?G_%}R{XG0AneY?~I|y{>LAnw+C>u9*sbfjFV4*DirqtG+
zR-h|E^lQUR2@!lcp`t0Tak@|wnE}pl7g{&Ct!eB(8c{Av+9TOq5lT-~s{2d?
zpP?u(zuaLEfg|UXMENnSpW2}$oVlKSz=&|zJ(L0{UE1_*&0>u9R3ID*+)m-ntYRfy
zDyoN?3mMz7LY8hpL%~A9zNL_LL;o7u0pQ>su9R7*wAVwMm$DZba&)M4lAdUkhliq=W+MaN
zv+@al^@Hwq4oqnC-shWcU!aK2!VL}!??LwYs47bqBaNUEp0Vx_kQ<{Eu5eDdV7jAT
zMRpaRq>%i`Mb4LDC{z`?&Bxzt7oN`}KXf)3<
zJ0XyinQJ|xrpTcgs}_S_7?XDM3W&Rj3dbcPt@w~5evQUCaSL>aXjOtCN3qer|D`vql4$%ri`bhnAA88j3VwF_zTj`3b
zbJ(!>%nfztv}miBLKy|`k;mCdwQZ=<2&3a}M>q0KE9M3#&j@IsN9ZCvbblk}A7c0y
zCiTtg#ZA$ajeqvczOy)~SqleW$Vi1($Z$}fYVou5Gp13n#4iz0g#t~*gC{E^eUcQ>mea|UgS{`c&VsfE#fTY@yJRrPFQ~0**;_v(I3
z*5nWvq4uG6Ez!E>aW%IdBcP;|!8u_2cJY9P$h{WMzs_8X&j$PS3V;hhI+ZBnH?A}X
zm7!L+)t_zbs?v*a(AgK4O~9m8o;DxU(s-$S!{bSXmL2jYWuqFMp+<7cy27gSBp@3-
z{PN`$03b;bI3_Uo+RRl0Qv4IH6yz5-5}J22=168uo|fj777IwOHj
z#u%gBn0s*uf-gt~gUXsR?|H&&pDK3#lEp$(2<@
z$R^t6AkK-({3;@x@=-tgXE2WS?(e1=FxvMY(^ULK7|BfF9);&R$oLV(hNb1wl+M=XOYix>&&RZJ;yLh&if6|M(ZPhPg>f9Vz>xu&1
zWBIJ%)x*zlY=4--;9*X3!)dk(D`o~TP#JL#I-xh4vF2>{>wkocu75mC`sRzwQSgq}
z$)@E-Z=+s)#%A#@_I(9zGl$GaH3{J>iHuyh>QUF~wQU8nP4axET-#mkT=wO19B{Eb
zd0HY7Q*E@fZF5s&t<&d@W|H!H(@yjFm8NWq!P)Fh~&@!{W}J_pEccno-%6~
zw_!7xDpi*m&2`kWp(UkaIyr+HtE1)K4GNC)Gzf8w+B83c3s6jn1(H@-CK#hbVJf_@
z{Uzi%`mZDMf8q*sYg?XDoD4rErFI3DZs57t^1K2x{9vs56A*3L#I|{0jG-b#@g6-imf57sv
z>47?iEzIX223ZdiEET1ucxuj2M--L_c)QY^X7|kg3ZtSn`dxnU-K!GQMYWZgzZfne
zMQ+BrkALq1JX|r}@0P_ZsA@LQo8Wzrsm+3_-#q$VWl3Y$b5y5+#!=yO|^1y(_?!JPmAMA_2c9IWw_n>
z8uz1zut-g|DQ>k;ilBGMMajMQ3wTz{CVzTB;&ySD(D6DpN|_HS-C
zXsIk1re5^Qi2BS=l=cdkcaW#FVA`*aG_3mevqPuK>lLsw@%Jb<@H>-VPPgI*Gh+0W
z;i>pRH?1>bt)QC?>G(bkC{)T#PFS5tQ<(*XD@lwiUPsSG>QnuiLj9VBo&f{nMEtz&
z)8U$yddzRW>g9{IrM{76f4T!Db(LRM+8ARS85OR5{jN?jMk@5rdAE5`M8=mZdp1bZ
zhI|$lqJC@klnTB0VDI=LQfJ>zAzK0&TdLqnx=QLl$8BMFL5Z3$0)#XAYPMOKGp5hQQqQ0x;s
z;pd5yk02JJMM)OOI<{PsDlv_(lZytMdbbTY4sJ_YnQHM4Q<|;COQIx_{Yrd6
zJ+)vOf6H=V+V)hRg5|4aWZ~vW4L+F@1{o&IWKsFIidj_hi7b!&c_@j=RNa8kq<`&cLF|TC1IIFa>8SC
zZO1c%tf%eXx6Q;JfnP@a7GXj66FDm#tXXI_3&>^MQX1UeAtb&~v28$Xqup14a}HC{)a~QMTz+Cw
z*dzCh_udtQrRApNZ)u-eXBQhBR{6i@*}+tz0u%w0DMC6sRudjZZvATo3th3KkvDiT
z>1d!rx&=)1{m%CbJ*-RS?~FBtk(hL|ygM)0%vS%3iwj5Y|D)z4pjjVQkX+
zf0-4;l54so(sB)VaQ6wR;mo5rrVEsvZ^TOPe)<_KoO4dm|5fe9%x5VYHk>a-2lxAsZu06oUXxgGiZ8(RWbx#!1UvePv7_gg^SSQ#bU%(A{N
z^8hZ@uhYE(%<8(0BxQvbn@t7&`VT#3w;Bi^-`hWG>i+)XO!|b4%Xo<%kIiw7+@h<&
zc}cw#u*f*F-5saIEyyXICkw9(j4aUBy21u^i`&Nzd!E}y$k@CBB;Td}upa=9T@n0|
znHWrjBkan)Hu)Cpl7xXU`vKpe#`?p8wr?{{%5d7b2Mq}J6h#0tR|w`hs}48}dyI2O
z|JVR+=I*M6rVHH4hibM%Zsyv(QmG1^Fl^_iLpAL
ziETd|<0YPMuybqq{A57h&1~TXcRMp<%$FIWj?Z0L1nEHS<1}0gE70oo6+cPw%6!BE8S~!EQyIz7WFfZ5b`}
z3-y7`s0nWuongfKk7q
zmBW09^~K%H(k(@9)sHrw>Daiu6XdZ18#DOW;;d(xv^4i>(Wu`ZLeIuIX3xf%Z*yi|
z2y^nb-|20`6FkB{qzDvbzYQz~7C8kwwzxVR4keqI2^9w?E|(h61x(E?5d_&E^v~oO
zvfoG)>P7gcMT%}q!vu4~ghh(idatxUk6vK9G;YUb*b_>@LwD+lQ#oGmQ0*Oe$B%~>
ztQVc$N3)yGv!2Qq0cL|gm%p9tel*-0X&c`guPj)k=zV5+dHCGIk7iZoMtQy`@-4pP
z0|~P$f7Y}t6?pVO&kB;dk&0Ui0~hfmQ0nTR6V4~_W_$>!`Ll*N--rB7Fh;_>lpRsl
zOW_iZ2TKzx(0^8&os;#iZHMoQqw5zHWKB=Ucv!Ghc8Sk5bu}K037phk)E>=sDU67Z
zI*AAO-Oec8rk|jbPz(;0tA*pmwT0IeZBLX)?eS%)KnD@@Yp8%85j6f}h*^e}Q3d5o9!MjM6)r{7wBz>fuH!0;b+
zAW6G7AJ$F(%$G#dHJpw!$Wq+skN43iHjx0DNYbj4mO~|sUCwBl#t}TYLyx0wmhi!r
z{xc-kDymAbfSKvLB>HE2E$=mr89yWVUEA
zm6B@C!s>K`+TQqSOhMbmgS6r2v4c#BF~#2_zLTO}JfPqR`pNVZkA3R!XH~8j6%1{Y
zu~UiR6RatL!;5bGU*+hq8d&QzvD?c$3RGXNP%!c5jBq&!l4U
zCN5Mxny#ccZmW?rQ62x@(n!RSWQ|3(7A(<)#eBq&l}=K)U+!g9G-j4xJ7!r+hia)z
z-1M|_c=-WdNz|ue_g5_8F2CfCy|>5_*7!>-)+-=9veLn<7HsmzU&`!SkN#!2aN(Y-
zPWM`c>7c;dM3ioWX1Di&CbDWaw!uYFyK*P(K9q=$gULE|A=Yc%KJ}nct=+bM=s8!z
zui1O=Gnwk&tK>%21d*!mm*x|s=@YsUPQ
z$N1;9AWoFD`e?($fh#}91hWOw+?o2B9)WJet#JNogCsReSwZXDO|A#Il_w8)+;BoC
zn4Rp1=qNeaqx&(Rw$7vR$1ty1%Tas2&d>^T9aNVR%Z*Oktrklc3k+-iG+E7G24UK#nh;yD
z_hyu%v4v@cm~RPwkU9Oyb?i}XmE9QpOsadZZ(bkddZ-F^~?}nUKHv`#NKTHBsWQ~K0*E`4L>WF)s4wTBNEwJX606@{DErJ
z@9~iS6(B>uV>U;|?3zX9PJiud$|iJk8J?h1dh3=hjkX!GHI0zu?ALyCsLJ((9oc_f
ztdPv)y4lKb#Gl-*Vg;wLS?s7$tYV~Ai#t{#W0s)K=p*bx&meY?ByUiR9Vspmh6pR+
z64tINs;Wt}1iTpKN8@D>D-bv^)hyS@@-g^Q2&aY2h9R3!-I?_=MX&A(5|8YcDiHeJU=jFBM0Dc2}gD6`@yR?WI~#`d96u(h-BmKMmOgH<0aH%Pf_ca$C*LRBRP
zP7qH^%Rm9sHPW;=LW0Yr=qO96%QadVq{_u5D4^nf;OSC;N%ZPL!63qn!#z4-{?Vj;
z#3d;2FXe-jr2z6xi5MK5>Tw?nTs}U;yfyJ?TV|y5Fr>Dm5+!;=(0upoF83Nrs_g*9eG%o0P(Xt)e
zD;tl`xWa*2Re^Zzw&k^UIN6ULn-aUm)YXB&d%HNhu5$eq%)e(f^b4h3527^1-y1a=
z9lrT0xO~YT(gGDQaOCb@Zgd_M!yamvo0dB7{ZPL6(vr8T)>2&t@i8<^QnL(ldtkU+
zyRlx+<#tWp{!VyR7Z6yoyNTD-V&FF&SaMD6Z
zMB`ixC9BrHv5K#Q{_EtuAMR3HrS%8ZriTi=l%sf>kXu8bOe_oA*Cr%#CL#fzEP45)
z7yNGQa+Fw!`_5kajlb{Tp`*(di75Q-4Y5*(Jb6@avSVg8I5kH9!-rV0IFT$s=jvpS
zDnlW`Pe7Me{i)4niRU5o&nYgKfTz;^MDLyM0+$2+w;%8EbV?<<8*^6$w4+;rD@NXP
z4}hTPvm+Z~snfggEjM3Q>=Na|OY~t`C3ihcqPA^h!L*r?jwSXFPlcGrlzO0m$9_5Bh6*IdFKiYgUxiUUV54Kv)$d&DG
z-oEI-E!AwP&3jo(Hte--8f^mW^FH4(1v7MHXN$gNKc)DJQ3@<4<~yIP4VCkkms!>s@ZEIK
zehoSBl()5WT({IP?oNDj{o78Yjv{F6oVwCef*_euv;2TUf*GaX
z+8A5SnqYmYzS3F@b^J}<*MES|SZaY(W~zBTo=m^z#^?f(YD8+ZvW#oy2aTdDp?1-W
z(Z;d*w8C|fTaCuVpc134CDY%j6>b`BU@{b@up{HpkM{SKQ79zy6NsoEdRgE*WuT#X
z9^ts+Xx@-fnXZ0OKfF6glO<^%4{k|@lYk^m)nVQi-ge)N30GU-njjQj3OfNvGPn@!
z1_6xBNnAY0G9+&hl~7cmkWf@kpP0@B`N0r+W4{29C0Ou~6;;sx4CUWO@{qqRfW@E`
z$L3In<1}#z0^?9gxF(Qm%y9m^t%;{1b;CLQ`o^GwW2>!f(Z
zxUqYTg%kJ0k4iSfihH~l?_U8Cy8;H)<2ZIX#kGG72n)(qm(EsO+X|_pcpE>sED#@9
zP69U;^++<4)?C0DlS5RlG0V)J9c$T_T=BPjRft}aeiswjJqARCnVEP`G%2~YjQN8`
z?-o^tzQDq`2CoKM8(5ML=7a<>@0Yj>Cd
zN}4gpqSR<=ltf4R_)w?loZ5z2bSDTofW0^FMOjV_hlF_Ow)N4_2&>2A77Lly9vG(+
zsbiWgZ8_iLwkPt==2I*zwz3FIh9XRs&;*9fAoOU3DY+0^j~LirMTG&
zHnwEO~8Qu`TrP)uGD4_6tDy*N)BQ99b>-ySXRl7*$JWnPRv1b{Y>sVOB{i>tT1-DhDhtSk
z)Ax@nE1Y?GYtKcaZO-|!t4hk&9<$dLv(9u#?nTMkJsCyo%f?lLzbUX7WVh4j{;c{n
zEg<0A`CK-VOfaw>R^>jM6yNltGCuWJh-94>vmbCaCMIW3S4-PbrrR!Peqv8T)-;Vp
z>FiUQ&JEL<*R`DTI3wGvN^taq%axysxnWb<3a_$$YSy~Pb9QIY`2OIeP7Yy@`?+VX
ze#-NcvkuA443D;IyB-TT|6ZhY06L2=wUC3F(7SfEllb+CPAh9MPr6?IflD`W3UkRO
zEbpdAPC@~{`bW*rAN29(Q%sIvUn4?d4SXa+6fOVHbh2e8#$7zeftjVrrLC_st}N|$
zA|k;cU?FGyviv!-QX_Q7E@x}^?5TFXN9nQ`rOc$1b^Wn^A+D`W`u#EHrz@Yt>nC?#*H=3H6UHw8K+G10XFe+PF
zI}c*7(l*vxt1kziS^r^kwogkJb>O-<^R-u)X`@U)FOKS{sXep7pb%E@ZMk-Mb~GmZ
z%ZfTamP~o#MqZi-vVB%U%s+eGW?Hq{d4`X|7JLY%pF_rQj>vY34$#_%H7wFGE~B?U0MwcI>ydDlhwV45f6
zJGF(GXlN(6-6+r5>xZ!y*Ft4S+EPBBjdD$qr*D$?ngi;k#WU%~8>C<)yk;Ke7pP1G
z^Upj&QUc4*Yw9fNz!37pV?m|rN0`
z)}s2Sal-fGfO1Vvi+kz$^fju-!Wnqodq#eW{rDblPYJu%wu+9G@+)**t0BoIg=5S5
zlm0gWHKX;vByAA4*u$96rsmBodpVp3>p|JA4&x44%Uxb(&c$zkM1(I&^5@cIWepr1
z^%LuFe)zgYkIte)AKkWQc%|Bd4gUdP-N)KW5$%TC%73L9Kx@72+b=~Mur6sKiNWCs
ztmjN24)iz2cBD6}1-I>H>}3aD_?Zg}(~AmtxkV4Oaw*6^=&Bs!eSPF|q_i}XbMqXI
zK-ibYHo9gtbvzDf-3!pqu4ntG(oybI8c65buM{@_>8`&S8q#T5McKIIZ
z>C_fmMc>azRtYP?WOCojcC$~kO$cchDV#7eMlBQkeaNK;$0sF^>gP&7HK^rwTq+$;
zP2!;2&1m-68}f=h3T1~+#V!_%59Ou1uXMMbw;RbRiUd-Scc(|w1t+TSJMldu2gt={
z4QVy%#$b?`m{ebJW^l=1A3WYYIi9&b=egn$KlT%?P+9@wtjwepT%USN*3LYCTRf|(
z);MbNv@m$eyYpsG*fYGrE%&G&m-2BhYS~2F_zlWf(qvb3sV!*Y1;qA=KwMv~eV_0j
zZRox~plFUQq8PJI|MCRCc)lHVC$l?yy^f74O&vU+Gd{zjYI!<0>zWI6FWTk&&
znjLKV7NcYK<&=f_A0SK#iQrW|fkzg%cs;S!|IxZIq&u6B=O?wfYg@+^A3d2}1^Wr`
z;IHG@nJL)u1%qt4*mS%*?Z7uFFD4?Qw#+1I*!AX@TT|+Fi*J!ptBA4Nt$;9L+tuDO
zGn<^C`;P_9!UCSyR~O5W<{W5F1o?r}j5O0Fbcb-EM*4@WZQf?-W1Qw4nJf9e4?XZ>
z{TwT=%rWiQnB-^tkDaYUQzCwyB1HCn>aQhn7?t(!KFV?ni`@X~ga{JHR;|gg-t`nz
z7+sy3x)rm?aW~7W?kA8Yk?|)&W(nu|k+I<VS=4feUAZNQ0DuDDrgI=
zpHm_O7LsB98>iZ)(Y<2ElJ?{x@%Zww>PN6Ch5TQ%=)7XBuzD<+VYvJGo&E%39E@^TM#tvytuFUW(kyxA3CXL~@5{^k4^VAl63>6sT5MI{UuB@q
zC-lWnDbddntijD)lb6Bshif(a*;~lK@l5PlPPc+iC$HX%bnwJZx_a^iD8%ygwq0LZt!V%$DZWvDW~6K?q8w$rjN9dGf+Jv}G<{5(8ND~(EoC{7BF3uWULyi5-%9*mgb
z_m{r8>77QhgCCefcO=)?&-reYM?-j;EeaAsv@r?i4|!~MY5C`}
zdYba(Nl!X>en|%3ljQUW>>$N24^`gU7PL24<(~W8+a%@``xM6%C-mUB8oMG~Gpikn
zmm&Nj{oj{b;~^e%Z>L51igF&>vw7uaYY*)=yR$UlHZ%!}H+KnP?xZ0wD`5fk$BHW@8TVD~wjK@>B_1#BqJc?-
z+Q^;_ZQ?`1KIbQDIKeMeEouVMWN($d(BtNo
zPt$_HVct>xH8MFmxOT&%Xk?0Y7mo9=U;|5p2I5@ABZEt`b%7iNmz+`u_(B6eYX9;mVv(3%m6s~z$s=um{?iZav$?W^Ja1|Yso
zJ~wNntPyH}pvQ=Y5QrVlLU=a|8Cl)S7U_;`x_wL)+||gly>l&IJ7IGA2o2^iTxlcD
zP2D-}^fGW;%Pf*OY7$Q5)$-!09<0i0*5TT*%NWssgV>HR*+xv|Qhjr@e5pv!iGl1N
zo)Ja&PjBQV@o!M8|AxqJa;w*#*kqcT=O_wPBE0G@X~!2(>9N9C4T+oHsF
zvZ^uwHCEiFdvk(DQEvURvwzcdP1wXl??p!-;cDaARgcRl*(efeK=dD#_)+|_=rwP~
z>IKo)F^pcNu*Yy=9>dhbC*Uv^XecTW8U^if(hGdElmk=cuud2n8lbHY`dO1H9JZND
zpYY9idp>i`%$a9FfLspM(@u#=T>F~9%nq#27{{`J0c07Ba@9m}QzH*`ac!O?{AiTM
zzC4IkNCK(W_i6$q3Tpi0J(}=eWHtT-LXGs^$D0K9%PWIDP}$K3Om1QndQ!YK=-SL?
z;W9`3BQ=uzV(OV8K?yQ_i)I7I@NBX~R_fXdQ?pK3z&pbIk5SG%HkRB!wxlLNY8BT~9rgK*=97`6rbCZJ)>cUKcL=-@3qdsr(brxyORGS<
zCDcGK?rn!KCJdEvrV7p8B$e8vTTLhT-!yKkgaoiSD=S?S49nSlN@ZX2-0~JLQaGg|
zW_vmcjuW7qXgHos8nM8a!hG!zHHF&ZC`U07uq;=*CcYUP3|7i$S9!h-^bZ`lSzngy8jfl*Tv|K
z?sHfn%WqV_H_mUvc*Ma)K6^0c5Jv_!dTB(Y{0l7LJHdNR57s
z%3;;3${F7ON>NPCVE-;k<-BOn1#CA!A&zD8Lc^eR_F{n
zX^CJ19@&$~U8csVszhH_m)e23d?wrUNKKQHRm4|E)
zb&cXOs$v4$4r9_C`hj4sAu{}S?1Y!Ar653eU-NJ1n)(E|+Bi5rFz>lwK0LLKvx<7|v8S2Eqt3Kiga|*rpHx5FHZ`FJ!
zq=_F)(ICZ#M&BH8e2Yu+5fD)3{8Lg8{{hNTOWNNZlM_zX3Ft6HpE`AkyzO=W#K#MX
zNBkIxZBB99(E?^*6irlEviz<|^j_z3ADB*n`Hn^4`YgGjx&dC4v4O=}8KgQVdRuG&
zw&ezwc^cJ4J4fpl0P?1_48(Px%2K#4$sM0RKYb)D9IUH0;xNxlWPd8;>Bh>oc!na+
zq)o8j!SUa5s$#moWmqdqwMgypp2uLZ0_%aDsq;?OE@-Sk(mv&px5ecTsa}7mOE*mR
zKERnCo{Ckksh(AF&@>C?4$9@#3Q+0`-~T$!!Mu_I<=N@Qx-S*Ov*XeXD#YA-pqq)ro>
z-xTDA^)bZMe?;|!dt$ZuUu!n;cN44ISIRg+eF-9AzBEVnLTZ$a0ZnCF^-E8!8a-tv
z1OqKhkphj91T^g?S|5&v#=M})ZiBkkWvA6yKtQbwDR>me0{lu{@99fjs`$S?&)t&6Zd1+G6
z!Th!f>hYi%8FsCrYi9G7A6Vhb0K22ox+X5H72VmS
z(H}wrY6=Q>FtOHGaW>HBep;G3e#F~^|E3%so*R`iWuy&
zke(6az&=kdnGT2G3?AXilWeXk?9{OvMLxNj+0EUdp(*hmDdX%7uUBnf#u;w3e~!8p
zfH7p@w8&?$Gut^LM6L<
zzQ?6)(x|VnWk77lP0vV@@Cn5P3PaYcSSCQMF{|>jFVq@_Mmdm1>}dCP{b@65-HPx6
zJEDpE6R
zou^cKnIbz-Bq=CXz8t(;L~TCc>6HFi_7IEOCeGRU@ZK~3O*~P#qns@o3saILchhR$
zx+V>y!**Xy&f*v7cVBUu77h=OJJCH#mXEU3AyX3n_zGzj=nRkBAW*%M?F&gmjnQ&y
zrB9ZMQ`9cnKjxu7t2H#P;zd4DJ!MzH9t(|bu&BdqU$qt@+p$`iIiA!)Ecyo^PoJ;~
z&mNzhu)#n?06tsLf1Px-jxL+CM;T#}9P8HVit9YFWVWWp9yV62LXA5-Cxe7jCw`sg
z&2_8uq(nG&f644A|E11^#GEFEq#QKAf?snhu)QDnqeJ)4=WDw`j5Yc?6myZ%0y8Vl
zIu&P^S0J1Zw5McJi~AU@{;iiDM5f6q_ogTzZE9#iB%h>f#i9qxDQZ3q56ohxwLW*u
zoTYiLwgk=w4ioK_hW1m#p-;p{t73;N!ESHUF%anebOjSI;zq{wn?T=f)y39!=BCah
zlz4%5e(|Bl{m}Kf2{hAxfUmzWnUq*F2pLiPg&+&Nm8PSdiv=5{)0lh^n>Kn3XHlnH
z4&4~vEJ0ScvOEp9f#$>!R}%H;;c#Rzjhs1C4TOGrzC%_-k4j`CM6PZ+ep70iCSGUQ
z`t37LL<>vVb=y4~3n8u%jU*p5_A`%cPF9nmgEfm;4iJuGVohKb7vyX-t08jL@D9Q^
zbuz1XRXIl!sAS17A|g4^bfg}WNgRzHi6wqkT2DdaTm?m8`aVjzcuxCVnJQ*Y)_IuW
z*^Rn2jQ$%JBgZ$==>19;f}O4{eIdr3U&L{hE8d3d37-v|%j)qtGH@>H
zDCH%l4;WtymD@3!!kIpxR|=Iu=&mTly<(%!cfJDuc*~x3ASCnJ2!Ov{E!f8zvov
z;rqjP7>d3@*D&17y2+)V%&R|z`?sG1m>ux8S+r(onhq}Xd}c;h^#hU_ar+I
zyV=O&WzD_x<&t7zH1XjqtPIPokpI5LCz=`_|LzY*Ui$B^eMWDM1xfm#e85%3OT{@?7V0&s^9P
zI`Q_yaifVr?Nzd1tI%-kO9t*jcPL-kfzE!VCU%_YN!K8#o#V&RLGj*zeXr}dIu+F-(~9QzoR2F5o&7AB}4
zI%fqaHJM`#XDe|Pn6RNiK#Cm#Q%V_+<*ar75TbVypD?hhNFU^8;iG`eA+|XEf>h`(
z2;n?a|N7}+00vjjcE4B%NR)S3v3{)iDr2C)J;kR&l296b(VIMKy{VsA{Fvd8?m)%c
z)>iXbsXJf3N#-%7>}H!WIY^WTcQ27J&+)nVq)zm&ZqchA@DB6i%-BVw{krm_{qws1QtP@@
zgH@yCDm8#c7e!*82d@h)G9GX(fl~TGYH4%s
zeV}`~GXH~wI%R-Q=@iDyb;9MVQhgs{(43<+0T+g2l)w`&Z_8l%_hNOO;ng6%#L1UZk5k
zsyistQRXjamiF+Bg76b1obat*51PkMw86-LVr>D;#V{yIhiZ=6e6=N342zKpBOi
z)JpcZERZrw$v~fc`Z;{!#V#`S+n5(nIj`%_(9%#5i%8nmZA%%xvONM3&P%d_AN;<#
zMc%f&2poan3$66`F`~*F%Z+J`;PD0(i;#d1k3+<`*wlkfZ-0h+vh%+xt-c_u_9=OGzryf)1HJK)4^7{w*
zGU4x2MHSLNYS$Gmu&3mkewT$xm1%vSI34MtYsDeoCDv!9#Dn3GE*Mb{TzWlrLYL9m
zB%{V>a{P6E5o&q)UcTr^4ql3zZw>?bvr^%G%`}P
z`H%Y5NK%@6WhHy7Rj<8@&L4HjSEt_TBL?jM&_;0YY3}c~kN@&VFaT_<|MEvv?*vjw
z%i{jk|2Jr2eWsl{JZ;o}_~W8})O)qL-N$j%$wsfrh*93z`gp$A?O_s&o33PMhWWQ
z_lUmyM$t{nwS0oJLu$FHo`-Su6u2#j!Dr(8*j2sVpN!w(xMM2nm@WW2+@He0pVVbT
z@3sWY&H&B@omY+=*;w^ivk3jJ5cbgSX0fg2O>cg)-zWW;8RLxo~d1`prB
zSEQd5b4Cu6**8~WRXf2}VRLYGLTZ?#&eKNC7X!idzrs2|)_dUhN19)TNF0~0Ps}61
zlgo8Q7MZ%s`2`*lsv)&;QtZ(YM*9hDcM7lt#`Kj6h_pr1ckZb94+@+sKYg6TZEOHa
z**V2w_lzBE8aM%+5|oq(9X=`ZMO9Q`PNT=7S79b7+1&kQiv%ae3Xfl3G*TpXyJ$Jk
zF|ZdZZD^4B_s*aW;R7GhWze%wJ?FUtax|gqbZ2Msz)cpXdvdFu3%dLUtHQ*HGfaY5
zlP+4eXgO32gU<5yObPU8WC3AdVv6)Y#+k>b=x-_nvJe*>#*{51Hrzg6aa0-xWk+(z
z{@FAZ*O*n>_P~J_%M$U$mVC=DKeXfq?A2v7+o}N4v!xJOZ%r7_(B0tWh;o^PvmwgM
zZd2Z-o1|Mq2c)$D=PtKJEzZ?
z3mmXVOE4BPw+F7(KsY`l>DZTNMvcGZ=W(T|(lY0LmFT1x_#DIt&h3T9K-4YWj+XqE
zf@7ja1XZNg4_<5nY^huy$G_G&_oIT$(^D8kk{?VGa1qWx57?@C8Fzr7K^KSb_n^Dk
z_K}Dsb^30v?tg$zBSy-tfuSdZ%7?YrO#}zTq1d6jn@du%qJ@6|uHWS40i7}#sg6P&
zqdI
zX&aI1yj_Y7UH@AA{PO8I?H?etD3#>bv%*!%Q1%!V4@|;iP9$IOq?X$HXC3mv-dI?h
zMQnBzN~$Xdg1Ng=X{k-7$ARr*+mZl
zJ;f%1{F~&wb)X@a25c?*v5`bV>9`w$n@V4#uPi!!tx~Z3CIJ5cBvYI>hLn()!ERej
zx~!X_jv>^jsA9$!#gWhFLllBTh$c1h^oNnOX3)29*tRflJ3ylr@~hBD;TTO}`1G-W
zXgc8>kH3UeWJIPr{43co*C2YMw*Gr7iQUbAfCNOYx8-(;z2bj>6^U%`wq_^yH)zPF
zwZ*GeFk;YqH=&92y=n5{xs$$)`!XAC^ue-uLS%<)3OHYL&k!{DshLVhb(CssSUABz
zVMamHE12|ZIGV1N?@av$#_{H5apEqwOD?skauXzM$H>QHa~C+To9F1FXM;rhJD|ln
z+QcTPk~Ri?iC3_tnN-kdx=6qdgm&&k?6`l1QR)$Pu_UB=fe?vfU`;gj9WSZoxs#$h
z%EVAkCZ>5^kAjEKvN?iLfRjd~(|WyEpkGQppctRRQ-K`BR+8dbcyI-6BHAfWLfv0=Na5Gs-jP!C^pxi%yHa
zl2}XKCuEVlYsg>CAhVcQxNtdeB9Pwnm`+ZeJrcK-a^+RCsgT@eH-G0D%KKC};Aq~I
zs^}k5@a`*%kvgIx9Gw&GyPE$#o(_+S9Sd!-fe^}+O+urxvDEtWK;*KjB9_ZZG><73
zU(MH0F$+Yo5N=^QaSLBmFS_25i2na0(YuuKA0^{|k?21_MAfo5a`j&&<3D~>EYruQ
zh=^>bymuBLqY#`31%7ILw+g}m_tMdn^_zjsz33@qMTUmIX>cjzFXGPC3M+1{7!i>=
z=_&A@=jYL$DU@8$vlqQ(OAIWiHpd_hgH~K~Clil@3};WFpTXV?Go>b`*9EA;$)diq
z8K&-|%KLnl51i+cx@6=z^QhS~7rFE`N0RGInQgellWoJev~g@AY*U+FMQ;VV7!k`xWNcc{l00V{)6I
zq^jCYzeziYUjV*X>m>F`a#A8vHP|Iryr^+fWKd??JWSrd6p3-nq{H1zF$&QbvJRBN
z^}YEO_EG6OqEF|gw)YHU95>2QL46Ohq`~$Qdct9n)b@Z8*+-*_hHF+#KHUwg(B84c
zNE)7A`m8Qr-HIzOSK*e{Z+zAr84A1o{{SAnJ0pR^4_4nD5;BGDjcd%!5|3^>iQ|kH&r4=@6HFOqH(+!
zYh0M?kzXX4n1V(N$!llS>SK1J^MQe;&73Pfj40>Bj`F4it&uipQ8^GnrcI=@drrs!
zyelubzK4a}TpwrkFh>4zf}s=KnIlI54JzA#bsT__^%0wAEsc>CFD3P`I{&sO>nsK-
z@Hs`){7M7wz-^%#cyD$2`hYY_Ke>iQmaRQ>N?YRH<9BqLMgV>maX1|qz;3s<0$aCj
zmFlxSSe>%YILb=e%qKZD$t*~^a)Br@-P4NaNJrz1s3^)2+nk%e>Umd-=n7KTVs
zUkD6@VIJ1JUZ&uFy=s1xVEe!k^T*}+0Erdg`9ir#c4zyVD_S@Nq4|C1^&X1dNfanL
zq~HQ?PTq6?xHWkV+{A$?`tT|{^aNv;bG!Yd69R{Vrt_p>K2t(YN0Ze%IkSSO3S37^
zjanGML2&O!uJ(!mMe;h53drcO9+$
z7sB5}0>?1Ixgjz0^AF4!LSnN=*eT!3)B2pnmZ|a8o+k{r#8oZ)VK)`+ooeaRP=;lp=vPkN_+;r{_lheC@&c!C!U0Ibn>C&T{$
zrD0Xlzk3SOG{OjtDrRp({Je-gm9x3`cz9lX9$3jEz5Q_?9Km@|7o0mwiZ2NQ^K~3M
zAuxgN+uM26El9Ejw~z5=`g2#W!Md($F-Gq*p=oHK5%0Cu_Xv;ZiFDSRhk>hI|4VF!
z8mp3rrx(S)j0={*??bTaGt09f8RKsUBz+0NF4qPr!?UB96N;gWtaZu)n<@Wh*T;}nQ7XE;KjZPs%r_`h3x7}i`Z
zy4Yw1(9@s1o|=XBNI}Lf#(karKEs=uI=LkC>8#yf<()cJ+Got7QP-p>k>Kk4_gyaM
zoPw>=5{Pw7*?B447O
z?OAEq?kjI@@cQrA%7)`>G?ZKQD3+jUPu{PR%PVxg;@;W-i$kZTqN!ZAac
z?;YSsx`PQtX>(!m6UI=9)x|Y^9Y@4jrGaRfF*3Um%WSCExhdVwM)_
zKju)CYHrzBP&F}LLEs|7vgd5SCZTH{jMVm?7!)}k^4_{OnYRRF*^R}xPjsDB@P1#h
z#VjN7r@$e01d&f`F8g#pFW0D{JSvrSLCIyleRv!`P7N*U6O4lGT0u1e(tn14qoYn|
zDkzIT1<=$fsIwT7DL8tLNjx-ntFr#hTzL2PE{bcTs#Q~KlX6gHIem4zj5B8VzLn1u
zNR}rr1*`&_hl1X?C4Av5s2R&xV+Gqyh(s_sB~^;m>$vv103<*Jl)Bwq1V}8)MA-#H
z1F0r0p~Wo+04mU)V@6lKVq`C17+l(#d{v}G=N>iWn!XcN2qgurI3>CHk$D|ELu%;qm93fK=L
ze}Zf=_V7AcJQKG4_X9e0sHDLO6D&xu(QTS?m^&lx*htg{=9xxMD6&
zWq|*h`|Q*_-V|Yf1S0?jD=37UEybC{Mq^n7l7`K!Ga8jq?`iR<-;RO&YzQfR(JdNs
zrYX?ixUrwWhDJ#kAev%eNGR9OUrYx&2~&w{JQrrzelR2Mc^=s{h*;s@m*ydgKj7BI
zhcwR{inQ&sP*tKnRWn{75Z+j|$-GTMt-eboGIHXih+0L+k+XHnu_C*aCL0F^Qr0zew#e
zQHQp~no3v~{{j3$HS8mY&N=5{M-FIQ2ok2Jlzf{4oQz?p-fvpcc4(ADNN>T;i%gGE
zV6!jxdY+yIa0Hv10kTbACTW3Wf7tgwFuXRXLm71s(`t1A{nGMywSs|L{hU@~w=7A_
zR+zl(An;n%_Y}@m>fG#?nP2`AO|d$1on@cWNkT(&LAk6d$uPfn>A;&B71w-ZvfF+&
z@k{l~a8~aDr8C1^WeEM2&2XWUCvcuRe*)zqe6!-KReaG({Yh5`6e);$+_rTrfr$X6
z*fW>7T0I&H5;E3^YZ4d59xC_(L&Hl(U8-ImR%a%nmuWim+SpKeW*ObLLxpCXq?a)-
z*opSXIK^pTwfxtbE)Gd5#A$3z9MfjSr3ZyK8U2X8GJ-9g8*Hmg6En&sA>;dRoU;?FEece|_a0kKw62DU0LKkV0}RR7FHO09&9t|$kKy^XtG}^6;5&auGr;r&
z9I@+6fRuUC4G?n1-IBOXeg
zh^3duHTh#6WiR9Dj6wonI7*Iz_v0N~c!FXlmtyu4#!8&`TNUeXC5+a`vI@eVJkhux-?EFbwOT
z6N^9!+%Q;`sO=W@H);X;qR_hc2$7%2;$wAuPl3Vv6c+)EeegeapKsB+L_Kgf
z#iePzS1czU`VhGeRH(ND};3bP|sO;^LRS187{=x6AO
z)r#MqhA!buH;)1z3mMFHkWZ-NH!9V1$jEplnsLiT;%OT`w4EF%j`Q+0{?7d*$(7`8
zeXyQX8#uWlKCt5w=R`Xsf9rjKcmsrJUdSz~M>9MVcbXBdup=S1Gx558U-?8&3ZlNh
zsGkmHBI#fMR3lFN8E0P#fnGq7)?@u7+yH8jI#LWz&u=}3``uJ2j|7{bS}#XZ42teT
zmE-85@A7jVsG64p}@Zz{atE|?lqabL5F|%AvgeK@b@wzNvw4ImBE*GMetAsU)KBU}YfL=Yo
ztm~BfFyXu4beY~PQ3pBWZ@K=+%+bKg6kiENCmDIb@x;&GJwrG6FiEMRECrVtC>|V3
zNj4%`Fj^B#L#CBoSWb_C&0C|$mO)-2&TjU7tLZ7V`^Q9k6~v5+V5CQ!zfb_o+Y#}Z
zilv--1jzDwOU7xAaCe(Fc>868(%WFk&IF~}$dr+r;-RnixA4W4s>J8dRe!+I`s%l6wY=)gc5I)DyT
zD)>Yg!dXecNXL)gi<|Sz1IY7W51X@
zgznKVr9iM7ybYR7z7ZdEYD7fX*DF+n`@U7umNXxU$}g3h?5-Y{(9;oOvd
z?|yU(nvb3Ds45cm&l-n4nViEK-KAkSt{OmR^2855aNBi?4`;q5icwI^f(?x@QDNrM
z?0d|?BAk~$c^@exv|dNx_sga)1Lv@4UWV@}c5E~@vIAN8#9ffhh~`{wIc0b}*uRQK
z5&05f*b=IYNWm%T4zKWFZ6riz!|uZlCsF9`^WpZhs$nT(D$AXr{>d>>oQJu`wnud(
zW>zSJCD)W}&T_*oN-={FOe6f0HQc=!*IVkBs@dIybIjF`*N{XRiJT?jPp%6|_u
z9GK=h&2jti-T4xYbxlZenc^KC`Z$w*Vc4B%{O$5T@`wW`hx30)gK-1^ABE|ub*~Cl
z1O#@7WLkUCUS*prLKZpbh+*sf1`lAaDfTR!>svAB;xr?6aRx_4npi~k2VsZe@}*HG
zigB<_rmeC+tWgSZ0XAshW6G=)k1LuLg3Y-W#te}h?9F4HdLlwghs{o?2oh+U;xy1}
zleR}=TRQfyq_I%OLb9-6JTWMr;6Er~fi01gPD%j19R@t0qQgy6w~`}!w7|*Ag(bmc
z73^!(E
zFV%Q9!`MyXAj>9!9@SY4;!hfa%IdIWXsGyuIN$MDD+`6=A^`Z4WUtTJ>cZp|8d`!h
zgo^DSWfq%T%5M>bt#Y%fAH49`I*-39@{+(Tjj)T*m}R<2zmnZ)LWn-Bnnb+subJo#
z5gL6+f1+ToxP}7!3B}7+%1tGH4}=!liiBR9hkW8Bd(Y{;8f{=pwx_s2&HdoEaIa6N
z%h}E2I7X&{pTrNh)66u__NsWyau{Ij02|h)nR5|AQ-IdQ0aBH%x%`xAWPS(Ja9XgR
zNn4ERF#~I)o}9IX?UiJmuUpgsYi5p#^LI#QLin>p>w#D7NSqB5a}Rt+B}ur{ByG2%
zm8B5H!6<#U9LHF7CzJX>&ooLmQtnq0T*C3Htu7rT%dOhV56y02
z;o!}oyz+`t+0P4jho7MRzD=ovYU;;|C3A&7gv*lY-_P*vwY!7&U;ucht~DK5_J!22
zNy(PiQ+Qhd$(Zf$(2G+-yWRt!HV_8I{#CZ8_FiO^9VL|4gaWBXdySa_e8$qECgJy(
zMXF@x`U!U5Qgis`8nVOxOdDUW)NNT3SVnIg1wU|*B!ikNb6Vrlcl=d$C1dvRA7I9_
zy2R)dKTk*v8wXvy19mRdzIVvA2X9Y}tLukW)0P@T>+^Hel`%=OWmUQf6~a_gwKxK<
z^n;^PnwQO5*qvsJw(oDSovseaH?}B%uCeJ->p1ULx7?~BB?m0h7o=!b4iuKRij7*Q
zxns2)P6_-xO%!{*qgc@;*rDo2-VTpr%&$PfkX*ZNIQSbG>NWI%(_5w>gelMgQ+^dJ
z*vTH2_slgN>0KP=tSDLCY7fw
z;wjA85_r+$`Cj8kVebjy+D%UNRHfZJFF66{@l~wBn0v~)hAISXNdav|J9@^>guKo(O7lq4Ex2JLCL6
zfDq0D?v_ves$Io(d8FME1(H}XB?yHbFu*i-!f?KxljE9EG{L`YMDC+{dHf~>Unfl?
zomk>PWOdPjMt6A@;FLJPgGonrJ|GYq1~J2Q-;(k~y~H?ratwoXAg~^o!Z(f0P}UFB
za_WchEVO1J;A0Ns!3+0WUWdxy628dlu%hTE;Mf5QJ*=Bz!r>pMEWNbXbFvUy0pf3R
z5Jeac5y^J;6t$Ic#V*AfH`39kSJ~=6xIHp^-i6av{o~(|Cz%2&{j4eYU!!dVMq9h($WH~&)1d*&0VGp-b&^p0HM9*i(Qns9piGgvupV6yyNLYDplZ0-y*
zmSz6o7UEke+n*xQg9>&eSw%O(KLvRhI3$6MqXm)h3G+56&?A{p5nSzWoE-V6l*`PqADG!9iwWz}7P*hpJ#sJ%Z{5CaXn7YfU$8&aV^M
zPLkVzsP6~Z9iqFr35Zb?flyWWyJoB`=TY4>!^OuKED|dF=Y(M%L99)pwh%{>Rrd%<
znlTH{mMg>r;xmpF1TH4>B>fDIsADR8rkN85(xfOvz1S>Q1BvSgAqRn3iigI
zeny<`?#IxsQ%WeiJX3H+j`~`D3j&`c^DZgaHjD(j&YD+bjLtuRUJzk?CLT&co}wko
z)Vx0+$#BXHp{aIGf6{EBX={R`^z17G{(-7iDz%=)y8UVB)E9~p{cuAHk4fjFxrINu
z(q*sfDhV$@7C1iDvv-kYdCzMG0gATs40(wbbBT<>4TK)QJKl8-5VE_nP}=*O)TNPE
zOs&dX%BS@FSMINg%3O33nK-IOsDEM><7~#8$Km{`(}M1fX@6-W-(=UcC{=9iDRVBVGHW%NDOk_g~AqqJPbUl4oGuj+U(^}d$(cbWcLyV
zIiskmjNxM3uK9X^MZDX?9*Cv4Ag~8+7APKsV#K$PFriRpOSKrP-{hg37!R~~W596+
zstI02%6KPaTcj3A<*SRtt#q}PLVzNVGNVGP*7)iwDqUjIs5x3oyIX8zduFydq3lek
z1mQHt2T;o43Z_BO$DkWakC}s<#bQ@LIkG)psGHgxh(b`G&QT}8{{VA3M9Rn1%>@I>
zU4%PW0^a53Vy3wt(i?fzYwlY7(=f2s%-s1yD5Gv*4F{?q>etvdJP12~zECLbQ~Qs`e4weLnu8`B*AUu0S7`95yTg)~#c@~yR^9RQ
zP=QCC6)ym25HCpJwMNx@H&cer>PZ24uDdj5`=?{XZ>cpnH$)4F7ps_vUF81&DB9S^
z{{R^DFK_afc<&@FT5$CdG>aSNFVMB-2~Zy7$RYXeR>^LBN9NRa$52mKIS_kd#4j}Q
z7kx%^9)K&N${>G0VM~8=;tXo;5-5YQJu~YV+@(X{T*AG$q9>*KY7D5bNTBZNjt4}t
z3iSq|vxS@t7IyK8Wl1Pu`2@4WYZbHDY^
znprcmYn`g6_SyUN*}JMvmz?Ky`E?8MMovmr3IGEG1CW6(fY)udpORkI765>(ED(SU
z_$!#uf&zE}zyn~Q=x+hQ{0o2o3;&J(R=~o*0pOszAZUSu;=i_tf6ITv`2WU#?OW{kOot!oedTA|a!o
zqCpMX-~7!K94tH>0s=fd)Y>0f4}ix;z@cXUh={9dg7nrIk0Us_7@0<_xd&fu<|i$u
zsY?h7Dghx8F$o<#!#hSG7dHFVw49~c}O9vPjTn_pO5
zT3%V*-r3#TKR7%(KKXTZeRF$v|NG(bZ(K0YgZf|cufYC)abZK_f`x~NgGc%s7YwW?
zw7_A*BT%y=;(Sy^GI7Rz%Mpx>Czf2?+=D{HsrD1!)MW;hfR<~U?$_VY{)z1WEU=LO
zr^x706@U&013h?f*Z@(${;jaQCa&J0ZX}WF{PpcKQwBs@YO`#QVqwkb
zq*=an!t-ve5v+)3#m1HmhneSVe`-)1vm?2YumJ?N)q@N~xi=>2DIR8*#hJsus*K7$
zGfAIIVA<@|FP<=_|8x>fx0ARY)l@}cnf95k2+`P>_~`Zhwvu#F7z0GS55LbI8los5
zYY`n~PDWA4vH2J+5M?I;6m7s6&|}-=5S2QZ
zm7ni%36cBs?niUd8EK+$bTO9O{K3d{RTn=a;YP
z{TOo34Fx}Jykqmflii%kKG(ZkKZthY_*jy+E@pzGUTQg8Y>$>I6hG28LYrVj42~D$kR?*3)#Q8X=2@X&ElXm>J=}0)RUX?y|e7
zW2wYKKV5)vtFh=fWe4R8lucKH#N4Od6+=1SDScLFEv*d6Qj}a|8?Y3+akeAXPYwOU
z`ujUib0$W>PR_DA+iNmU_S5VgMncKYHkZq^5i4aDpE9a@s_5EcX&pl_UDX>ZGFhem
zG9*fhit*xNO(tgCWrS^
zs`U^}y{qho&Uqz1p);%Ftf2rA;akbU-8eUVt;g%}ISDxf8A1m_2szebHf=Iq^5EPF7_=pHR63S~z;>u(U`&IzrR;0N
zaC`%wX3iWM511T6Y17Ae5S4An)aLPJg#|P1%f&X(1>>4?Eo(urqCA-ficqJX~fXOHU<`X2y4hZURm1
zv?sRnZf^{Gx=c`j+WBKW_y)A6Ulx>zWoR)MHgs2C0oF|sTJLeu&lzHhck~G~p72|`
zjWI?;0hlY?V@h!qjfBN(LK-Hlx-3c`l$}@$+-YT2#z0H;H1baO5r?FEcBw;12{tz}
zW>L#V7Tl(Ufnxl`C9-8IcXFHNO``~u{ohn42?||irTADjutd+VFx4pC42s7k4tXH$`%zq-{mD0~fkK>T0
ze7B{Ei21Dap&`-rBbqoGeqvtLrA8$Ze=P!c}
z_P++9`q=W0e)lhLHsr8b&6`nkl=4K|@)Zt3D;k2w(QZ6d;qA+&ll)jiKk!u4H+Ngd
z=e~jiZ_(|@X^X@8qv=>vd(fxMk$$WU*m*kdZ}VPhaIv8)SDO;OQF8Jv1d9~F3s3Z^
zL0`1o65RU~TXo23BO*o(xD0x{CA-!v{heOx9aqiFl8|kre5wK-u=mz^7ej_SU;PRo
znFW$#=jB_}t*q#H;cN~mg}~K_)l*RkiuW}@?>D?IAD%RWf2KwP)}2b`bcIzkP{jt?I26O=cW8C#EbWka}S|C_yrCG3Q%rfGJ{}f}MJ*
zO&u{gN4oDJypomrINudpB8;jl@>XwT`DXYt*4C3ph73JhHc=}!uUbI6*jx_<-s~-9
zhyH045rGL=O6faV>^$c={stjF>^cXmiT?i7cegefH-W+$KwxhVX(01#8a8&d&G`-T*w2N+wpA!?4$3P
zFRrH$PwX42fU`GBfvBaneoIQf`Uta*iIe-ycorx+FV7W!G_&_?m;YP@-(Cre&8g44
z&FZuJRXcRGEq$!8v?CLy%{~Y{H4D{^98LT(mok!9*>g;Qm#F+`1-8?Es$PpZn$;gx
zZ~_UP+vaQXnjO~2yyAFSG?h5MJmwu#P(iN7hB~iE!4&Pm?l^SgN#6&OaAH30N6JO2
zEYWpl*QYy?!CYM@`Q+VIuFHcQz3IX@!_Djw+76>Y^&CK^`>(nx~ym{1E@T;(rMQB!a+{!_&A!IzbHaI6dfaZEZEDochTR#|QlE7mwVwCUf8o
zht4ZGC8sgxw*14D8dSiP)ir-nM>%2|J2eU-hW828Skuff9i5Zgx+62KS1WXR1+=ko
zEp;nhK3;fYxq>V>nk9;EX@fP(e{r{YFM9?#hY>ZH9`s(U9x!<`H^47_awe9`I3EmX
zdzO~{GONbO%Dk`4cTU=4