Water Heate Control
From:  
This paper gives a detailed description of a prototype of control module for a gas water heater. All manipulations over input variables, as well as control of output relays have been handled by the microcontroller Arduino Uno. The module can be controlled using a 4x4 keypad and a Nokia 3110 display, as well as over a network connection. The theoretical part describes the principle of operation of the gas water heater. There are also some topics related to the field of thermodynamics that have been discussed. A two-point controller applied to a thermodynamic system has been described as well. The paper deals with design of the module and with characteristics of hardware components that have been used. A compatible shield has been developed in order to adequately connect components to the microcontroller. The software has been created using the open-source Arduino environment. Besides standard libraries that are part of the environment, some additional libraries have been used: for graphics of the display, as well as for time interrupts. A special library for work with generic functions for reading and writing to EEPROM memory and a function which detects keystrokes have been created. Possibilities for remote control via TCP/IP (using the ethernet shield and Telnet protocol) have been clarified. The microcontroller acts as a server which can be accessed using any available Telnet client software. A Telnet php client has been developed. In combination with a web server available on a LAN network, it openes up numerous possibilites and at the same time it can provide the global Internet access to the control module. Furthermore, the module can be connected to the Internet directly through a router with Internet access. Keywords: Control module, gas water heater, embedded system, Internet controlled device
Structural block diagram of the control system
Structural block diagram of the control system
… 
Response of the system regulated by two-position controller
Response of the system regulated by two-position controller
… 
Configuration of the gas boiler and the heating process
Configuration of the gas boiler and the heating process
… 
SN74LS244
SN74LS244
… 
Ethernet shield
+11
Ethernet shield
… 
Figures - uploaded by Dajic ArminAuthor content
Content may be subject to copyright.
Gas Heater:
	
	
	
	
	
	
	
	
	
	

/*H*****************************************************
* COMM WITH DISPLAY
*******************************************************/
#include <Adafruit_GFX.h>                                  // Graphics
#include <Adafruit_PCD8544.h>             // Basic functions Nokia LCD

// ===================== DEFINES ===================================
#define XPOS 0
#define YPOS 1 // The starting position of DDRAM
#define DELTAY 2
#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16 // Size of logo

// ===================== VARIABLES ===================================
// Digital pin
// -------- DISPLAY NITIALIZATION ----------
// Adafruit_PCD8544( SCLK, SDIN, D/C, SCE, RESET);
Adafruit_PCD8544 display =
	Adafruit_PCD8544( pinKey[0], pinKey[1], pinKey[2], pinDisplaySCE, pinKey[3]);
byte pinDisplayControl = 14; // A0 as a digital pin
byte pinDisplaySCE = 9;

// ===================== PROTOTYPES ===================================

/*F*****************************************************
*
*******************************************************/
void
setup()
{
	pinMode( pinDisplayControl, OUTPUT );
	digitalWrite( pinDisplayControl, LOW );                       // Turn on
	display.begin();                                       // Initialization
	display.setContrast( 42 );
	display.display();                                               // Logo
	display.clearDisplay();                                 // Clears screen
	display.drawLine( x1, y1, x2, y2, BLACK);  // A line from (x1,y1) to (x2,y2)
	display.setTextSize( size );
	// size of text to be printed. If size =1 each character occupies 5x8
	// pixels. If size =2, then characters occupy 10x16 pixels.
	display.setCursor( x, y );             // Set cursor to default position
	// Y should be increased by 8 (if TextSize=1) to move to a new line
	display.print( F( "Text" ) );
	// Prints string message, automatically moves cursor in front of text
	display.println( F( "Text" ) );
// After printing string message go to new line and put cursor at beginning of x axis
}

char matrix[4][4] = {                           // ----- Initialization-----
	{'1','2','3','A'},
	{'4','5','6','B'},
	{'7','8','9','C'},
	{'*','0','#','D'} };

char customKey =0;                          // Pressed key, not released yet
char currKey =0;                                   // The auxiliary variable
boolean readyForKey = true;       // While true, keystroke is to be accepted
// ------------------------------------
The role of these variables is best seen in the following code snippet:
if( !readyForKey ) 
{                                       // Pressed, not released yet
	currKey = getKey();
	if( !currKey || (currKey != customKey) ) 
		readyForKey = true;
	// Released, or a new one is pressed,
	// the program is ready to execute a new key command.
}
/*F*****************************************************
*
*******************************************************/
char 
getKey() 
{
	byte red=10, kolona=10;
	Timer1.stop();
	for( byte i =0; i = 3; i++)                       // Get pressed key
	{
		pinMode( pinKey[3-i], INPUT_PULLUP); // All rows to +5V (enable pull-up
		// resistors)
		pinMode( pinKey[7-i], OUTPUT);
		digitalWrite( pinKey[7-i], LOW); // Columns to LOW
	}
	for( byte i =0; i <= 3; i++ )
	{
		if( digitalRead( pinKey[3 -i]) == LOW) 
		{                   // That’s the one
			red = 4 -i; 
			break;
		}
	}
	Timer1.resume();                 // Continue timer, as it will do all
	// necessary things to avoid wrong key detection
	// if an interrupt happenes below
	for( byte i =0; i <= 3; i++) 
	{
		pinMode( pinKey[7-i], INPUT_PULLUP); // Columns to +5V
		pinMode( pinKey[3-i], OUTPUT);
		digitalWrite( pinKey[3-i], LOW);
		// Rows to LOW
	}
	for( byte i =0; i <= 3; i++) 
	{
		if( digitalRead( pinKey[7-i]) == LOW) 
		{                       // That’s the one
			kolona = 4 -i; 
			break;
		}
	}
	red--; 
	kolona--;
	if( red > 4 || kolona > 4) 
		return( 0 );                       // IF STILL 10 SOMETHING IS WRONG
	else 
		return( matrix[red][kolona] );
}

/*H*****************************************************
*  EEPROM Stuff
*******************************************************/
#include <EEPROM.h>                         // STANDARD EEPROM LIBRARY
#include <EEPROM_anything.h>      // GENERIC FUNCTIONS, READ/WRITE ANY
					// data type from/to EEPROM
/*F*****************************************************
* Control for gas water heater
* data type from/to EEPROM
*******************************************************/
void 
ucitajEEPROM() 
{
	adresa=0;
	// The order of variables must remain unchanged even though type can
	// be changed
	din_adr[0]=adresa; adresa+=EEPROM_readAnything(adresa,Tzadano);
	din_adr[1]=adresa; adresa+=EEPROM_readAnything(adresa,Histereza);
	din_adr[2]=adresa; adresa+=EEPROM_readAnything(adresa,Dtime);
	din_adr[3]=adresa; adresa+=EEPROM_readAnything(adresa,DTmin);
	din_adr[4]=adresa; adresa+=EEPROM_readAnything(adresa,mod);
	// Take the addresses, to access and change variables later
	// EEPROM: 0,1,2,3|4,5,6,7|8,9|10,11,12,13|14
	adresa=0;
	EEPROM_limitVar(8.0,35.0,20.0,Tzadano,adresa);
	// Executed only if EEPROM has invalid data
	EEPROM_limitVar(0.5,7.0,3.0,Histereza,adresa);
	// Address automatically increases within the function
	EEPROM_limitVar(1,60,15,Dtime,adresa);
	EEPROM_limitVar(1.0,30.0,4.0,DTmin,adresa);
	if( mod>0) mod=true; else mod=false;
	EEPROM_writeAnything(adresa,mod);
}


/*F*****************************************************
*  TIME INTERRUPTS
*******************************************************/
#include <TimerOne.h> // For a time interrupt.

Timer1.initialize( 1000000 );           // INITIALIZATION OF INTERRUPT IN US
Timer1.attachInterrupt( provjeriCrtaj );  // THIS FUNCTION RUNS EVERY SECOND
byte state =1;                            // 1-MAIN SCREEN, 2-MENU, 3-PROMPT

/*F*****************************************************
* 4. TCP/IP communication and Telnet protocol
*******************************************************/

Table 4.1. 4-layer model, TCP/IP group of protocols
LayerProtocol
Application DNS, DHCP, TLS/SSL, TFTP, FTP, HTTP, IMAP, IRC, NNTP, POP3, SIP, SMTP, SNMP, SSH, Telnet, BitTorrent, RTP, rlogin, ...
TransportTCP, UDP, DCCP, SCTP, IL, RUDP, ...
InternetIP (IPv4, IPv6), ICMP, IGMP, ARP, RARP, ...
Data AccessEthernet, Wi-Fi, Token ring, PPP, SLIP, FDDI, ATM, DTM, Frame Relay, SMDS, ...
/*F***************************************************** * COMM CODE TCPIP Code *******************************************************/ #include <SPI.h> // FOR ETHERNET #include <Ethernet.h> // FOR ETHERNET #define textBuffSize 7 // Longest command plus 2 places for CR + LF byte mac[] ={ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE}; // VARIABLES FOR ETHERNET byte ip[] ={ 192, 168, 1, 177 }; char textBuff[textBuffSize]; // For received text byte charsReceived =0; boolean connectFlag =false; // To know if a new connection has been created unsigned long timeOfLastActivity; // Time of last activity [ms] unsigned long allowedConnectTime = 300000; // After 300000 ms (5 min) // of inactivity, Telnet session is closed EthernetServer server( 23 ); // Telnet, tipically goes through the port 23 EthernetClient client =0; Ethernet.begin( mac, ip); server.begin(); /*I*************************************************************** May use any ot these calls Ethernet.begin(mac); Ethernet.begin(mac, ip); Ethernet.begin(mac, ip, dns); Ethernet.begin(mac, ip, dns, gateway); Ethernet.begin(mac, ip, dns, gateway, subnet); *****************************************************************/ /*F***************************************************** * Gas Water HEater Contorl: *******************************************************/ if( server.connected() && !connectFlag) { // IF SOMEONE ACCESSED TELNET SERVER, AND SESSION IS NOT OPEN connectFlag = true; client = server.connected(); client.println( F( "Arduino Telnet Server")); client.println( F( "---------------------")); printHelpMessage(); printPrompt(); } if( connectFlag && client.available()) getReceivedText(); // IF NEW CHARACTER WAITING TO BE READ, AND FLAG IS HIGH if( connectFlag ) checkConnectionTimeout(); // If session open, check whether to do // disconnection for inactivity /*F***************************************************** * *******************************************************/ void parseReceivedText() { switch( textBuff[0] ) { case 'i' : ispisiVarijable(); break; case 'A' : promjTzad(); break; case 'B' : promjHist(); break; case 'C' : promjDTmin(); break; case 'D' : promjDtime(); break; case 'E' : modus(); break; case 'F' : bojlerch(); break; case 'R' : restart(); client.println( F(" Modul restartovan")); break; case 'c' : diskonektuj(); break; case '?' : printHelpMessage(); break; case 0x0d : break; // Enter command is ignored default: printErrorMessage(); break; } } le 4.2. The list of available commands that module recognizes
SyntaxFunction
iprint all significant variables
Axx.xchange setpoint of the room temperature (8,35). For example: A27.5
Bxx.xchange hysteresis (0.5,7). For example: B4.1
Cxx.xchange DTmin (1,30). For example: C5.2
Dxxchange Dtime (1,60). For example: D20
Exchange mod, x=0 manual, x=1 automatic. For example: E0
Euturn burner on (only in manual mod)
Eiturn burner off (only in manual mod)
Futurn boiler on
Fiturn boiler off
Rrestart of the module
cdisconnection
?help: prints all commands
After correctly connecting the module to a boiler system and to a computer (or a router) with an Ethernet cable, we run a web server on a LAN with a website implemented specifically for demonstration of the module's interface. Opening of the web page displays the following screen: