Project tutorial

Voice Controlled Arduino Smart Home - Without Wires © CC BY-NC

Just say the needed temperature, control your home with voice or Android! Two units on synched Bluetooth link, RFID functions...

  • 3,238 views
  • 2 comments
  • 30 respects

Components and supplies

Apps and online services

About this project

A full home automation Arduino project with voice control - without wires. I designed, built and programmed it in my style, following only my own fantasy. Every each parts are handmade.

Introdution

The system controls the heater gas boiler, the lights in the flat, the airing fan and my media player.

There are two autonomous Arduino-based units communicating on a synchronized Bluetooth link. They connecting and synchronizing each other automatically, so there is no outdated or missing information on the screens.

The Central Unit accepts commands, and switches the proper local appliance or forwards the command to the Thermostat Unit to perform.

The whole system can be locked with RFID card – I designed a fully functional RFID card handler for it.

And yes, the Central Unit talks! Says a greeting message when I connect, what’s more it laughs at me if I try to unblock it with an unlisted RFID card for instance.

Video

Just a click, and you will see and understand everything!

Central Unit

Developed on Arduino / Genuino 2560 board, HC-05 Bluetooth adapters, RF 315/433MHz receiver, IR receiver, MFRC-522 RFID reader and programmer, Elechouse VR3 module, TM1637 display drivers, ST9720 (12864) lcd screen, 4x4 matrix keypad, DS3231 RTC chip, 4ch relay board, ISD1820PY voice chips with LM386 amplifier, active and NE555 controlled buzzers.

The Central Unit accepts Voice, Bluetooth, IR and RF remote and Matrix keypad commands - in order to control the local devices, or forwards the command to the Thermostat unit, and receives working state reports from it - via Synchronized Bluetooth link.

This unit sends working reports via Bluetooth to an Android device, screen prints, and audible reports (Speech and buzzers).

All system settings and information are stored in the EEPROM (RFID cards, locked/unlocked state, device states, alarm clock etc.) .

Thermostat Unit

Developed on Arduino Nano (or higher) board, DS18B20 temperature sensor,
HC-05 Bluetooth adapter, I2C 128X64 bicolor OLED display.

It handles the Heater gas boiler, the bathroom Airing ventilator and the Kitchen lights - switched with relays. Accepts commands from the Central Unit, and sends reports back.

The intelligent Thermostat Routine controls the heater,
with open window-sensing and error detection.

Works in Celsius and Fahrenheit unit (by changing it converts the stored previous temperature values), and calculates natural gas cost with statistics.

This unit is as an advanced version of my earlier published standalone Bluetooth thermostat project.

Android Help Menu

Plenty of commands, hard to keep in mind. Just type „HELP” on your Android phone, and you will get a detailed user’s guide!

Authenticating an Android user

After connecting, the user has to send „hi” first, as "Two-factor authentication" - with the Bluetooth PIN code. Otherwise the user will not receive any information and unable to send commands.

If the user sends anything else instead of 'hi', will be disconnected immediately.

If we send „bye”, the system will also disconnect us, but a voice command „System - Disconnect” will do the same.

RFID Security and System Lock

[check video at 10:10]

The Central unit contains a complete RFID handler function I designed with Card Manager menu („#” button from the main screen) allows to delete and store new tags in the EPPROM memory. If we delete a tag, the auto-sort procedure will follow to keep avoid empty rows in the list.

If we lock the system, all appliances will be turned off, all commands will be ignored - only the thermostat routine will keep the temperature has been set earlier.

Two important features:

  • After a power outage the system stays in locked status.
  • If only one device (Central or Thermostat unit) is locked, and the another is not (for example one is turned off), after the next synch procedure they lock down each other instantly - the locked status, so the security has always the highest priority.

Synchronizing two Arduinos - Cyclic Binary Synchronous Sequences

[check video at 14:15]

Two fully autonomous units has to work together – can be power ON and OFF anytime! If one of these will be switched off suddenly, there is no chance to tell it the another one - would show outdated temperature value and device states - or will miss system events if I power it on later!

So I created the model as I call "Cyclic Binary Synchronous Sequences". My solution can be seen below.

Ways of control

There are function buttons on both unit as well as the Central Unit accepts commands from Android device, Voice commands, IR and RF remote signals.

Using the RF remote;
"A" - Airing, "B" - Kitchen lights, "C" - Media player, "D" - Desk lights

We have IR remote
with direct-device and numeric buttons:

All attached home appliances can be controlled directly, but the system also accepts numeric codes (from matrix keypad, IR remote or Android smartphone); 10..24 or 50..76 are the target temperature values, and there are service codes and control codes.
We can also send acronym messages from smartphone with numeric parameters, like adjust the real time clock; "t1234" - that means 12:34.

Voice control

I programmed a structure handles the Elechouse VR3 module with groups, using this even the typical target temperatures can adjust with voice commands.

Heater control

The heater function has two working ways:

  • One Time Heating. 15 minutes timed mode, useful at spring or autumn evenings
  • Thermostat mode (higher priority). The adjusted target temperature stored in EEPROM. The program detects hardware errors and window opening - in these cases the heater stops and/or will not start.

Cost Calculation and Heating Statistics

After a heating period the Thermostat Unit creates a summary contains the elapsed time in minutes, the temperature difference and the natural gas cost - and sends to the Central Unit via the Bluetooth link. Counts the heating minutes, which is stored in the EEPROM memory, and can be reset manually.

A voice command "System - Cost" or "System - Cost Zero" also handles the calculated cost value.

The cost value is the multiplication of the minutes and the Cost Coefficient.
I determined the U.S. average annual natural gas residential price;
$10.52 / 1000ft^3 (www.eia.gov), 1000ft^3 = 28.31685m^3, and my boiler uses up 1m^3 natural gas in 50 minutes, so the Cost Coefficient equals „10.52 / 28.31685) / 50”.

Error Detection

-127 or -196.60 are HW errors, +85 is typical SW error, but can be fire, or a broken window. A false measured temperature would cause running the gas heater continuously, so in this case all attached home appliances will turned off by the system.

Intelligent Thermostat Routine

Frequent, short-term switching of the heater gas boiler would cut short its lifetime, that's why the system calculates with correction variables. Lower temperature demands greater correction variable, because the walls are colder and adsorb better the warmth from the freshly heated-up air, so the above described effect would more stronger.

Changing Measurement Unit

There are two ways for it:

  • Set the target temperature using Android phone, IR remote or Matrix keypad:
    [10] ... [24] numbers will be accepted as Celsius, or
    [50] ... [76] numbers as Fahrenheit
    The given number will declare the unit of the measurement.
  • Flip measurement unit by pressing the INCR and DECR buttons at the same time. In this case the current target temperature will be converted.

By changing measurement unit, the system converts the previously stored temperature values used for statistics and trend analysis - even during a heating period.

Open Window Detection

If in a measurement cycle the temperature drops (-0.2C/ -0.36F), it will evaluate as a window is open, the heater stops or will not start, and the airing fan will turn on to help refresh the air.

If the temperature stops falling, the air becomes warmer (+0.12C/ +0.216F) due to the heat capacity of the environment, the system will switch back to normal mode.

Real-Time Clock & Alarm Clock functions

I have built a DS3231 RTC chip into the Central Unit, and I coded a routine accepts current and alarm times from a smartphone.

The alarm procedure takes 10 minutes, uses a very loud active buzzer and flashes the desk lights – can be stop by saying „Silence!”, pushing a key or sending a Bluetooth command „aloff”.
The alarm time also stored in the EEPROM memory.

Code

Central UnitArduino
// ---- SMARTHOME ***CENTRAL UNIT*** PROGRAM CODE
// Program code of an intelligent Arduino smarthome system - Central Unit. 
// Based on Arduino / Genuino 2560 board, HC-05 Bluetooth adapters, RF 315/433MHz receiver, IR receiver, MFRC-522 reader and programmer,
// Elechouse VR3 module, TM1637 display drivers, ST9720 (12864) lcd screen, 4x4 matrix keypad, DS3231 RTC chip, 4ch relay board,
// ISD1820PY voice chips with LM386 amplifier, active and NE555 controlled buzzers.
//
// It controls the desklights and mediaplayer (as local devices), and controls the another unit of the system, 
// the Thermostat Unit via Bluetooth. These units connect and synchronize eachoders automatically - so there is no missing or outdated
// information in the system.
//
// The Central Unit accepts Bluetooth (Android for instance), Voice (speech), IR and RF remote, and Keypad commands in order to control 
// the local devices, or forward the command to the Thermostat unit, and receive working state reports from it. Also works as a very 
// effective alarm clock, the whole system can be locked by RFID cards (with a full-function card handler menu). 
// This unit sends working reports via Bluetooth, screen prints, and audible reports (Speech and buzzers).
// All system settings and informations (RFID cards, locked/unlocked state, device states, alarm clock, etc.) are stored in 
// the EEPROM memory.
//
// The another part of the system is the Thermostat Unit, based on Arduino Nano (or higher) board, DS18B20 thermo sensor, 
// HC-05 Bluetooth adapter, I2C 128X64 bicolor OLED display.
// It handles the HEATER gas boiler, the bathroom AIRING ventilator and the kitchen LIGHTING - swithed with relays.
// The heater has two working ways. 1: One Time Heating (15 mins) timed mode, 2: Thermostat mode (higher priority). The adjusted target tempr.
// stored in EEPROM. The program detects hardware errors and window opening - in these cases the heater stops and/or will not start.
// Works in Celsius and Fahrenheit unit (by changing it converts the stored previous temperature values), and calculates natural gas cost.
//
//
// Designed, built and programmed 
// by Gyula Osi.
// All rights reserved.
// ------------------------------------------------------------------------------------------------------------------------------------

// ---- MFRC-522 & System Security
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 53              // RST: 48, MISO: 50, MOSI: 51, SCK: 52, SDA: 53
#define RST_PIN 48
MFRC522 rfid(SS_PIN, RST_PIN); // instance of the class
MFRC522::MIFARE_Key key; 
String strC;                   // collect and store readings
#define arrNUIDSize 4
String strNUID[arrNUIDSize];
char arrNUID[arrNUIDSize][20];           // number of strings & length of each strings
#define lckItems 2
// i -->                             0   1
const int lock_strings[lckItems] = {98, 97};
bool locked;                   // state of syslock
bool cardManager = 0;   
// i -->                    0  1  2  3  4  5  6  7
bool cardBoolSwitches[8] = {1, 1, 0, 0, 0, 0, 0, 0};   
      // ---- Array of Binary Card Operators & Process State Codes
        //    [i] FUNCT 
        //     0  has been seen (temp=1) 
        //     1  get NUID (call at startup)
        //     2  not stored yet 
        //     3  delete
        //     4  already stored 
        //     5  new NUID 
        //     6  invalid type of card 
        //     7  memory full
// i -->                     0  1  2  3
bool delNUID[arrNUIDSize] = {0, 0, 0, 0}; // marked for delete 

// ---- Receive IR
#include <IRremote.h>
const int RECV_PIN = 8;
IRrecv irrecv(RECV_PIN);
decode_results results; 

// ---- Matrix Hexakeys and Buttons
#include <Keypad.h>
const byte matrixR = 4;  // R
const byte matrixC = 4;  // C
char matrixHEXkeys[matrixR][matrixC] = {  //define the cymbols on the buttons of the keypads
// iR/C --> R0C0 R0C1 R0C2 R0C3   R1C0 R1C1 R1C2 R1C3   R2C0 R2C1 R2C2 R2C3   R3C0 R3C1 R3C2 R3C3
           {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'}
  };
byte pinsMatrixR[matrixR] = {40, 42, 44, 46};   //connect to the row pinouts of the keypad
byte pinsMatrixC[matrixC] = {43, 45, 47, 49};   //connect to the column pinouts of the keypad
Keypad customKeypad = Keypad(makeKeymap(matrixHEXkeys), pinsMatrixR, pinsMatrixC, matrixR, matrixC); //initialize an instance of class NewKeypad
int matrixVal;            // stores analog value when button is pressed
int sum = 0;              // to collect Control Codes from Keypad and IR
char sumChar[2];          // to conv in into char for writeout
const byte btnAl = 28;

// ---- RF 315/330/433MHz recv
#include <RCSwitch.h>  
RCSwitch mySwitch = RCSwitch();
const byte pwrRF = 27;
bool rfOn;

// ---- Display TM1637
#include "SevenSegmentTM1637.h"
#include "SevenSegmentExtended.h"
const byte CLK0 = 4; 
const byte DIO0 = 5;
SevenSegmentExtended d0(CLK0, DIO0);
const byte CLK1 = 6;
const byte DIO1 = 7;
SevenSegmentExtended d1(CLK1, DIO1);
char sevenTempr[4];
int sumPrev;

// ---- Display ST7920
#include "U8glib.h"
U8GLIB_ST7920_128X64 u8g(39, 38, 41, U8G_PIN_NONE); // screen constructor13, 12, 9, U8G_PIN_NONE
byte frame = 0;                          
bool prntRTC = 0;

// ---- Internal Serial Communication & State of Remote Appliances                            
bool onlink = 1;                       // 1 in order to send a sync request for binary reports, and try to toggle the Thermostat unit into 
                                       //    onlink mode at startup. After an unsuccesful attempt this bool will turn zero within five seconds. 
const int requestInt = 99;             // request for cyclic binary reports
String recvdStates;                    // store the received report
bool gotReport = 0;                    // the Thermostat unit has been seen since startup of the Central unit
const char expiredReport = 'X';        // the expired report is empty, contains only the preamble
String strTemprRecvd;                  // store the tempr value extracted from the data stream sent by the Thermostat unit

// ---- Memory Management
#include <EEPROM.h>  // memory calls
// i -->                0  1  2  3  4  5  6  7  8  9  10  11  12  13
const byte addr[14] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 22}; 
     // --------  Memory Map  --------
     //
     //     [i] VAL
     //      0  bool locked
     //      1  bool REMOVED FEATURE
     //      2  bool prntRTC
     //      3  bool relayDLState
     //      4  bool relayMPState
     //         ----> byte al[i]  VAL
     //      5  byte al[4]    0   alH0
     //      6  byte al[4]    1   alH1
     //      7  byte al[4]    2   alM0
     //      8  byte al[4]    3   alM1
     //         ----
     //      9  bool alOn
     //     10  bool rfOn
     //     11  bool vrOn
     //     12  byte RFID INIT
     //     13  byte RFID REL
     //
     // ------------------------------
byte pointer = 0;
                                    
// ---- External Serial Communication
#define exRXSize 5
char exRX[exRXSize];             // variable to receive data from the serial port
bool auth = 0;
const byte ledAuth = 26;
bool startup = 1;         // otherwise the self defense routine would run at startup
const byte extBTen = 3;   // ext bt enable

// ---- Local Appliances
const byte relayDL = 9;   // desk lights
bool relayDLState;
const byte relayMP = 12;  // media player
bool relayMPState;

// ---- Strings and Related Features
char inCharFromIntBT;
String strCollectFromIntBT;
#define strSize 6
String str[strSize];
#define functItems 74
// i -->                                0         1                2                    3                   4                   5                     6              7                  8                 9        10                 11               12    13         14                   15                    16                    17                     18               19   20   21                     22                    23        24              25       26   27   28   29   30               31                 32                  33              34                       35                36                37                  38                 39                 40                 41                 42                 43                 44                 45                 46                    47                      48           49       50                 51            52                  53                 54           55           56                    57                  58                 59                 60                 61   62          63                 64                  65                     66                   67                      68              69               70                       71                  72                    73
const String functStr[functItems] = { "READY.", "Voice command", "Bluetooth command", "IR code received", "BT code rejected", "RF signal received", "Key pressed", "Mediaplayer is ", "Desklights is ", "CODE= ", "* DEL | ENTER #", "Central unit ", "un", "locked!", "Desklights is on.", "Desklights is off.", "Mediaplayer is on.", "Mediaplayer is off.", "Temperature= ", ".", "!", "Thermostat Offlink!", "Thermostat Onlink!", "ONLINK", "CARD MANAGER", "del: ", "A", "B", "C", "D", "Card Deleted!", "Already Stored!", "New Card Stored!", "Memory Full!", "Invalid type of Card!", "Card Rejected!", "Card Accepted!", "Authorized User!", "REMOVED FEATURE", "REMOVED FEATURE", "REMOVED FEATURE", "REMOVED FEATURE", "REMOVED FEATURE", "REMOVED FEATURE", "REMOVED FEATURE", "REMOVED FEATURE", "SYSTEM REBOOT SOON", "You may connect now.", "Good bye.", "ERROR", "Clock adjusted.", "Please say", "What temperature", "would you like?", "a system ", "function!", "User disconnected.", "Alarm accepted. ", "Alarm rejected.", "Alarm disabled.", "Alarm enabled. ", ":", "Wake up!", "RF receiver on.", "RF receiver off.", "Target temperature:", "[10]-[24] Celsius", "[50]-[76] Fahrenheit", "VR3 enabled.", "VR3 disabled.", "The system is locked!", "Sending guide...", "No connected user.", "Guide sent." };
#define sevenItems 18
// i -->                               0       1       2       3       4       5       6       7       8       9      10      11      12      13      14      15      16      17
const String sevenStr[sevenItems] = {"    ", "SYS_", "boot", "CArd", "MENU", " dEL", " Add", "A_St", "FULL", "CodE", "----", "LEtS", "PLAY", "Good", " bYE", "Conn", "SCAN", "AL  " };

// ---- System Timers
#include <elapsedMillis.h>         
//                   ----  Countdn tmr for synch request or devalidation of received report            
elapsedMillis timer0;         // 8-bit, PWM timer, used by function elapsedMillis()
#define sysTmrExpired 5000    // timer of cyclic binary report expiration [ms]
//                   ----  Cyclic timers for various system functions as below;
#define cycTmrInterval0 200   // used for RTC call
#define cycTmrInterval1 800   // icon blink tmr
#define cycTmrInterval2 2400  // seven segment timed printouts
#define cycTmrInterval3 5000  // NUID event display
#define cycTmrDim 4
bool cTmrUp[cycTmrDim];               // binary state of each tmrs
unsigned long cTmrPrev[cycTmrDim];    // prev vals for comparison 

// ---- System Management
#include <avr/wdt.h>
uint8_t presc = WDTO_60MS;

// ---- ISD1820PY & Buzz
const byte play0 = 22;
const byte play1 = 23;
const byte LM386 = 25;
// i -->                 0   1
const byte buzzer[2] = {13, 24}; // D13(PWM) & D24

// ---- DS3231
#include <DS3231.h>
DS3231  rtc(SDA, SCL);     // init the DS3231 using the hardware interface
String strTM;
#define RTCArrSize 4
byte tm[RTCArrSize];
byte al[RTCArrSize];
bool alOn;
bool alActivated = 0;      // temp variable, becomes high if al has been turned on, used for serial report
bool alActive = 0;         // temp variable for TM and AL comparison, becomes high if these values match, and
                           //   activates the alarm procedure by turning alCnt into 1.
byte alCnt = 0;            // enables an alarm procedure whitch is longer than one minute, it increases until
                           //   reach the alCntLim limit. One alCnt period equals to cycTmrInterval2, that's why takes 2400ms.
const byte alCntLim = 255;

// ---- VR3
bool vrOn;
const byte ledVrOn = 29;
#include <SoftwareSerial.h>
#include "VoiceRecognitionV3.h" 
VR myVR(10, 11);          // RX, TX
uint8_t record[7];        // save record
uint8_t buf[64]; 
byte group;
#define spkHeater   (0)
#define spkAiring   (1)  
#define spkKitchen  (2)
#define spkMedia    (3)  
#define spkDesklig  (4)
#define spkSys      (5)  
#define spkTempr    (6)
#define spk17       (7) 
#define spk18       (8) 
#define spk19       (9) 
#define spk20      (10) 
#define spk14      (11) 
#define spk58      (12) 
#define spkBack    (13)
#define spkCost    (14) 
#define spkCost0   (15)
#define spkReport  (16) 
#define spkReboot  (17)
#define spkTime    (18) 
#define spkDisc    (19)
#define spkSilence (20)

// ---- Terminators & The Cleanup Crew
const char termCharNull = '0';
const char termChar = 'Z';
const char termCharSpc = ' ';
char termCharEmpty;
const String strTerm;
const int intTerm = 0;
const bool boolTerm = 0;

// ---- Startup
const uint8_t frame1[] U8G_PROGMEM = {
  0xFF, 0x7F, 0x41, 0x10, 0xB8, 0xFF, 0x02, 0xC0, 0x7F, 0x01, 0xC0, 0xBF, 
  0x2F, 0x08, 0x02, 0xFF, 0xFF, 0xBF, 0x40, 0x10, 0xA8, 0xFF, 0x02, 0x80, 
  0x7F, 0x01, 0xE0, 0xBF, 0x17, 0x08, 0x02, 0xFD, 0xFF, 0x7F, 0x41, 0x10, 
  0xB8, 0xFF, 0x02, 0xC0, 0x7F, 0x01, 0xC0, 0xBF, 0x2F, 0x08, 0x02, 0xFF, 
  0xFF, 0xBF, 0x40, 0x10, 0xA8, 0xFF, 0xFE, 0x9F, 0x7F, 0xFF, 0xFF, 0xBF, 
  0x17, 0x08, 0x02, 0xFD, 0xFF, 0x7F, 0x41, 0x10, 0xB8, 0xFF, 0x02, 0xC1, 
  0x7F, 0x81, 0xC0, 0xBF, 0x2F, 0x08, 0x02, 0xFF, 0xFF, 0xBF, 0xFE, 0xFF, 
  0xAF, 0xFF, 0x02, 0x81, 0x7F, 0x81, 0xE0, 0xBF, 0xD7, 0xFF, 0xFF, 0xFD, 
  0xFF, 0x7F, 0x41, 0x10, 0xB8, 0xFF, 0x02, 0xC1, 0x7F, 0x81, 0xC0, 0xBF, 
  0x2F, 0x08, 0x02, 0xFF, 0xFF, 0xBF, 0x40, 0x10, 0xA8, 0xFF, 0x02, 0x81, 
  0x7F, 0x81, 0xE0, 0xBF, 0x17, 0x08, 0x02, 0xFD, 0xFF, 0x7F, 0x41, 0x10, 
  0xB8, 0xFF, 0x02, 0xC1, 0x7F, 0x81, 0xC0, 0xBF, 0x2F, 0x08, 0x02, 0xFF, 
  0xFF, 0xBF, 0x40, 0x10, 0xA8, 0xFF, 0x02, 0x81, 0x7F, 0x81, 0xE0, 0xBF, 
  0x17, 0x08, 0x02, 0xFD, 0xFF, 0x7F, 0x41, 0x10, 0xB8, 0xFF, 0x02, 0xC1, 
  0x7F, 0x81, 0xC0, 0xBF, 0x2F, 0x08, 0x02, 0xFF, 0xFF, 0xBF, 0x40, 0x10, 
  0xA8, 0xFF, 0x02, 0x81, 0x7F, 0x81, 0xE0, 0xBF, 0x17, 0x08, 0x02, 0xFD, 
  0xFF, 0x7F, 0x41, 0x10, 0xB8, 0xFF, 0x02, 0xC1, 0x7F, 0x81, 0xC0, 0xBF, 
  0x2F, 0x08, 0x02, 0xFF, 0xFF, 0xBF, 0x40, 0x10, 0xA8, 0xFF, 0x02, 0x81, 
  0x7F, 0x81, 0xE0, 0xBF, 0x17, 0x08, 0x02, 0xFD, 0xFF, 0x7F, 0x41, 0x10, 
  0xB8, 0xFF, 0xFE, 0xFF, 0x7F, 0xFF, 0xFF, 0xBF, 0x2F, 0x08, 0x02, 0xFF, 
  0xFF, 0xBF, 0x40, 0x10, 0xA8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 
  0x17, 0x08, 0x02, 0xFD, 0xFF, 0x7F, 0x41, 0x10, 0xB8, 0x7F, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0xBF, 0x2F, 0x08, 0x02, 0xFF, 0xFF, 0xBF, 0x40, 0x10, 
  0xA8, 0x7F, 0x00, 0x48, 0x22, 0x09, 0x00, 0xBF, 0x17, 0x08, 0x02, 0xFD, 
  0xFF, 0x7F, 0x41, 0x10, 0xB8, 0x7F, 0x20, 0x49, 0x22, 0x49, 0x02, 0xBF, 
  0x2F, 0x08, 0x02, 0xFF, 0xFF, 0xBF, 0xAA, 0xAA, 0xAA, 0x7F, 0x26, 0x49, 
  0x22, 0x49, 0x32, 0xBF, 0x57, 0x55, 0x55, 0xFD, 0xFF, 0x7F, 0x55, 0x55, 
  0xB5, 0x7F, 0x26, 0x49, 0x22, 0x49, 0x32, 0xBF, 0xAF, 0xAA, 0xAA, 0xFE, 
  0xFF, 0xBF, 0xAA, 0xAA, 0xAA, 0x7F, 0x26, 0x49, 0x22, 0x49, 0x32, 0xBF, 
  0x57, 0x55, 0x55, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0x7F, 0x26, 0x49, 
  0x22, 0x49, 0x32, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xBF, 0x7F, 0x26, 0x49, 0x22, 0x49, 0x32, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0x7F, 0x26, 0x49, 0x22, 0x49, 0x32, 0xBF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0x7F, 0x26, 0x49, 
  0x22, 0x49, 0x32, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xBF, 0x7F, 0x26, 0x49, 0x22, 0x49, 0x32, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0x3F, 0x00, 0x49, 0x22, 0x49, 0x00, 0xBE, 
  0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 
  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 
  0x55, 0xD5, 0x55, 0xD5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFC, 
  0xFF, 0x7F, 0xFC, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0x0F, 0xE0, 0xFF, 
  0xFF, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 
  0xFF, 0x07, 0xC0, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0x03, 0x80, 0xFF, 0x03, 0x80, 0xFF, 0x3F, 0x00, 0x00, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0x03, 0xC0, 0xFF, 
  0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x80, 
  0xFF, 0x05, 0x80, 0xFF, 0x3F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFD, 0xDF, 0xFF, 0xFD, 0xDF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x81, 0xFF, 0x05, 0x81, 0xFF, 
  0x3F, 0x10, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0xC1, 
  0xFF, 0x05, 0xC1, 0xFF, 0x7F, 0x10, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0x05, 0x81, 0xFF, 0x05, 0x81, 0xFF, 0x3F, 0x10, 0x04, 0xFF, 
  0xFF, 0xFF, 0x5F, 0xFD, 0xFF, 0xFF, 0x05, 0xC1, 0xFF, 0x05, 0xC1, 0xFF, 
  0x7F, 0x10, 0x04, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x05, 0x81, 
  0xFF, 0x05, 0x81, 0xFF, 0x3F, 0x10, 0x04, 0xFF, 0xFF, 0xFF, 0x79, 0x8F, 
  0xFF, 0xFF, 0x05, 0xC1, 0xFF, 0x05, 0xC1, 0xFF, 0x7F, 0x10, 0x04, 0xFF, 
  0xFF, 0xFF, 0x7C, 0x3F, 0xFF, 0xFF, 0x05, 0x81, 0xFF, 0x05, 0x81, 0xFF, 
  0x3F, 0x10, 0x04, 0xFF, 0xFF, 0x7F, 0x7E, 0x3F, 0xFE, 0xFF, 0x05, 0xC1, 
  0xFF, 0x05, 0xC1, 0xFF, 0x7F, 0x10, 0x04, 0xFF, 0xFF, 0x3F, 0x7D, 0xDF, 
  0xFC, 0xFF, 0x05, 0x81, 0xFF, 0x05, 0x81, 0xFF, 0x3F, 0x10, 0x04, 0xFF, 
  0xFF, 0x9F, 0x7B, 0xEF, 0xF9, 0xFF, 0x05, 0xC1, 0xFF, 0x05, 0xC1, 0xFF, 
  0x7F, 0x10, 0x04, 0xFF, 0xFF, 0x8F, 0x77, 0xF7, 0xF9, 0xFF, 0xFD, 0xFF, 
  0xFF, 0xFD, 0xFF, 0xFF, 0xBF, 0xFF, 0x7F, 0xFF, 0xFF, 0x9F, 0x6F, 0xFB, 
  0xF1, 0xFF, 0x01, 0x00, 0x55, 0x01, 0x80, 0xFF, 0x3F, 0x00, 0x00, 0xFF, 
  0xFF, 0x8F, 0x5F, 0xFD, 0xF9, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 
  0xAF, 0xAA, 0xAA, 0xFA, 0xFF, 0x9F, 0x3F, 0xFE, 0xF1, 0x7F, 0x55, 0x55, 
  0x55, 0x55, 0x55, 0xFF, 0x57, 0x55, 0x55, 0xFD, 0xFF, 0x0F, 0x00, 0x00, 
  0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0x9F, 0x7F, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x7F, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0x7F, 0xFF, 
  0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0x8F, 0x7F, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 
};

const uint8_t frame2[] U8G_PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 
  0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x03, 0x00, 0x00, 
  0x1C, 0x00, 0x00, 0x00, 0xF0, 0x87, 0x1F, 0x00, 0x01, 0x30, 0x00, 0x00, 
  0x00, 0x3C, 0x00, 0x00, 0x00, 0x08, 0x60, 0x00, 0x00, 0x00, 0xCE, 0x1F, 
  0x38, 0x80, 0x21, 0xC0, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x88, 
  0x80, 0x01, 0x00, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x22, 0x80, 0x01, 0x00, 
  0x40, 0x80, 0x00, 0x00, 0x00, 0x10, 0x04, 0x03, 0x00, 0x40, 0x20, 0x40, 
  0x00, 0x01, 0x20, 0x01, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x02, 0x06, 0x00, 0x60, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x06, 0x00, 
  0x20, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x0C, 0x00, 0x30, 0xC0, 0x1F, 
  0x00, 0xC6, 0x7F, 0x00, 0x18, 0x00, 0x18, 0xE0, 0x7F, 0x80, 0xC3, 0xDF, 
  0x02, 0x3E, 0x00, 0x0C, 0xC0, 0xFF, 0x83, 0xF9, 0xFF, 0x02, 0x68, 0x00, 
  0x06, 0x00, 0xF0, 0x03, 0x1F, 0x01, 0xF0, 0xC1, 0x00, 0x6A, 0x00, 0x80, 
  0x00, 0x06, 0x07, 0xFC, 0x87, 0x01, 0x82, 0x0F, 0x80, 0x00, 0x00, 0x9E, 
  0x0F, 0x46, 0x01, 0xC2, 0xB9, 0x81, 0x00, 0x00, 0xF8, 0x21, 0x4C, 0x03, 
  0x02, 0xF0, 0xC1, 0x00, 0x00, 0x00, 0x70, 0x48, 0x02, 0x02, 0x04, 0x70, 
  0x00, 0x03, 0x00, 0x7C, 0x58, 0x02, 0x22, 0x06, 0x38, 0x00, 0x2F, 0x00, 
  0xCF, 0x5B, 0x02, 0x0E, 0x06, 0x1C, 0x00, 0x08, 0xC0, 0xC3, 0x49, 0x03, 
  0x16, 0x0F, 0x39, 0xF0, 0x09, 0xF8, 0x60, 0x08, 0x03, 0x4C, 0x0F, 0x60, 
  0x00, 0x0D, 0x5F, 0x70, 0x8C, 0x01, 0x08, 0x3F, 0xC0, 0x03, 0xE0, 0x43, 
  0x3C, 0x90, 0x01, 0x18, 0xF7, 0x80, 0x01, 0x7E, 0x60, 0x1F, 0xC0, 0x00, 
  0x18, 0xD7, 0x07, 0xF8, 0x3F, 0xF0, 0x1B, 0x70, 0x00, 0x10, 0x13, 0xFF, 
  0x7F, 0x30, 0xFC, 0x0C, 0x30, 0x00, 0x10, 0x97, 0xC1, 0x10, 0xA0, 0x7F, 
  0x04, 0x18, 0x00, 0x10, 0x9F, 0x41, 0x10, 0xF0, 0x4F, 0x06, 0x08, 0x00, 
  0x10, 0xFF, 0xEF, 0x38, 0xFF, 0x41, 0x03, 0x0C, 0x00, 0x10, 0xFF, 0xFF, 
  0xFF, 0x7F, 0xC0, 0x01, 0x04, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0x47, 0xC0, 
  0x00, 0x06, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0x40, 0x70, 0x00, 0x03, 0x00, 
  0x10, 0xEA, 0xFF, 0x67, 0xC0, 0x18, 0x00, 0x03, 0x00, 0x10, 0x5E, 0xC6, 
  0x60, 0x80, 0x0E, 0x80, 0x01, 0x00, 0x10, 0xCC, 0x84, 0x60, 0x80, 0x07, 
  0xE8, 0x00, 0x00, 0x10, 0xBC, 0x8C, 0x60, 0xF0, 0x21, 0x72, 0x00, 0x00, 
  0x18, 0xF0, 0x9F, 0xE1, 0x3F, 0x88, 0x18, 0x00, 0x00, 0x18, 0x00, 0xFF, 
  0xFF, 0x01, 0x22, 0x0E, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x80, 0x88, 
  0x07, 0x00, 0x00, 0x18, 0x08, 0x40, 0x01, 0x10, 0xC2, 0x01, 0x00, 0x00, 
  0x18, 0x20, 0x00, 0x00, 0x46, 0x78, 0x00, 0x00, 0x00, 0x18, 0x01, 0xFF, 
  0x0F, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x18, 0x04, 0x00, 0x60, 0x80, 0x07, 
  0x00, 0x00, 0x00, 0x18, 0x30, 0xFE, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 
  0x10, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 
  0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x03, 0x00, 
  0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x1F, 0xFE, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x1F, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
}; 

const uint8_t frame3[] PROGMEM = { 
  0xFF, 0xFF, // 11111111 11111111 00
  0xFF, 0xFF, // 11111111 11111111 01
  0xCF, 0xFF, // 11001111 11111111 02
  0xC3, 0xFF, // 11000011 11111111 03
  0xC0, 0xFF, // 11000000 11111111 04
  0xC0, 0x3F, // 11000000 00111111 05
  0xC0, 0x0F, // 11000000 00001111 06
  0xC0, 0x03, // 11000000 00000011 07
  0xC0, 0x0F, // 11000000 00001111 08
  0xC0, 0x3F, // 11000000 00111111 09
  0xC0, 0xFF, // 11000000 11111111 10 
  0xC3, 0xFF, // 11000011 11111111 11
  0xCF, 0xFF, // 11001111 11111111 12
  0xFF, 0xFF, // 11111111 11111111 13
}; 

const uint8_t frame4[] PROGMEM = {
  0xFF, 0xFF, // 11111111 11111111 00
  0xDF, 0xFB, // 11011111 11111011 01
  0xEC, 0x37, // 11101100 00110111 02
  0xF8, 0x1F, // 11111000 00011111 03 
  0xF0, 0x0F, // 11110000 00001111 04
  0x90, 0x09, // 10010000 00001001 05
  0xF0, 0x0F, // 11110000 00001111 06
  0xF8, 0x1F, // 11111000 00011111 07
  0xDC, 0x3B, // 11011100 00111011 08
  0xBD, 0xBD, // 10111101 10111101 09
  0xFC, 0x3F, // 11111100 00111111 10
  0xFD, 0xBF, // 11111101 10111111 11
  0xFE, 0x7F, // 11111110 01111111 12
  0xFF, 0xFF, // 11111111 11111111 13
}; 

const uint8_t frame5[] PROGMEM = {
  0xFF, 0xFF, // 11111111 11111111 00
  0xFF, 0xFF, // 11111111 11111111 01
  0xFC, 0x3F, // 11111100 00111111 02
  0xF8, 0x1F, // 11111000 00011111 03 
  0xF0, 0x0F, // 11110000 00001111 04
  0xF0, 0x0F, // 11110000 00001111 05
  0xF0, 0x0F, // 11110000 00001111 06
  0xF8, 0x1F, // 11111000 00011111 07
  0xFC, 0x3F, // 11111100 00111111 08
  0xFD, 0xBF, // 11111101 10111111 09
  0xFC, 0x3F, // 11111100 00111111 10
  0xFD, 0xBF, // 11111101 10111111 11
  0xFE, 0x7F, // 11111110 01111111 12
  0xFF, 0xFF, // 11111111 11111111 13
};

const uint8_t frame6[] PROGMEM = {
  0xFF, 0xFF, // 11111111 11111111 00
  0xFE, 0x7F, // 11111110 01111111 01
  0xF8, 0x1F, // 11111000 00011111 02
  0xF3, 0xCF, // 11110011 11001111 03 
  0xE7, 0xE7, // 11100111 11100111 04
  0xEF, 0xF3, // 11101111 11110111 05
  0xCF, 0xF3, // 11001111 11110011 06
  0xC0, 0x03, // 11000000 00000011 07
  0xC0, 0x03, // 11000000 00000011 08
  0xC1, 0x83, // 11000001 10000011 09
  0xC1, 0x83, // 11000001 10000011 10
  0xC1, 0x83, // 11000001 10000011 11
  0xC0, 0x03, // 11000000 00000011 12
  0xFF, 0xFF, // 11111111 11111111 13
}; 

const uint8_t frame7[] PROGMEM = {
  0xFF, 0xFF, // 11111111 11111111 00
  0xFF, 0xFF, // 11111111 11111111 01
  0xF3, 0xE7, // 11110011 11100111 02
  0xF3, 0xE7, // 11110011 11100111 03 
  0xF3, 0xE7, // 11110011 11100111 04
  0x80, 0x67, // 10000000 01100111 05
  0x80, 0x67, // 10000000 01100111 06
  0xF3, 0xE7, // 11110011 11100111 07
  0xF3, 0xE7, // 11110011 11100111 08
  0xF3, 0xC3, // 11110011 11000011 09
  0xFF, 0x81, // 11111111 10000001 10
  0xFF, 0x81, // 11111111 10000001 11
  0xFF, 0xC3, // 11111111 11000011 12
  0xFF, 0xE7, // 11111111 11100111 13
  0xFF, 0xFF, // 11111111 11111111 14
}; 

const uint8_t frame8[] PROGMEM = {
  0x00, 0x00, // 00000000 00000000 00
  0x00, 0x00, // 00000000 00000000 01
  0x0C, 0x18, // 00001100 00011000 02
  0x0C, 0x18, // 00001100 00011000 03 
  0x0C, 0x18, // 00001100 00011000 04
  0x7F, 0x98, // 01111111 10011000 05
  0x7F, 0x98, // 01111111 10011000 06
  0x0C, 0x18, // 00001100 00011000 07
  0x0C, 0x18, // 00001100 00011000 08
  0x0C, 0x3C, // 00001100 00111100 09
  0x00, 0x7E, // 00000000 01111110 10
  0x00, 0x7E, // 00000000 01111110 11
  0x00, 0x3C, // 00000000 00111100 12
  0x00, 0x18, // 00000000 00011000 13
  0x00, 0x00, // 00000000 00000000 14
}; 

const uint8_t frame9[] PROGMEM = {
  0xFF, 0xFF, // 11111111 11111111 00
  0xFC, 0x1F, // 11111100 00011111 01
  0xF0, 0x07, // 11110000 00000111 02
  0xEE, 0x0B, // 11101110 00001011 03 
  0xE7, 0x1B, // 11100111 00011011 04
  0xC3, 0x39, // 11000011 00111001 05
  0xC1, 0x71, // 11000001 01110001 06
  0xC0, 0x81, // 11000000 10000001 07
  0xC7, 0x41, // 11000111 01000001 08
  0xCE, 0x61, // 11001110 01100001 09
  0xEC, 0x73, // 11101100 01110011 10
  0xE8, 0x3B, // 11101000 00111011 11
  0xF0, 0x07, // 11110000 00000111 12
  0xFC, 0x1F, // 11111100 00011111 13
  0xFF, 0xFF, // 11111111 11111111 14
}; 

const uint8_t frame10[] PROGMEM = {
  0xFF, 0xFF, // 11111111 11111111 00
  0xFC, 0x9F, // 11111100 10011111 01
  0xF0, 0xC7, // 11110000 11000111 02
  0xE0, 0xE3, // 11100000 11100011 03 
  0xE0, 0xE3, // 11100000 11100011 04
  0xCC, 0xC1, // 11001100 11000001 05
  0xDE, 0x81, // 11011110 10000001 06
  0xFF, 0xFF, // 11111111 11111111 07
  0xC0, 0xBD, // 11000000 10111101 08
  0xC1, 0x99, // 11000001 10011001 09
  0xE3, 0x83, // 11100011 10000011 10
  0xE3, 0x83, // 11100011 10000011 11
  0xF1, 0x87, // 11110001 10000111 12
  0xFC, 0x9F, // 11111100 10011111 13
  0xFF, 0xFF, // 11111111 11111111 14
}; 

const uint8_t frame11[] PROGMEM = {  
  0x1F, 0x03, 0x18, // 00011111 00000011 00011000 00
  0x3B, 0x83, 0xB8, // 00111011 10000011 10111000 01
  0x79, 0xC1, 0xF0, // 01111001 11000001 11110000 02
  0xDA, 0xE0, 0xE0, // 11011010 11100000 11100000 03
  0xEB, 0x61, 0xF0, // 11101011 01100001 11110000 04
  0xF2, 0xE3, 0xB8, // 11110010 11100011 10111000 05
  0xF9, 0xE3, 0x18, // 11111001 11100011 00011000 06
  0xF2, 0xE0, 0x00, // 11110010 11100000 00000000 07
  0xEB, 0x60, 0x00, // 11101011 01100000 00000000 08
  0xDA, 0xE0, 0x00, // 11011010 11100000 00000000 09
  0x79, 0xC0, 0x00, // 01111001 11000000 00000000 10
  0x3B, 0x80, 0x00, // 00111011 10000000 00000000 11
  0x1F, 0x00, 0x00, // 00011111 00000000 00000000 12
}; 

void setup() {
  TM1637( 0, 0, 5);
  readEEPROM();
  rtc.begin();
  irrecv.enableIRIn();
  mySwitch.enableReceive(0);  // receiver on D2 (interrupt 0)
  SPI.begin();                // init SPI bus
  rfid.PCD_Init();            // init MFRC522 
  pinMode(relayDL, OUTPUT);
  pinMode(relayMP, OUTPUT); 
  pinMode(extBTen, OUTPUT);
  digitalWrite(extBTen, 0);
  pinMode(ledAuth, OUTPUT);  
  digitalWrite(ledAuth, 0);
  pinMode(ledVrOn, OUTPUT);   
  for (byte i = 0; i < 2; i++) {
    pinMode(buzzer[i], OUTPUT);
  }
  pinMode(play0, OUTPUT); 
  pinMode(play1, OUTPUT);
  pinMode(LM386, OUTPUT);
  pinMode(pwrRF, OUTPUT);
  pinMode(btnAl, INPUT);
  Serial.begin(9600);   
  Serial2.begin(9600);        // start serial communication at 9600bps EXT
  for (byte i = 0; i < exRXSize; i++) {
    exRX[i] = termCharSpc;
  }
  Serial3.begin(9600);        // INT
  myVR.begin(9600);
  voiceLoad(0);   
  ISD1820PY(0); 
  strSelector(0);
}

void loop() {
  irRecv();
  extRX();
  cyclicSysTmr(); 
  countdnSysTmr();
  intRX();
  btnRecv();
  sevenSum();
  voiceCmd();
  rfRecv();
  rfID();
  u8g.firstPage();  // picture loop
  do {
    draw();
  } while(u8g.nextPage());
  if (frame == 0) { 
    TM1637(2, 0, 0);
    delay(1000);
    frame = 1;
    clrScr(); 
  }
}

// ---- System Management
void wdRst() {
  wdt_enable(presc);  // start wd with the prescaller
  while(1) {          // the prescaller time has to expire 
  }        
}

void readEEPROM() {
  for (byte i = 0; i < 6; i++) {
    if (i < 4) {
      al[i] = EEPROM.read(addr[i + 5]);
      delay(20);
    }
    if (i == 5) {
      i = 9;
    }
    switch(i) {
      case 0:
        lockSystem(EEPROM.read(addr[i]));
        rfidHandler();
      case 1:
     // REMOVED FEATURE
      case 2:
        prntRTC = EEPROM.read(addr[i]);
      case 3:
        digitalWrite(relayDL, relayDLState = EEPROM.read(addr[i]));
        break;
      case 4:
        digitalWrite(relayMP, relayMPState = EEPROM.read(addr[i]));
        break;
      case 9:
        alOn = EEPROM.read(addr[i]);
        delay(20);
        i++; 
        digitalWrite(pwrRF, rfOn = EEPROM.read(addr[i])); 
        delay(20);
        i++; 
        digitalWrite(ledVrOn, vrOn = EEPROM.read(addr[i])); 
    }
    delay(20);
  }
}

// ---- Start of System Security Routines
void rfID() {
  if (cardManager) {
    for (byte i = 0; i < arrNUIDSize; i++) {
      if ((delNUID[i] == 1) && (strNUID[i] != strTerm)) {
        strNUID[i] = strTerm;
        delNUID[i] = boolTerm;
        cardBoolSwitches[3] = 1;     // del card
        cardBoolSwitches[2] = 1;     // not stored
        TM1637( 3, 5, 0);
      }
    }
  } 
  if (!rfid.PICC_IsNewCardPresent()) // wait for a new card
    return;
  if (!rfid.PICC_ReadCardSerial())   // check if the card has been readed
    return;
  cardBoolSwitches[0] = boolTerm;    // the card has not been seen
  strC = strTerm;
  MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
  if ((piccType != MFRC522::PICC_TYPE_MIFARE_MINI) && (piccType != MFRC522::PICC_TYPE_MIFARE_1K) && (piccType != MFRC522::PICC_TYPE_MIFARE_4K)) {
    if (!cardManager) {
      strSelector(34);
    }
    else {
      cardBoolSwitches[6] = 1;       // invalid type of card
      ISD1820PY(1);
    }
    return;  
  }
  for (byte i = 0; i < rfid.uid.size; i++) {
    strC.concat(String(rfid.uid.uidByte[i] < 0x10 ? " 0" : " "));
    strC.concat(String(rfid.uid.uidByte[i], HEX));
  }
  strC = strC.substring(1);
  if (cardManager) {
    if (strNUID[3].charAt(0) != termCharEmpty) {
      cardBoolSwitches[7] = 1;      // memory full
      TM1637( 3, 8, 0);
      return;
    }
    for (byte i = 0; i < arrNUIDSize; i++) { 
      if (strC == strNUID[i]) {     // maybe the readed card has been stored earlier
        cardBoolSwitches[4] = 1;    // already stored
        TM1637( 3, 7, 0);
      }
    }
    if (!cardBoolSwitches[4]) {
      for (byte i = 0; i < arrNUIDSize; i++) {
        if ((strNUID[i] == strTerm) && (!cardBoolSwitches[0]) && (strC != strTerm)) { // if there is a space for the new card, but only one space needed, if the reading strC isn' empty
          strNUID[i] = strC;
          cardBoolSwitches[5] = 1;  // card new
          cardBoolSwitches[0] = 1;  // card seen (because we want only one time to store) 
          cardBoolSwitches[2] = 1;  // card NOT stored yet
          TM1637( 3, 6, 0);
        }
      } 
    }
  } 
  else {
    for (byte i = 0; i < arrNUIDSize; i++) {
      if (strNUID[i] == strC) {
        lockSystem(!locked);
        strSelector(36);
        extTX(1);
      }
    }
    if ((locked) && (strNUID[0] != strC) && (strNUID[1] != strC) && (strNUID[2] != strC) && (strNUID[3] != strC)) {
      strSelector(35);
      extTX(1);
      ISD1820PY(1);
    }
  }
  rfid.PICC_HaltA();          // halt PICC
  rfid.PCD_StopCrypto1();     // stop encryption on PCD   
}

void rfidHandler() { 
  if (cardBoolSwitches[1]) {  // read at startup
    pointer = addr[12];
    for (byte i = 0; i < arrNUIDSize; i++) {
      strNUID[i] = (EEPROM.get(pointer, arrNUID[i])); 
      delay(20);  
      pointer = pointer + addr[13];
    }
    cardBoolSwitches[1] = boolTerm;
  }
  if (cardBoolSwitches[2]) {  // not stored yet or marked for delete
    buzz(0, 13, 1, 0);
    for (byte i = 0; i < (arrNUIDSize - 1); i++) {  // sort
      if (strNUID[i] == strTerm) {
        strNUID[i] = strNUID[i + 1];
        strNUID[i + 1] = strTerm;
      }
    }
    pointer = addr[12];
    for (byte i = 0; i < arrNUIDSize; i++) {
      strNUID[i].toCharArray(arrNUID[i], strNUID[i].length() + 1);  // conv str to ch array
      strNUID[i] = (EEPROM.put(pointer, arrNUID[i]));
      delay(40);   
      pointer = pointer + addr[13];
    }
    cardBoolSwitches[2] = boolTerm; 
  }
}

void lockSystem(bool op) {  // 0: unlock, 1: lock
  bool prevState = locked;
  EEPROM.write(addr[0], op);
  delay(40);
  locked = EEPROM.read(addr[0]);
  delay(20); 
  if (locked) {
    cardManager = boolTerm;
    sum = intTerm;
    relayHandlerDL(boolTerm);
    relayHandlerMP(boolTerm);
  }
  if (locked != prevState) {
    extTX(0);
  }
  intTX(0, lock_strings[locked]);
}

// ---- Start of 315/330/433MHz Receiver
void rfRecv() { 
  if ((locked) || (!pwrRF)) {
    return;
  }    
  if (mySwitch.available()) {
    if (mySwitch.getReceivedValue() != 0) {   
                                         // btn 'A' - Airing
                                                    //       <-- ADDR 2^20 | DATA 2^4 -->
      if (mySwitch.getReceivedValue() == 5592332) { // 010101010101010100001100
        delay(50);                                  // TRI-STATE FFFFFFFF0010
        if (!onlink) {                              // RAW 16972,524,1664,1576,572,516,1656,1596,556,544,1644,1604,556,508,1660,1584,580,528,1648,1588,552,540,1648,1596,564,500,1668,1576,576,516,1668,1596,548,516,1664,508,1668,532,1656,500,1672,1592,576,1552,640,464,1676,504,1664,
          strSelector(21);                          // PulseLength: 545 microseconds Protocol: 1
        }
        else {                        
          intTX(0, 42);                               
          strSelector(5);                             
        }
      }                                  // btn 'B' - Kitchen lights
      if (mySwitch.getReceivedValue() == 5592512) { // 010101010101010111000000
        delay(50);                                  // FFFFFFFF1000    
        if (!onlink) {                              // 16952,528,1644,1596,532,524,1648,1588,564,528,1636,1596,544,512,1656,1588,564,508,1652,1580,568,516,1656,1580,552,508,1664,1552,612,476,1680,1584,540,1584,568,1588,564,508,1656,528,1644,524,1656,500,1668,508,1664,504,1672,
          strSelector(21);                          // PulseLength: 546 microseconds Protocol: 1
        }
        else {          
          intTX(0, 82);                               
          strSelector(5);                             
        }
      }                                  // btn 'C' - Media player
      if (mySwitch.getReceivedValue() == 5592323) { // 010101010101010100000011
        delay(50);                                  // FFFFFFFF0001
        relayHandlerMP(2);                          // 16980,532,1640,1588,580,504,1660,1608,540,536,1648,1608,540,516,1656,1580,588,496,1676,1596,552,536,1636,1580,588,488,1688,1580,560,520,1664,1564,592,484,1672,512,1668,516,1668,520,1652,504,1664,536,1644,1608,564,1584,556,
        strSelector(5);                             // PulseLength: 546 microseconds Protocol: 1                                    
      }                                  // btn 'D' - Desklights                       
      if (mySwitch.getReceivedValue() == 5592368) { // 010101010101010100110000
        delay(50);                                  // FFFFFFFF0100
        relayHandlerDL(2);                          // 16996,528,1644,1596,544,508,1664,1576,584,496,1668,1568,580,536,1644,1588,560,500,1660,1564,604,484,1708,1580,524,524,1652,1580,568,532,1656,1580,556,508,1668,512,1668,1576,568,1600,552,508,1672,520,1672,484,1676,504,1664,
        strSelector(5);                             // PulseLength: 546 microseconds Protocol: 1                             
      }                                             
    }
    mySwitch.resetAvailable();
  }
}

// ---- Start of Matrix Handler Sequence
void btnRecv() {
  // --------------------- Accepted Keys on Main Screen --------------------------------------------------------------------
  //
  // -- Remote appliances:
  //   'A' : Airing On / Off
  //   'B' : Kitchen lights On / Off
  //   '*' : Heater On / Off (15 min timed mode)
  //
  // -- Local appliances:
  //   'C' : Media player On / Off
  //   'D' : Desklights On / Off
  // 
  // -- Other functions:
  //   '0' .. '9' : Enter Numeric Code command such as Target Temperature for 
  //                the Thermostat mode [10-24 (as Celsius) or 50-76 (as Fahrenheit)],
  //                or two-digit control code to access the attached home devices or system functions (see details below).
  //            --> '*': Delete the typed code, and go back to the Main Screen
  //            --> '#': Enter code
  //
  //        [by pressing first time] [second time]                                         [third time]
  //   '0' : Print RTC on LCD On  --> Print both RTC & Alarm on LCD, and turn Alarm On  --> Dismiss both
  //
  //   '#' : Enter the RFID handler menu
  //     --> 'A' : Delete pos 0    
  //     --> 'B' : Delete pos 1 
  //     --> 'C' : Delete pos 2    
  //     --> 'D' : Delete pos 3
  //     --> '#' : Exit RFID menu and go back to the Main Screen
  // -----------------------------------------------------------------------------------------------------------------------
  
  if (digitalRead(btnAl)) {
    delay(50);
    alOn = !alOn;
    EEPROM.write(addr[9], alOn);
    delay(40);
    TM1637(0, 0, 1); 
    buzz(0, 12, 10, 1);
  }
  char customKey = customKeypad.getKey();
  if (customKey != NO_KEY) {
    if (alCnt > 0) {
      alOn = boolTerm;
      EEPROM.write(addr[9], alOn);
      delay(40); 
      alCnt = alCntLim;       
      TM1637( 0, 0, 1);
      strSelector(59);
      return;
    }
    buzz(0, 22, 4, 0);
    if (group != 0) {
      group = 0;
      return;
    }
    if ((customKey == '0') && (!cardManager) && (sum == intTerm)) {
      TMandALmanager();
      return;
    }
    if (locked) {
      strSelector(70);
      return;
    }     
    if ((customKey == '#') && (sum == intTerm)) {  // rfid menu
      if (!cardManager) {
        TM1637( 3, 4, 0);
      }
      cardManager = !cardManager;
      if (!cardManager) {
        TM1637( 0, 0, 1);
      }
    }  
    if (cardManager) {  
      switch (customKey) {
      case 'A':
        delay(50);
        delNUID[0] = 1;
        break;
      case 'B':
        delay(50);
        delNUID[1] = 1;
        break;
      case 'C':
        delay(50);
        delNUID[2] = 1;
        break;
      case 'D':
        delay(50);
        delNUID[3] = 1;
        break;
      }  
    }
    else {
      switch (customKey) {
        case 'A':
          delay(10);
          if (!onlink) {                              
            strSelector(21);                          
          }
          else {  
            strSelector(6);
            intTX(0, 42);
          }
          break;
        case 'B':
          delay(10); 
          if (!onlink) {                              
            strSelector(21);                          
          }
          else {      
            strSelector(6);
            intTX(0, 82);
          }
          break;
        case 'C':
          delay(10);
          strSelector(6);
          relayHandlerMP(2);
          break;
        case 'D':
          delay(10);
          strSelector(6);
          relayHandlerDL(2);
          break;
        case '*':
          delay(10);
          if (sum > intTerm) {  // del the numeric code
            sum = intTerm;
          }
          else {
            if (!onlink) {                              
              strSelector(21);                          
            }
            else {  
              intTX(0, 32);
            }
          }
          break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          delay(10);
          if ((sum <= 10) && (sum <= 99)) {
            sum = sum * 10 + (customKey - '0');
          }
          break;
        case '#':
          delay(10);
          if (sum == 25) {
            intTX(0, sum);
            delay(100);
            wdRst();
            return;            
          }
          if (sum == 26) {
            rfOn = !rfOn;
            digitalWrite(pwrRF, rfOn); 
            EEPROM.write(addr[10], rfOn); 
            delay(40);
            if (rfOn) {
              strSelector(63);
            }
            else {
              strSelector(64);
            }
          }
          if (sum == 27) {
            vrOn =! vrOn;
            digitalWrite(ledVrOn, vrOn);
            EEPROM.write(addr[11], vrOn); 
            delay(40);
            if (vrOn) {
              strSelector(68);
            }
            else {
              strSelector(69);
            }            
          }
          if (sum == 28) {
            sum = intTerm;
            if (auth) {
              scr();  
              strSelector(71);
              extTX(3);        
...

This file has been truncated, please download it to see its full contents.
Thermostat Unit [V2.0]Arduino
// ---- THERMOSTAT PROGRAM CODE v2.0
// Program code of an intelligent Arduino smarthome thermostat solution, 
// based on Arduino Nano (or higher) board, DS18B20 thermo sensor, HC-05 Bluetooth adapter, I2C 128X64 bicolor OLED display.
// The system can be controlled by buttons and Android smartphone via bluetooth.
//
// It handles the HEATER gas boiler, the bathroom AIRING ventilator and the kitchen LIGHTING - swithed with relays.
// The heater has two working ways. 1: One Time Heating (15 mins) timed mode, 2: Thermostat mode (higher priority). The adjusted target tempr.
// stored in EEPROM. The program detects hardware errors and window opening - in these cases the heater stops and/or will not start.
//
// Designed and programmed 
// by Gyula Osi.
// All rights reserved.
// ------------------------------------------------------------------------------------------------------------------------------------

// ---- Display I2C BUS, SDA(TX) -> A4, SCL(RX) -> A5
#include "U8glib.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);  // display constructor
byte frame = 0;                               // scr pointer

// ---- Ports and Related Declarations
const byte buzzer = 9;   // buzzer to D9
int btnVal;              // stores analog values from buttons
const byte relayA = 11;  // airing
bool aStateByCmd;        // if "window alert" is gone, the airing control goes back to the original state
bool aState = 0;
const byte relayL = 13;  // lighting
bool lState = 0;
const byte relayH = 10;  // heater
const byte ledH = 6;     // PWM ports allow output level adjustment [2^8] used to change brightness
const byte ledA = 5;     
const byte ledL = 3;
const byte bright[3] = {0, 10, 100};
byte brightHeat = bright[2];
#define ledInterval 1000 // heater led blinking interval
unsigned long prev = 0;

// ---- Strings for Display Printout and Bluetooth Reports 
#define STATE_ITEMS 3    // device state indicator strings for outputs
// i ->                                  0      1       2
const String state_str[STATE_ITEMS] = {"on.", "off.", "auto."}; 
String heaterState;               
String airingState;
String lightingState;
#define FUNCT_ITEMS 22
// i ->                                  0             1             2               3                4                  5               6                  7            8                 9     10    11    12        13              14                 15         16    17         18                   19   20        21
const String funct_str[FUNCT_ITEMS] = {"Heater is ", "Airing is ", "Lighting is ", "Window Alert!", "Hardware Error!", "Hit a Key >>", "or send a Code!", "Target = ", "Temperature = ", " * ", " -", "> ", "REPORT", "Celsius mode", "Fahrenheit mode", "System ", "un", "locked!", "Command rejected!", "X", "cost= ", "USD"};

// ---- Memory Management
#include <EEPROM.h>
const byte addr[4] = { 0, 1, 2, 3}; // ---- Memory Map
                                    //     [i] STORED
                                    //      0  byte tTarget
                                    //      1  bool celsius 
                                    //      2  bool locked
                                    //      3  int costMinutes, initial address [int: 2^16]

// ---- Temperature and Heater Related Features
#include <elapsedMillis.h>        
elapsedMillis timer0;               // 8-bit, PWM timer, used by function elapsedMillis()
#define sftyTmrInterval 15 * 60000  // one Time Heating (15 mins) timed mode interval [ms]
bool sftyTmrEnded;                  // boolean startup, the timer0 has ended
byte tTarget;                       // adjusted target tempr
bool celsius;                       // measurement unit; 0 = F, 1 = C
float heatCorrVal;                  // declares the degree of overheating and cooling back, see tempMeas() function
#define mUnit 2
#define tRef 2
#define corr 3
const float hCorrRef[mUnit][tRef] = {{62.6, 66.2}, {17, 19}};
const float hCorrVal[mUnit][corr] = {{0.9, 0.72, 0.54}, {0.5, 0.4, 0.3}};
bool hState = 0;                    // heater boolean state
bool hStatePrev = 0;                // previous heater boolean state
bool hThermostat = 0;               // thermostat boolean state
#include "OneWire.h" 
#include "DallasTemperature.h"
#define DS18B20 2                   // setup the OneWire bus on D2
OneWire temprWire(DS18B20);         // setup DS18B20 to work on the OneWire bus
DallasTemperature sensors(&temprWire);
float tempr;                        // measured value
float temprPrev;                    // copy of measured value for trend analysis
float temprStart;                   // copy of measured value for summary of a heating period
bool windowAlrt = 0;                // specified degree of tempr drop
#define aVal 3                      // drop/lift/under
const float wAlrt[mUnit][aVal] = {{ 0.36, -0.216, 68}, { 0.2, -0.12, 20}};
// = {{ 0.36, 0.2}, {-0.216, -0.12}, {68, 20}}; 
bool measError = 0;                 // measured tempr value is out of the range specified as normal
bool errMsgSentBySys = 0;           // do not flood
const long temprInterval = 60000;   // cyclic tempr measurement interval [ms]
unsigned long temprTmrPrev = 0;     // the elapsed will be the previous when temprMeas() called
bool sysChanged = 0;                // this bool will high, if the tempr meas system has changed recently (C2F/F2C),  
                                    // so the tempr analisys won't start due to the unappropriate temprPrev value
bool hPeriodFin = 0;                // writeout the summary after a heating period 
int costMinutes;                    // minute counter stored in EEPROM
int minutes = 0;                    // minute counter for summary of a heating period                                 
//                |<--  cost of 1 min  -->|
//                 |<--  USD/m^3 -->|
float costCoeff = ((10.52 / 28.31685) / 50); // based on the U.S. average annual natural gas residental price; $10.52 / 1000ft^3 (www.eia.gov)
float cost;                                  // 1000ft^3 = 28.31685m^3, and my boiler uses up 1m^3 natural gas in 50 minutes
                                  
// ---- System & Security
#include <avr/wdt.h>
uint8_t presc = WDTO_15MS;
bool locked;
bool startup = 1;                   // keep avoid a duplicate BT report at startup if the system is locked

// ---- Configuration of Serial Communication on virtual RXD/TXD [D8/D4]
#include <SoftwareSerial.h>   
const int RX1 = 8;
const int TX1 = 4;
SoftwareSerial sUART(RX1,TX1); 
char RX[2];                        // store received serial data
#define rprtInterval 1000          // sending interval of the Cyclic Binary Synchronous Report 
unsigned long prevRprt = 0;        //                    (please see details & the structure on the bottom of the code)
bool sent = 0;
bool onlink = 0;

// ---- Terminators & The Cleanup Crew
const char charTerm = 'Z';
const bool boolTerm = 0;
const int intTerm = 0;

const uint8_t frame1[] U8G_PROGMEM = {  // XBM map
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0xFE, 0xFF, 
  0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFC, 0xFF, 0x7F, 0xF0, 
  0x3F, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 
  0xFC, 0xFF, 0x7F, 0xF0, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 
  0xFF, 0xFF, 0x1F, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 
  0x8F, 0xFF, 0x1F, 0x7F, 0x8C, 0x3F, 0x1E, 0xFF, 0x00, 0xFE, 0x1F, 0xFF, 
  0xF1, 0x00, 0x18, 0xC0, 0x8F, 0xFF, 0x1F, 0x7F, 0x8C, 0x3F, 0x1E, 0xFF, 
  0x00, 0xFE, 0x1F, 0xFF, 0xF1, 0x00, 0x18, 0xC0, 0x8F, 0xFF, 0x1F, 0x1F, 
  0x0C, 0x3E, 0x1E, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0x31, 0xFE, 0x7F, 0xFC, 
  0x8F, 0xFF, 0x1F, 0x1F, 0x0C, 0x3E, 0x1E, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 
  0x31, 0xFE, 0x7F, 0xFC, 0x0F, 0x0E, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0xFC, 
  0x00, 0xF8, 0x1F, 0x7C, 0x30, 0xFE, 0x7F, 0xF0, 0x0F, 0x0E, 0x18, 0x1F, 
  0x0C, 0x3E, 0x1E, 0xFC, 0x00, 0xF8, 0x1F, 0x7C, 0x30, 0xFE, 0x7F, 0xF0, 
  0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 
  0xF0, 0x00, 0x7E, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 
  0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x00, 0x7E, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 
  0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 
  0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 
  0xF0, 0x3F, 0x78, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 
  0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 
  0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 
  0x3F, 0x00, 0x7E, 0x00, 0x3C, 0x80, 0x07, 0xF0, 0x00, 0xF8, 0x7F, 0x00, 
  0x3C, 0x00, 0x1E, 0xC0, 0x3F, 0x00, 0x7E, 0x00, 0x3C, 0x80, 0x07, 0xF0, 
  0x00, 0xF8, 0x7F, 0x00, 0x3C, 0x00, 0x1E, 0xC0, 0xFF, 0xFF, 0xFF, 0x1F, 
  0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x7E, 0x06, 0xE6, 0x3F, 0x06, 0xC6, 0x7F, 0xFE, 0xE7, 0x3F, 0x7E, 
  0xFE, 0xC7, 0x7F, 0x00, 0x00, 0x30, 0x06, 0x66, 0x60, 0x06, 0x66, 0x00, 
  0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x30, 0x06, 0x66, 
  0x60, 0x06, 0x66, 0x00, 0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 
  0x00, 0x30, 0x1E, 0x66, 0x60, 0x06, 0x66, 0x00, 0x60, 0x60, 0x60, 0x30, 
  0x06, 0x60, 0x00, 0x00, 0x00, 0x30, 0x3E, 0x66, 0x60, 0x06, 0x66, 0x00, 
  0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0xE6, 
  0x61, 0x1E, 0xC7, 0x3F, 0x70, 0xE0, 0x3F, 0x3C, 0xFE, 0xC3, 0x3F, 0x00, 
  0x00, 0x3C, 0x7E, 0xE6, 0x61, 0x1E, 0xC7, 0x3F, 0x70, 0xE0, 0x3F, 0x3C, 
  0xFE, 0xC3, 0x3F, 0x00, 0x00, 0x3C, 0xDE, 0xE7, 0x61, 0x1E, 0x07, 0x70, 
  0x70, 0xE0, 0x1D, 0x3C, 0x1E, 0x00, 0x70, 0x00, 0x00, 0x3C, 0x1E, 0xE7, 
  0x61, 0x1E, 0x07, 0x70, 0x70, 0xE0, 0x31, 0x3C, 0x1E, 0x00, 0x70, 0x00, 
  0x00, 0x3C, 0x1E, 0xE6, 0x61, 0x1E, 0x07, 0x70, 0x70, 0xE0, 0x61, 0x3C, 
  0x1E, 0x00, 0x70, 0x00, 0x00, 0x3C, 0x1E, 0xE6, 0x61, 0x1E, 0x07, 0x70, 
  0x70, 0xE0, 0x61, 0x3C, 0x1E, 0x00, 0x70, 0x00, 0x00, 0x7F, 0x1E, 0xE6, 
  0x3F, 0xFC, 0xE3, 0x3F, 0x70, 0xE0, 0x61, 0x7E, 0xFE, 0xE7, 0x3F, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 
};

const uint8_t frame2[] PROGMEM = {
  0xFF,0xFF, // 11111111 11111111 00
  0xFF,0xFF, // 11111111 11111111 01
  0xFE,0x7F, // 11111110 01111111 02
  0xF8,0x1F, // 11111000 00011111 03
  0xF3,0xCF, // 11110011 11001111 04 
  0xE7,0xE7, // 11100111 11100111 05
  0xEF,0xF3, // 11101111 11110111 06
  0xCF,0xF3, // 11001111 11110011 07
  0xC0,0x03, // 11000000 00000011 08
  0xC0,0x03, // 11000000 00000011 09
  0xC1,0x83, // 11000001 10000011 10
  0xC1,0x83, // 11000001 10000011 11
  0xC1,0x83, // 11000001 10000011 12
  0xC1,0x83, // 11000001 10000011 13
  0xC0,0x03, // 11000000 00000011 14
  0xC0,0x03, // 11000000 00000011 15
  0xFF,0xFF, // 11111111 11111111 16
  0xFF,0xFF, // 11111111 11111111 17
  }; 

void setup() {  
  sUART.begin(9600);
  pinMode(relayH, OUTPUT);   
  relayHandlerH(4);             // get the working state
  pinMode(ledH, OUTPUT);
  pinMode(relayA, OUTPUT);
  relayHandlerA(4);
  pinMode(ledA, OUTPUT);
  pinMode(relayL, OUTPUT);
  relayHandlerL(4);
  pinMode(ledL, OUTPUT);
  pinMode(buzzer, OUTPUT);
  sensors.begin();              // start DS18B20
  tTargetHandler(EEPROM.read(addr[0]));  // Read the previously stored tTarget value from the current address of the EEPROM at startup
                                         // First the tempMeas() will be called by tTargetHandler() by getting the appropriate tTarget
                                         // value readed from the EEPROM. Later the temprMeas() will do it.
  lockSystem(EEPROM.read(addr[2]));
  costCalc(0);
  startup = boolTerm;
}

void loop() { 
  temprTimer();
  btRecv();
  ledHandler();
  binaryReport();
  btnReadings();
  safetyTmr();
  u8g.firstPage();        // display loop
  do {
    draw();
  } 
  while(u8g.nextPage());
  if (frame == 0) { 
    delay(3000);
    frame = 1;
    clrScr(); 
  }
}

void costCalc(bool op) {  // 0 = read, 1 = write
  if (!op) {
    costMinutes = EEPROM.get(addr[3], costMinutes);
    delay(20);
    cost = costMinutes * costCoeff;
  }
  else {
    EEPROM.put(addr[3], (costMinutes + minutes));
    delay(80);
  }  
}

void lockSystem(bool op) {
  bool prevState = locked;
  EEPROM.write(addr[2], op);
  delay(40);
  locked = EEPROM.read(addr[2]);
  delay(20);
  if (locked) {
    startup = 1;
    windowAlrt = boolTerm;
    relayHandlerH(0);
    relayHandlerA(0);
    relayHandlerL(0);
    startup = boolTerm;
  }
  if (locked != prevState) {
    sTX(12);
  }
}

// ---- System Management
void wdRst() {
  asm volatile ("jmp 0");
//wdt_disable();      // the WDT method works only on boards with new bootloader
//wdt_enable(presc);  // start wd with the prescaller
//while(1) {          // the prescaller time has to expire 
//}    
}

void btnReadings() {                 // --------- btn readings
  if (locked) {
    return;
  }
  btnVal = analogRead(A0);                     // read analog val from A0
  if (btnVal >= 510 && btnVal <= 516) {        // btn Lighting
    delay(100);                                // btn debounce
    buzz(3, 1);
    relayHandlerL(2);                          // call proper function with logical state flip opcode as parameter
  }
  else if (btnVal >= 849 && btnVal <= 853){    // btn Airing
    if (windowAlrt) {                          // if the system is in Window Alert mode, disable it
      windowAlrt = boolTerm;                                                        
      buzz(4, 3);                                                            
      relayHandlerA(3);
    }
    else {                                     // else turn on/off the airing ventilator
    delay(100);
    buzz(3, 1);
    relayHandlerA(2);
    }
  }
  else if (btnVal >= 927 && btnVal <= 933) {   // btn One Time Heating (15 mins) timed mode
    delay(100);
    buzz(3, 1);
    relayHandlerH(2);
  }
  else if (btnVal >= 767 && btnVal <= 777) {   // btn decrease tTarget
    delay(100);
    tTargetHandler(0);
  }
  else if (btnVal >= 687 && btnVal <= 697) {   // btn increase tTarget
    delay(100);
    tTargetHandler(1);
  }
  else if (btnVal >= 855 && btnVal <= 862) {   // inc & dec btns at the same time will change 
    delay(100);                                // the unit of temperature measurement.
    tTargetHandler(2);                         // flip measurement unit and convert the tempr target value 
  }
}

void btRecv() {           // ------------- Receive Serial Data  
  while (sUART.available() > 0) { // if data is available to read
    for (byte i = 0; i < 2; i++) {
      RX[i] = sUART.read();
    }
  }  
  int iRX[2]; 
  for (byte i = 0; i < 2; i++) {
    iRX[i] = RX[i] - '0';
  }
  if (iRX[0] * 10 + iRX[1] == 98) {
    lockSystem(0);
  }
  if (!locked) {
    switch (RX[0]) {       // ------------- Accept SINGLE ALPHABETICAL control codes
      case 'A':
        relayHandlerH(1);   // 1=on
        break;
      case 'a':
        relayHandlerH(0);   // 0=off
        break;                           // Received serial data can be a single alphabetical letter from "Arduino 
      case 'B':                          // Bluetooth Control Device" Android app. If a proper alphabetical
        relayHandlerA(1);                // character arrives, the program code will not wait for the second one,
        break;                           // but calls the applicable function with a proper operation code.
      case 'b':                          // Cases of combined numeric data can be seen below.
        relayHandlerA(0);    
        break;
      case 'C':
        relayHandlerL(1);
        break;
      case 'c':
        relayHandlerL(0);
        break;
      case 'D':
      case 'd':
        tTargetHandler(21);                           
        buzz(3, 1);
        break; 
      case 'E':
      case 'e':
        tTargetHandler(19);                           
        buzz(3, 1);
        break;
      case 'F':
      case 'f':
        tTargetHandler(14);                          
        buzz(3, 1);
        break;
      case 'R':             // call for an overview report about controlled devices
      case 'r':
        if (windowAlrt) {
          windowAlrt = boolTerm;                                                        
          buzz(4, 3);                                                            
          relayHandlerA(3);
        }
        else {
          sTX(8);
          sTX(2);
          buzz(5, 2);
        }  
        break;
      case 'W':             // disable Window Alert state
      case 'w':
        windowAlrt = boolTerm;                                                        
        buzz(4, 3);                                                            
        relayHandlerA(3);
        break;
      case '+':
        tTargetHandler(0);
        break;
      case '-':
        tTargetHandler(1);
        break;
      case 'l':
        lockSystem(1);
        break;  
      case 'X':            // accept a request and send back a report 
      case 'x':
        onlink = 1; 
        break; 
      case 'Y':  
      case 'y':
        sTX(16);
        break;
    }                             
                   // -------------------------------------- Accept COMBINED NUMERIC Control Codes     
                   // In this case a two-digit numeric control code arrives in char format, from an Android
                   // bluetooth serial app for instance. After a char to integer conversion
                   // (only if the first char is '1', '2', '5', '6', '7') a merge-process will follow, and the
                   // system of conditions and statements will make a decision and execute it.
                   //
                   // -------------------------- System Control Codes:
                   //
                   //    94: query for natural gas cost value
                   //    95: erase the cost value
                   //    96: lock the Thermostat Unit - arrives from the Central Unit (RFID)
                   //    97: unlock the Thermostat Unit - arrives from the Central Unit (RFID)
                   //    98: query for report of the devices (heater, airing, lights), and target temperature
                   //    99: sent by the Central Unit, activates the Onlink mode on the Thermostat Unit
                   //
                   // -------------------------- Device Control Codes:
                   //                  First = device code, Second = operator code
                   //
                   // 30, 31, 32 turns the heater: 30=off, 31=on, 32=flip logical state (on -> off / off -> on)
                   //                                            One Time Heating (15 mins) timed heater program
                   // 40, 41, 42 will do the same to the airing ventilator
                   // 80, 81, 82 handles the kitchen lights as above
                   //
                   // 90, 91, 92 desklights (on the Central Unit only)
                   // 93         media player flip boolean state (on the Central Unit only)
                   //
                   // -------------------------- Classified Operator Codes:
                   //             X3, X4 are classified, used only for function calls by inner program sequences
                   //
                   // -------------------------- Target Temperature:
                   // 10 - 24 as Celsius, or 50 - 76 as Fahrenheit values will be accepted and stored in EEPROM as 
                   // target temperature for the thermostat function. If the measurement unit changes, the system 
                   // converts all the previous temperature values (used for trend analisys, and summary statistics),
                   // and calls tempMeas() to make a new measurement in the actual unit.                
    switch (iRX[0]) {                                                      
      case 3:                                 
        relayHandlerH(iRX[1]);                  
        break;                                
      case 4:                                 
        relayHandlerA(iRX[1]);                  
        break;                                
      case 8: 
        if ((iRX[1] >= 0) && (iRX[1] <= 4)) {                                
          relayHandlerL(iRX[1]); 
        }                   
        break;                                
      case 9:
        switch (iRX[1]) {
          case 4:
            sTX(16);
            break;
          case 5: 
            costMinutes = intTerm;                  
            costCalc(1);                     
            sTX(16);
            break; 
          case 6:
            sTX(8);                 
            sTX(2);
            break;
          case 7:
            lockSystem(1);
            break;    
        }
      default:
        if (((iRX[0] * 10 + iRX[1] <= 24) && (iRX[0] * 10 + iRX[1] >= 10)) || ((iRX[0] * 10 + iRX[1] >= 50) && (iRX[0] * 10 + iRX[1] <= 76))) {
          tTargetHandler(iRX[0] * 10 + iRX[1]);      
          buzz(3, 1);                                   
        }  
        if (iRX[0] * 10 + iRX[1] == 25) {
          buzz(3, 1);
          wdRst();
        }
        break;                                          
    }                                                
  } 
  if (iRX[0] * 10 + iRX[1] == 97) {
    lockSystem(1);
  }                         
  if (iRX[0] * 10 + iRX[1] == 99) {
    onlink = 1;
  }
  for (byte i = 0; i < 2; i++) {
    RX[i] = charTerm;
  }
}

void relayHandlerL(byte lOperator) {  // lighting handler sequence
                                      // operators are: 0=off, 1=on, 2=flip the state, 
                                      // 4=fill/refill the lighting state string var.
  if ((measError) && ((lOperator == 1) || (lOperator == 2))) {
    sTX(4);
    return;                                    
  }
  if ((lOperator == 2) || (lOperator == 0) && (lState) || (lOperator == 1) && (!lState)) {
    lState = !lState;
    digitalWrite(relayL, lState);
    buzz(2, 1);
  }  
  if (lOperator >= 0) {               // fill up the working state char with the proper state indicator string
    if (lState) {
      lightingState = state_str[0];
    }
    else  {
      lightingState = state_str[1];
    }
    if ((!startup) && (!measError)) {
      sTX(7);
    }
  }
}

void relayHandlerA(byte aOperator) {   // airing handler sequence
                                       // operators are: 0=off, 1=on, 2=flip the state, 
  if ((measError) && ((aOperator == 1) || (aOperator == 2))) {  // operators are: 0=off, 1=on, 2=flip the state, 
    sTX(4);                                                     // 3=called by temprMeas() funct., 4=fill/refill the airing state char var.
    return;                                     
  }
  aState = digitalRead(relayA);        
  if (!windowAlrt) {
    if ((aOperator == 2) || (aState) && (aOperator == 0) || (!aState) && (aOperator == 1)) {
      aState = !aState;
      digitalWrite(relayA, aState);
      aStateByCmd = digitalRead(relayA);
      buzz(2, 1);
    } 
  } 
  if (aOperator == 3) {                // called by the temprMeas() function, 'windowAlrt' ended or started
    if ((!aState) && (windowAlrt) || (aState) && (!windowAlrt) && (!aStateByCmd)) {
      digitalWrite(relayA, windowAlrt);
    }
  }
  aState = digitalRead(relayA);
  if (aOperator >= 0) {
    if (aState) {
      if (windowAlrt) {
        airingState = state_str[2];
      }
      else  {
        airingState = state_str[0];
      }
    }
    else {
      airingState = state_str[1];
    }
  }
  if ((!startup) && (!measError)) {
    sTX(6);
  }
}
 
void relayHandlerH(byte hOperator) {  // heater handler sequence 
                                      // operators are: 0=off, 1=on, 2=flip the state, 
                                      // 3=called by temprMeas() funct., 4=fill/refill the heater state char var.
  if ((measError) && ((hOperator == 1) || (hOperator == 2))) {
    sTX(4);
    return;                                    
  }   
  hStatePrev = hState;                                   
  if ((!hThermostat) && (!windowAlrt) && (!measError)) {     // turn on/off the One Time Heating (15 mins) timed mode
    if ((hOperator == 2) || (hOperator == 1) && (!hState) || (!hOperator) && (hState)) {
      buzz(2, 1);
      hState = !hState;
      sftyTmrEnded = boolTerm;
      timer0 = 0;   
      digitalWrite(relayH, hState);
    }
  }
  if (windowAlrt) {
    sTX(3);
  }
  if (hOperator == 3) {               // this function called by the temprMeas() function (op 3) 
                                      // in order to examine windowAlrt & measError booleans extreme tempr values                                                                                                          
    if ((windowAlrt) && (hState)) {            // a window is open and the heater is running
      digitalWrite(relayH, 0);
      buzz(5, 3);
    }
    if ((!windowAlrt) && (!measError)) {
      if ((hThermostat) || (!hThermostat) && (hState) && (sftyTmrEnded)) { 
        digitalWrite(relayH, hThermostat);     // proceed the command of the thermostat routine
      }
    }
  }
  hState = digitalRead(relayH);
  if (hOperator >= 0) {
    if (hState) {
      if (hThermostat) {
        heaterState = state_str[2];
      }
      else  {
        heaterState = state_str[0];
      }
    }
    else {
      heaterState = state_str[1];
    }
  } 
//                                                       <------------------------------------------------------------------------------------------------------------------------------------>      
//                      <-------------------------->     <----------------------------->    <---------------------------->    <------------------------------------------>    <-------------->          
  if ((!measError) && (((!windowAlrt) && (!startup)) && (((hOperator == 0) && (!hState)) || ((hOperator == 1) && (hState)) || ((hOperator == 2) && (hState == hStatePrev)) || (hOperator == 4))) || (hState != hStatePrev)) {
    sTX(5); 
  }  
  if (hState) {
     minutes++;
  }
  if (hState != hStatePrev) {
    if (hState) {
      minutes = intTerm;
      temprStart = tempr;
    }
    else {
      costCalc(1);
      sTX(14);
      sTX(16);
      hPeriodFin = 1;
    }
  }
}

void safetyTmr () {        // timer for the One Time Heating (15 mins) timed mode
  if (locked) {
    return;
  }
  if ((hState) && (!sftyTmrEnded) && (timer0 > sftyTmrInterval) && (!hThermostat)) {
    sftyTmrEnded = 1;
    relayHandlerH(0);
    for (byte i = 1; i < 5; i++) {
      buzz(i, 2);
    }
  }
}

void temprTimer() {        // cyclic timer for temprMeas()
  unsigned long temprTmrCurr = millis();
  if (temprInterval <= temprTmrCurr - temprTmrPrev) {
    temprTmrPrev = temprTmrCurr;
    hPeriodFin = boolTerm;
    temprMeas();
  } 
}

void temprMeas() {          // ----------- Tempr Measurement & Comparison Sequence
  temprPrev = tempr;                       // save the value for next comparison
  sensors.requestTemperatures();           // update sensor readings
  if (celsius) {
    tempr = sensors.getTempCByIndex(0);    // read remperature
  }
  else {
    tempr = sensors.getTempFByIndex(0); 
  }
  if (((celsius) && ((tempr >= 40) || (tempr <= 0))) ||  ((!celsius) && ((tempr >= 104) || (tempr <= 32)))) { 
    if (!errMsgSentBySys) {             // -127, -196.60 are HW errors, +85 is tipically SW error, but
      sTX(4);                           // can be fire, or a broken window
    }
    errMsgSentBySys = 1;
    hThermostat = boolTerm;
    if (hState) {
      relayHandlerH(0);                      
    }
    if (aState) {
      relayHandlerA(0);                      
    }
    if (lState) {
      relayHandlerL(0);
    }  
    measError = 1;
    for (byte i = 1; i < 10; i++) {        
      buzz(4, 1);                          
      delay(50);
    } 
  }                                                       
  else {
    measError = boolTerm;
    errMsgSentBySys = boolTerm;
  }
  if (!measError) {         // --------------- Start of Temperature Analysis Sequence
                                            // Frequent, short-term switching of the heater gas boiler would cut short its lifetime, the
                                            // heatCorrVal value helps to keep avoid it. Declares the degree of overheating and cooling back.
                                            // Lower temperature demands greater heatCorrVal, because the walls are colder and adsorb better the
                                            // warmth from the freshly heated-up air, so the above described effect would more stronger.
    if (tempr <= hCorrRef[celsius][0]) {                    
      heatCorrVal = hCorrVal[celsius][0];                  
    }                                     
    if ((tempr > hCorrRef[celsius][0]) && (tempr < hCorrRef[celsius][1])) {   
      heatCorrVal = hCorrVal[celsius][1];
    }
    if (tempr >= hCorrRef[celsius][1]) {
      heatCorrVal = hCorrVal[celsius][2];
    }
    if (!sysChanged) {                       // If the meas system has changed recently, dont start the temp analisys for the first time 
      if (tTarget - tempr >= heatCorrVal) {  // subtract measured value from target, if the difference equals or greater than heatCorrVal
        sftyTmrEnded = 1;                    // deactivate the One Time Heating (15 mins) timed program if it is running
        hThermostat = 1;                     // turn on the thermostat
        buzz(1, 1);                          
      }
      if ((tTarget - tempr <= -1 * heatCorrVal) && (hThermostat)) {  
        hThermostat = boolTerm;
      }      
      if ((!windowAlrt) && (temprPrev - tempr >= wAlrt[celsius][0]) && (tempr <= wAlrt[celsius][2])) {       
        windowAlrt = 1;                      // in a measurement cycle and in heating season the temperature
        sftyTmrEnded = 1;                    // drops, it will evaluate as a window is open
        for (byte i = 1; i < 5; i++) {
          buzz(4, 1);
          delay(50);
        }
        relayHandlerA(3);                    // call airing function (opcode = 3), to help refresh the air
      }
      if ((windowAlrt) && (temprPrev - tempr <= wAlrt[celsius][1])) {
        windowAlrt = boolTerm;               // the tempr. falling is over, the air became warmer 
        buzz(4, 3);                          // due to the heat capacity of the environment, 
        relayHandlerA(3);                    // so switch back to normal mode
      }
      relayHandlerH(3);                      // the function will examine caller param(3) & windowAlrt & measError booleans
    }                                        // ---- end of sysChanged section                                                      
    if (!windowAlrt) {
      sTX(1);
    }
  }
  sysChanged = boolTerm;
}

void tTargetHandler (byte set) {             // -------------- Start of Target Temperature handler routine
  hPeriodFin = boolTerm;
  if (set == 0) {                
    if (((celsius) && (tTarget < 24)) || ((!celsius) && (tTarget < 76))) {         
      tTarget++ ;                                         // ------------------ Set of Operator Codes -------------------------
      tTargetEEPROM();                                    // 0 : INCR 
      buzz(3, 1);                                         // 1 : DECR tTarget until it reaches the max / min limit
      }                                                   // 2 : FLIP mesurement unit, and converts the stored tTarget
    else {                                                //     value into the appropriate unit. This function also converts
      buzz(2, 3);                                         //     temprPrev and temprStart values. Called by btnReadings()
    }                                                     //     if the INCR & DECR buttons has been pressed at the same time.
    tTargetEEPROM();                                      // 10..24 & 50..76: intervals received from Bluetooth, adding the
  }                                                       //     tTarget value, and the examination of tTargetHandler()
  if (set == 1) {                                         //     will declare the unit of the measurement.
    if (((celsius) && (tTarget > 10)) || ((!celsius) && (tTarget > 50))) {
      tTarget-- ;
      tTargetEEPROM();
      buzz(3, 1); 
    }
    else {
      buzz(2, 3);
    }
    tTargetEEPROM();
  }
  if (set == 2) {
    if (celsius) {
      tTarget = ((1.8 * tTarget) + 32), 0;
      temprPrev = ((1.8 * temprPrev) + 32), 2;
      if (hState) {
        temprStart = ((1.8 * temprStart) + 32), 2;
      }
    }
    else {
      tTarget = ((5 * (tTarget - 32)) / 9), 0;
      temprPrev = ((5 * (temprPrev - 32)) / 9), 2;
      if (hState) {
        temprStart = ((5 * (temprStart - 32)) / 9), 2;
      }
    }
    sysChanged = 1;
    celsius = !celsius;
    temprMeas();          // after set, call the EEPROM handler function, and write the tTarget value to the appropriate byte of the EEPROM
    tTargetEEPROM();      // target tempr has changed, call the temp meas function, don't wait for the cyclic timer
  }
  celsius = EEPROM.read(addr[1]);
  if (((set >= 10) && (set <= 24)) || ((set >= 50) && (set <= 76))) {
    tTarget = set;
    if ((tTarget <= 24) && (!celsius)) {
      sysChanged = 1;
      celsius = 1;
      if (hState) {
        temprStart = (5 * (temprStart - 32)) / 9;
      }
      temprPrev = (5 * (temprPrev - 32)) / 9;
      sTX(10);
    }
    if ((tTarget >= 50) && (celsius)) {
      sysChanged = 1;
      celsius = 0;
      if (hState) {
        temprStart = (1.8 * temprStart) + 32;
      }
      temprPrev = (1.8 * temprPrev) + 32;
      sTX(9);
    }
    tTargetEEPROM();
    temprMeas();
  }
}

void tTargetEEPROM() {
  EEPROM.write(addr[0], tTarget);  // after incr/decr/set, write the tTarget value to the appropriate byte of the EEPROM
  delay(40);
  EEPROM.write(addr[1], celsius); 
  delay(40);
  sTX(2);
}

void draw(void) {               
  if (frame == 0) 
    u8g.drawXBMP( 0, 0, 128, 64, frame1);
  else if (frame == 1)
    screenFunctState(); 
}
      
void screenFunctState(void) {     // function state screen
  temprWriteOut(0, 64);
  u8g.drawHLine(0, 46, 128);
  u8g.setFont(u8g_font_unifont);
  if (!windowAlrt) {
    u8g.setPrintPos( 0, 14);
    u8g.print(funct_str[0]);
    u8g.setPrintPos(84, 14);
    u8g.print(heaterState);
  }
  else {
    u8g.setPrintPos( 0, 14);
    u8g.print(funct_str[3]);
  }
  if (!locked) {
    u8g.setPrintPos( 0, 28);
    u8g.print(funct_str[1]);
    u8g.setPrintPos(88, 28);
    u8g.print(airingState);
    u8g.setPrintPos( 0, 42);
    u8g.print(funct_str[2]);
    u8g.setPrintPos(95, 42);
    u8g.print(lightingState);
  }
  else {
    sysLckWriteOut();
  }
  if ((!hState) && (!aState) && (!lState)) {
    screenStndby();               // if all of controlled devices are in off, call standby screen
  }
}

void screenStndby() {             // standby scr
  u8g.firstPage(); 
    do {
      u8g.setFontRefHeightText();
      u8g.setFont(u8g_font_unifont);
      if (measError) {
        u8g.setPrintPos( 4, 64);
        u8g.print(funct_str[4]);
      }
      else {
        if (hPeriodFin) {
          byte y = 48;
          u8g.setPrintPos(4, y);
          u8g.print(minutes);
          u8g.setPrintPos(26, y);
          u8g.print("min & ");
          if (tempr > temprStart) {
            u8g.setPrintPos(68, y);
            u8g.print("+");
          }
          u8g.setPrintPos(80, y);
          u8g.print(tempr - temprStart);
          if (celsius) {
            u8g.setPrintPos(114, y);
            u8g.print(char(176));
            u8g.setPrintPos(120, y);
            u8g.print("C");
          }
          else {
            u8g.setPrintPos(114, y);
            u8g.print("F"); 
          }
          costCalc(0);
          u8g.setPrintPos(  8, y + 16);
          u8g.print(funct_str[20]);
          u8g.setPrintPos( 62, y + 16);
          u8g.print(cost, 2);
          u8g.setPrintPos(104, y + 16);
          u8g.print(funct_str[21]);
        }
        else {
          if (locked) {
          sysLckWriteOut();
          }
          else {
            u8g.setPrintPos(33, 52);
            u8g.print(funct_str[5]);
            u8g.setPrintPos( 8, 64);
            u8g.print(funct_str[6]);
          }
        }
      }
    temprWriteOut(0, 16);
  } while(u8g.nextPage());
}

void temprWriteOut (int tX, int tY) {   // draw tempr & tTarget variables onto different coordinates
  u8g.setPrintPos(tX, tY);
  u8g.setFont(u8g_font_unifont);
  char buftTarget[9];                   
  sprintf (buftTarget, "%d", tTarget);  // int to char conversion
  u8g.drawStr(tX, tY, buftTarget);
  u8g.setPrintPos(tX + 18, tY);
  u8g.print(funct_str[9]);              // tX + 18 
  u8g.setPrintPos(tX + 50, tY);         // tX + 32
  u8g.print(tempr);                     // printing temp. 
  if (celsius) {
    u8g.print(char(176)); 
    u8g.print("C");
  }
  else {
    u8g.print("F");
  }
}

void sysLckWriteOut() {
  u8g.drawBitmapP(0, 22, 2, 18, frame2);
  u8g.drawHLine( 16, 22, 112);
  u8g.drawHLine( 16, 39, 112); 
  u8g.drawVLine(127, 22,  17); 
  u8g.setPrintPos(19, 36);     
  u8g.print(funct_str[15]);
  u8g.setPrintPos(71, 36);
  u8g.print(funct_str[17]);
}

void clrScr(){
  u8g.firstPage(); 
    do {
    } while(u8g.nextPage());
}

void ledHandler() {                       // the brightHeat of a led is low, if the indicated device is off, and high, if its on
  if (!locked) {
    if (aState) {
      analogWrite(ledA, bright[2]); 
    }
    else {
      analogWrite(ledA, bright[1]); 
    }
    if (lState)  {
      analogWrite(ledL, bright[2]); 
    }
    else {
      analogWrite(ledL, bright[1]); 
    }
    if (hState) {
      if (!hThermostat) {
        ledBlnk() ;                       // the heater led blinks when the One Time Heating (15 mins) timed mode is activated,
      }
      else {
        brightHeat = bright[2] ;          // and constant bright, if the thermostat routine is active
      }
    }  
    else {
      brightHeat = bright[1] ; 
    }
    analogWrite(ledH, brightHeat);
  }
  else {
    analogWrite(ledL, bright[0]);
    analogWrite(ledA, bright[0]);
    analogWrite(ledH, bright[0]);  
  }
}

...

This file has been truncated, please download it to see its full contents.

Schematics

Central Unit - Gyula Osi
Central schem w1bmk89we1
Thermostat Unit - Gyula Osi
Thermostat schem wk75h5a8u9

Comments

Similar projects you might like

Voice Controlled Scooter Lights and Garage Door

Project tutorial by Sahil Parikh

  • 2,689 views
  • 0 comments
  • 14 respects

Bluetooth Voice Controlled Appliances with 'OK Google'

Project tutorial by sumanskd

  • 7,023 views
  • 7 comments
  • 16 respects

Voice Controlled RGB Lamp

Project tutorial by Pham Hoang Son

  • 4,548 views
  • 1 comment
  • 15 respects

Table Cleaner Voice Controlled Arduino Robot + WiFi Camera

Project showcase by KureBas Robotics

  • 17,864 views
  • 18 comments
  • 49 respects

Voice Controlled Rover

Project tutorial by Team Brink.IO

  • 2,668 views
  • 1 comment
  • 16 respects

Voice Controlled Car

Project tutorial by Yug_Ajmera

  • 47,130 views
  • 10 comments
  • 37 respects
Add projectSign up / Login