Reverse engineer RF modules

 

In this project I reversed engineered the Elro AB440 remote controller. This remote is used to enable or disable power sockets. Why is this fun? Because then I can use my Raspberry to control the power sockets in my house. With the internet browser on my phone I can make a connection to the webpage on my Raspberry and switch power sockets via an RF module. The module I used to connect to the Raspberry is this one and costs around 4 euro.

The Elro AB440

 

How to start

The basics are the same for reverse enginering all amplitude modulated devices. This includes many remote control equipment like car garage openers, dimmers, remote control fans, and these Elro remote switches off course. The first part in this project is finding the way the remote sends data (the reverse enginering part) and the last part is writing a program that will do the same thing as the remote.

Reverse enginering the RF remote

The remote

 

First thing I did is opening the remote and check for part numbers. I found the IC PT2262 and after googeling I found a datasheet here .

PT2262 pinout

 

 

According to the datasheet this IC is an encoder and pin 17 is an output. As a test I set the remote system code (the dip switch) to 10000 (set the dip switch 1 to on and 2 till 5 is set off). I used my cheap old scope and measured on this dout pin while pressing the “A on” button on the remote. I had to change the settings of my scope to 10ms/div and 2V/div to see a complete frame.

Scope output while pressing A on the elro remote.

 

This frame gets send a few times. According to the datasheet the output sends a tri-state data form. Basically that means I should recognize 3 different symbols in the above burst that represents 3 different bits. After many tests I figured out the correct bits/symbols should be:

Symbols/bits

 

 

 

Decoding the RF signal

Now I know the data signals that represent the bits. That means I can write down the above frame and I will get “abccccbccccbc”. This frame get send a couple of times and then the remote power socket gets activated. The next part is to figure out what this pattern means in relation with the system code and the button pressed. I tried many tests with the scope and pressing buttons. I wrote down the patterns and got the following:

Frames

I noticed the following:

  1. Every burst starts with an ‘a’ and then this bit is never used anymore. Getting me left with only bit b and c. So I will call ‘a’ the start bit from now on.
  2. The dip switch corrosponds with the next 5 signals. When a switch from the dipswitch is enabled then the signal changes accordingly. That means that these 5 bits corrosponds to the system code.
  3. The next 5 bits corrosponds to the buttons pressed, but the weird thing is that it was not always the case. After trying some same tests again I did get the expected signals so sometimes I received the wrong signal because the remote is from a bad quality and my scope too or something else…The code works as good as the remote so it should be oke.
  4. The last 2 bits are always the same. When I press the “on” button the bits are “bc“ and when I press the “off” buttons then the last 2 bits are always “cb”.

Coding the RF hack

First I wrote separate functions for the different bits. To get this done i need to know the exact time the signal goes on and off. To get this I zoomed in on my scope and received the following.

Frame zoom-in

 

It’s a bit difficult to see, but one division is 1 ms. I was able to export the data to a csv file and get a better estimate of the short and long signals. The short signal is about 300 micro seconds and the long signal about 920 micro seconds.

rf.c

/*
 * Copyright (c) 2009-2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of Oracle nor the names of its contributors
 *   may be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <stdio.h>
#include <stdlib.h> // for itoa
#include <wiringPi.h>
#include <string.h>

#define PINNUMBER 1
#define SHORTTIME 300
#define LONGTIME 920
#define ON 1
#define OFF 0
#define TIMESSEND 8 // code frame pg 8

int sendByteA();
int sendByteB();
int sendByteC();

int main(int argc, char**argv) {
    // argv[1] == system code
    // argv[2] == device code
    // argv[3] == on/off
    
    char param2Temp[10];
    char onTextArray[] = "on";
    int systemCodeInt;
    int deviceInt;
    int onOrOffParam;
    int tempByte2Send;
    int savedSystemCode;
    int savedDeviceCode;
    int savedOnOrOff;

    if (wiringPiSetup () == -1)
        return 1 ;

    pinMode (PINNUMBER, OUTPUT) ; // pin 6 aan de overkant zie wiringPi pinout!
      
    if( argc != 4 ) {
      printf("We need 2 arguments\n");
      return 0;
    }
    if( argc == 4 ) {
        if (strlen(argv[1]) > 3){
            printf("First parameter got to many chars\n");
            return 0;
        }
        else{
            strcpy(param2Temp, argv[1]);
            systemCodeInt = atoi(param2Temp);
            if (systemCodeInt == 0){
                printf("The systemcode parameter is not an integer\n");
                return 0;
            }
        }

        if (strlen(argv[2]) > 3){
            printf("Second parameter got to many chars\n");
            return 0;
        }
        else{
            char secParamChar = argv[2][0];
            deviceInt = secParamChar - 64;
           
            //printf("DeviceInt is: %d\n", deviceInt);
            if ((deviceInt < 0) || (deviceInt > 5)){
                printf("The device parameter is not A,B,C,D or E\n");
            }
        }
        
        if(strcmp(argv[3], onTextArray) == 0){
            onOrOffParam = 2;
        }
        else{
            onOrOffParam = 1;
        }

    }
    
    //Arguments are checked and converted to integers, now loop the data at least 5 times 
    printf("systemcode: %d, devicecode: %d, on/off: %d", systemCodeInt, deviceInt, onOrOffParam);
    
    for (int i = 0; i < TIMESSEND; i++){
        
        savedSystemCode = systemCodeInt;
        savedDeviceCode = deviceInt;
        savedOnOrOff = onOrOffParam;
        
        sendByteA();
        
        for(int j = 0; j<5; j++){ // loop 5 times to get the system code send
            tempByte2Send = systemCodeInt & 16;
            if (tempByte2Send == 16){
                sendByteB();
            }
            else{
                sendByteC();
            }
            systemCodeInt = systemCodeInt << 1;
        }
        
        for(int j = 1; j<6; j++){ // loop 5 times to get the device code send
            //tempByte2Send = 0;
            if (deviceInt  == j ){
                sendByteB();
            }
            else{
                sendByteC();
            }
            
        }
        
        for(int j = 0; j<2; j++){ // loop 2 times to send on/off
            tempByte2Send = onOrOffParam & 2;
            if (tempByte2Send == 2){
                sendByteB();
            }
            else{
                sendByteC();
            }
            onOrOffParam = onOrOffParam << 1;
        }
        
        
        delay(10);
        
        systemCodeInt = savedSystemCode;
        deviceInt = savedDeviceCode;
        onOrOffParam = savedOnOrOff;
        
    }
    
    return 0;
}

int sendByteA(){
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(LONGTIME);
}

int sendByteB(){
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(LONGTIME);
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(LONGTIME);
}

int sendByteC(){
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(LONGTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, ON) ;
    delayMicroseconds(SHORTTIME);
    digitalWrite (PINNUMBER, OFF) ;
    delayMicroseconds(LONGTIME);
}

The code starts with checking the parameters. The correct way to send a signal with this application is using the parameters as following.

./rf systemCode deviceCode on/off

For example to set system code 10000b (thats 16 in decimal, use the windows calculator) and have the ‘A’ “on” button pressed you have to use the following command on the Raspberry:

./rf 16 A on

On my Raspberry that will be:

pi@hdh_box:~/NetBeansProjects/rf/dist/Release/GNU-Linux $ ./rf 16 A on

After the code checked the parameters it will send all the bits according the parameters. Download the source code and executable here.

The hardware

The RF434 module connected to the Raspberry

 

My end result

 

For testing the 5V of the Raspberry can be used for the module, but if you need this to work on a reasonable distance then use almost 12V as power supply for the RF module only. Also connect an antenna to pin 4 of the module to extend the distance even more.

I used the same page I created on my Raspberry as in my last blog. I only changed the buttonaction.php file to send the RF signals instead of toggling gpio’s. Copy all the files to the webserver directory and open test.php on a web browser.

Download the files Buttonaction.php and Test.php here.

So far controlling RF modules with the Raspberry. In the next job the Infrared Remote will be reversed engineered. Thanks for reading!

Raspberry pi remote control

In this project I created a web page on the Raspberry pi that can be used to control power sockets or anything else that requires to be switched. The advantage of the Raspberry is the speed and the bigger memory compared to an Arduino with a ESP8266 WiFi. The disadvantage is that the Raspberry is not realtime so it’s difficult to create time critical programs, but thats not needed for this project anyway.

How does it work?

First copy all the files to your Raspberry webserver. In my case that’s the directroy /avr/www/html.

Then go to the page http://yourRaspberryIp/test.php and a page with 4 buttons appears on the screen.

Start page test.php

 

Click on a button and a led corrosponding to a particular GPIO get activated. Pressing the button again will turn off the led.

The hardware

The hardware is any Raspberry , a couple of led’s and some resistors to keep the current low.

Raspberry connection details

 

I soldered a few led’s on a pcb, but a breadboard can be used too.

The Raspberry with the leds

 

The software

There is one file called gpio and that one is the compiled ‘c’ file with the code to enable or disable a led/gpio. To enable or disable a port you have to give 2 parameters. The firs parameter is the text “on” to enable a pin or anything else to turn off a pin. The second number contains the pin number to enable or disable.

I used the wiringPi.h library for the gpio application and that means the pin’s in the library corrosponding to the physical pin’s are a as below.

WiringPi pinout

 
gpio

#include <iostream>
#include <stdio.h>
#include <stdlib.h> // for itoa
#include <wiringPi.h>
#include <string.h>

int main(int argc, char**argv) {
    // Prints welcome message...
    
    char param2Temp[10];
    char onTextArray[] = "on";
    char offTextArray[] = "off";
    int param2Int;
    
    if (wiringPiSetup () == -1)
    return 1 ;
 
    pinMode (7, OUTPUT) ; // pin 4
    pinMode (0, OUTPUT) ; // pin 6
    pinMode (3, OUTPUT) ; // pin 8
    pinMode (12, OUTPUT) ; // pin 10
    
    
    if( argc != 3 ) {
      printf("We need 2 arguments\n");
      
      return 0;
   }
    else if (argc == 3){ // is 2 params
      if (strlen(argv[1]) > 3){
          printf("First parameter got to many chars\n");
          return 0;
      }
      else{
          strcpy(param2Temp, argv[1]);
          param2Int = atoi(param2Temp);
      }
      
      if (param2Int < 1 || param2Int > 4 ){
          printf("Pin number out of range, only 1-4\n");
          return 0;
      }
      
      if(strcmp(argv[2], onTextArray) == 0){
          
          switch(param2Int){
              
              case 1:
                  digitalWrite (7, 1) ;
                  break;
              case 2:
                  digitalWrite (0, 1) ;
                  break;
              case 3:
                  digitalWrite (3, 1) ;
                  break;
              case 4:
                  digitalWrite (12, 1) ;
                  break;
              default:
                  break;
              
          }
          
      }
      else if (strcmp(argv[2], offTextArray) == 0){
          switch(param2Int){
              
              case 1:
                  digitalWrite (7, 0) ;
                  break;
              case 2:
                  digitalWrite (0, 0) ;
                  break;
              case 3:
                  digitalWrite (3, 0) ;
                  break;
              case 4:
                  digitalWrite (12, 0) ;
                  break;
              default:
                  break;
              
          }
      }
      //delay(2000);
      return 1;
  }
   

    return 0;
}

 

The file buttonaction.php is the server side scipt that will execute the gpio file on the server/Raspberry with a parameter to decide which pin to enable or disable.

buttonaction.php

<?php

$buttonName = $_GET["name"];

if($buttonName == "button1on"){
	exec('./gpio 1 on');
}
else if ($buttonName == "button1off"){
	exec('./gpio 1 off');
}
else if ($buttonName == "button2on"){
	exec('./gpio 2 on');
}
else if ($buttonName == "button2off"){
	exec('./gpio 2 off');
}
else if ($buttonName == "button3on"){
	exec('./gpio 3 on');
}
else if ($buttonName == "button3off"){
	exec('./gpio 3 off');
}
else if ($buttonName == "button4on"){
	exec('./gpio 4 on');
}
else if ($buttonName == "button4off"){
	exec('./gpio 4 off');
}


?>

 

The test.php file is the starting file you have to call within a browser. It contains a few links to the button images and executes the buttonaction.php script when clicking on the button.
test.php

<?php

?>

    
<article>
 

<h4>Rapberry GPIO:</h4>
<table>
  <tr>
    <td>Lamp livingroom</td>
    <td><img alt="" src="images/button1_on.png" id="imgClickAndChange" onclick="btn1Clicked()"  /></td>
  </tr>
<tr>
    <td>Camera garage</td>
    <td><img alt="" src="images/button2_on.png" id="img2ClickAndChange" onclick="btn2Clicked()"  /></td>
  </tr>
<tr>
    <td>Fan</td>
    <td><img alt="" src="images/button3_on.png" id="img3ClickAndChange" onclick="btn3Clicked()"  /></td>
  </tr>
<tr>
    <td>Television</td>
    <td><img alt="" src="images/button4_on.png" id="img4ClickAndChange" onclick="btn4Clicked()"  /></td>
  </tr>
</table>
    



<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
    <script type="text/javascript">

    var currentImage1 = 1;
    var currentImage2 = 1;
    var currentImage3 = 1;
     var currentImage4 = 1;

 function btn1Clicked() {   
    if (currentImage1 == 1){
	document.getElementById("imgClickAndChange").src = "images/button1_off.png";
	currentImage1 = 0;
	$.get("buttonaction.php", { name:"button1off" });

    }
    else if (currentImage1 == 0){
	document.getElementById("imgClickAndChange").src = "images/button1_on.png";
	currentImage1 = 1;
	$.get("buttonaction.php", { name:"button1on" });
    }

    return false;
 }

    function btn2Clicked() {   
    if (currentImage2 == 1){
	document.getElementById("img2ClickAndChange").src = "images/button2_off.png";
	currentImage2 = 0;
	$.get("buttonaction.php", { name:"button2off" });
    }
    else if (currentImage2 == 0){
	document.getElementById("img2ClickAndChange").src = "images/button2_on.png";
	currentImage2 = 1;
	$.get("buttonaction.php", { name:"button2on" });
    }
    return false;
    }

    function btn3Clicked() {   
    if (currentImage3 == 1){
	document.getElementById("img3ClickAndChange").src = "images/button3_off.png";
	currentImage3 = 0;
	$.get("buttonaction.php", { name:"button3off" });    }
    else if (currentImage3 == 0){
	document.getElementById("img3ClickAndChange").src = "images/button3_on.png";
	currentImage3 = 1;
	$.get("buttonaction.php", { name:"button3on" });
    }
    return false;
    }

     function btn4Clicked() {   
    if (currentImage4 == 1){
	document.getElementById("img4ClickAndChange").src = "images/button4_off.png";
	currentImage4 = 0;
	$.get("buttonaction.php", { name:"button4off" });
    }
    else if (currentImage4 == 0){
	document.getElementById("img4ClickAndChange").src = "images/button4_on.png";
	currentImage4 = 1;
	$.get("buttonaction.php", { name:"button4on" });
    }
    return false;
     }
    
</script>




</article>




 

Instead of led’s it’s also possible to use a relay-board and enable/disable power sockets. Another way is using the well know RF transmitter and receiving units. In the next project I will reverse engineer the RF remote and create an application for the Raspberry to act like an RF remote. Thanks for reading!

Android app for remote control

 

This app can be used together with Arduino, ESP8266, Raspberry pi and many other modules where you require to send/receive data via WiFi.  By pressing an on or off button on the main screen the app will send the data specified in the settings tab to your module. Your Arduino, ESP8266, Raspberry pi can use this data to enable or disable any device like (home) lamps, televisions, modems, camera’s etc.

In the settings tab you can set up a destination IP address and port number to send data after you pressed a button on the main screen. In the settings tab you can predefine the transmitting data corrosponding to a button. After pressing the button the predefined data will be send to your module and can be handled there to activate led’s, lamps, televisions, fan’s or anything your project requires.

If the module sends the same data back to this app the app will detect this and change the status on the mainscreen. This is to make sure your module has a way to confirm the required action is done completely by the module.

The Android remote buttons app can be downloaded here.

The Atmega328 code

The software is a simplefied version of the software in my last blog. The last software “web server” was a bit more complicated, because a browser sends around 500 byters on average and thats a lot for this microcontroller. The Android app only sends around 50 bytes, depending what text you predefine. In the last blog I tried to keep the buffer small and therefor I had to programm some bufferoverflow corrections and thats not needed for this project. Besides that, a web page had to be send by the microcontroller to the web browser and that also takes a lot of bytes. With the Android app we only have to send about 50 bytes, again depending on the predefined text.

The working is simple. The ESP8266 waits for an incoming TCP/IP message send by the Android app. The ESP8266 passes this message to the microcontroller. The microcontroller checks if the incoming message match with some predefined values. If not it discarge the message. If it matches a led will be triggered and a message will appear on the lcd. After that the received message will be send back to the ESP8862 via the UART and the ESP8266 will convert it to TCP/IP and sends it back to the Android app. This whole loop just continues for ever. Download the full code for the microcontroller here and see the main changes compared to the framework below.

Application1.c

/*
 * application1.c
 *
 * Created: 8-2-2018 15:07:22
 * Author: HdH
 * Description: webserver controlling 4 outputs.
 */

#define F_CPU 16000000UL // 16 MHz clock speed for delay.h
#define PIN_LED1 10
#define PIN_LED2 9
#define PIN_LED3 6
#define PIN_LED4 8

#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdbool.h>
#include <avr/pgmspace.h>
#include "gpiolib.h"
#include "i2cdisplay.h"
#include "esp8266.h"



/****************************************************************************
	Bit and byte definitions
****************************************************************************/


extern int alarmCode;


void Application1(void){

uint8_t p;

	p = readInComing(0, NULL); // Checking incoming uart data
	switch (p){
	
		case BTN1RCVDON:
			writeI2cDisplayLine("Button1 on pressed",0,1);
			writePin(PIN_LED1, HIGH);
			esp8266_send(BTN1ONTEXT);
			resetIncoming();
			break;
		case BTN1RCVDOFF:
			writeI2cDisplayLine("Button1 off pressed",0,1);
			writePin(PIN_LED1, LOW);
			esp8266_send(BTN1OFFTEXT);
			resetIncoming();
			break;
		case BTN2RCVDON:
			writeI2cDisplayLine("Button2 on pressed",0,1);
			writePin(PIN_LED2, HIGH);
			esp8266_send(BTN2ONTEXT);
			resetIncoming();
			break;
		case BTN2RCVDOFF:
			writeI2cDisplayLine("Button2 off pressed",0,1);
			writePin(PIN_LED2, LOW);
			esp8266_send(BTN2OFFTEXT);
			resetIncoming();
			break;
		case BTN3RCVDON:
			writeI2cDisplayLine("Button3 on pressed",0,1);
			writePin(PIN_LED3, HIGH);
			esp8266_send(BTN3ONTEXT);
			resetIncoming();
			break;
		case BTN3RCVDOFF:
			writeI2cDisplayLine("Button3 off pressed",0,1);
			writePin(PIN_LED3, LOW);
			esp8266_send(BTN3OFFTEXT);
			resetIncoming();
			break;
		case BTN4RCVDON:
			writeI2cDisplayLine("Button4 on pressed",0,1);
			writePin(PIN_LED4, HIGH);
			esp8266_send(BTN4ONTEXT);
			resetIncoming();
			break;
		case BTN4RCVDOFF:
			writeI2cDisplayLine("Button4 off pressed",0,1);
			writePin(PIN_LED4, LOW);
			esp8266_send(BTN4OFFTEXT);
			resetIncoming();
			break;
			
		default:
			break;
	}
}

Esp8266.h

/*
 * esp8266.h
 *
 * Created: 8-2-2018 21:08:06
 *  Author: acer
 */ 


#ifndef ESP8266_H_
#define ESP8266_H_

//#define GETRECEIVED 1
#define USERDEFINEDRECEIVED 2
#define BTN1RCVDON 3
#define BTN1RCVDOFF 4
#define BTN2RCVDON 5
#define BTN2RCVDOFF 6
#define BTN3RCVDON 7
#define BTN3RCVDOFF 8
#define BTN4RCVDON 9
#define BTN4RCVDOFF 10
#define NOTHINGSET 11

#define BTN1ONTEXT "lamp on"
#define BTN1OFFTEXT "lamp off"
#define BTN2ONTEXT "tv on"
#define BTN2OFFTEXT "tv off"
#define BTN3ONTEXT "cam on"
#define BTN3OFFTEXT "cam off"
#define BTN4ONTEXT "fan on"
#define BTN4OFFTEXT "fan off"

void esp8266_init(void);
bool esp8266_send(const char* text1);
uint8_t readInComing(int timeout, const char* text1);
void resetIncoming(void);
uint8_t checkIncomingFlag(void);
char* getIP(void);

extern unsigned long int msCounter;
struct RcvFlag rcvFlag1;

struct RcvFlag {
	bool userDefinedReceived;
	bool btn1RcvdOn;
	bool btn1RcvdOff;
	bool btn2RcvdOn;
	bool btn2RcvdOff;
	bool btn3RcvdOn;
	bool btn3RcvdOff;
	bool btn4RcvdOn;
	bool btn4RcvdOff;
};


#endif /* ESP8266_H_ */

Esp8266.c

/*
 * esp8266.c
 *
 * Created: 8-2-2018 21:07:55
 *  Author: acer
 */ 

#define F_CPU 16000000UL // 16 MHz clock speed

#include <avr/io.h>
#include <util/delay.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <avr/pgmspace.h>
#include "timer4clocklib.h"
#include "i2cdisplay.h"
#include "uart.h"
#include "esp8266.h"



/****************************************************************************
	esp8266_init(), initialize the esp8266
****************************************************************************/
void esp8266_init(){
	
	uint8_t p;
	
	// Check the status of the ESP8266 if not ok reset
	_delay_ms(500);
	serialWrite("AT", CRLF);
	p = readInComing(1000, "OK\r\n");
	if (p != 2){
		writeI2cDisplayLine("Reset needed!", 0, 1);
		_delay_ms(4000);
		serialWrite("AT+RST", CRLF);
		p = readInComing(3000, "OK\r\n");
		if (p == 2){
			writeI2cDisplayLine("Reset done OK!", 0, 1);
			_delay_ms(1000);
		}
	}
	else{
		writeI2cDisplayLine("ESP8266 conn. OK", 0, 1);
		_delay_ms(1000);
	}
	
	// reset all flags generated by incoming data from the ESP8266 and empty the buffer.
	resetIncoming();
	
	
	// configure the server and activate the server on the ESP8266
	serialWrite("AT+CIPMUX=1", CRLF);
	p = readInComing(1000, "OK\r\n");
	if (p == 2){
		resetIncoming();
		writeI2cDisplayLine("AT+CIPMUX=1 set", 0, 1);
		_delay_ms(1000);
		serialWrite("AT+CIPSERVER=1,80", CRLF); //\r\n
		p = readInComing(1000, "OK\r\n");
		if (p == 2){
			resetIncoming();
			writeI2cDisplayLine("AT+CIPSERVER set", 0, 1);
		}
	}
	
	// init is done so clean up all flags again.
	resetIncoming();
}

/****************************************************************************
	esp8266_send, send uart data to the esp8266
****************************************************************************/
bool esp8266_send(const char* text1){
	
	int p;
	int textLength;
	char buf1[50] = {'\0'};
	char itoaBuf[3];


	if (text1 != NULL){
		strcpy(buf1, (char *) text1);
		textLength = strlen(buf1);
	}
	else{
		textLength = 0;
	}
	
	// the esp8266 requires the amount of bytes that will be send in chars.
	itoa(textLength+2, itoaBuf ,10);
	serialWrite("AT+CIPSEND=0,", NO_EOL);
	serialWrite(itoaBuf, CRLF);
	
	// the data is send wait on the esp8266 for a confirmation
	resetIncoming();
	p = readInComing(1000, "OK\r\n");
	if (p == 2){
		resetIncoming();
		writeI2cDisplayLine(buf1,0,2);
		serialWrite(buf1, CRLF);
		p = readInComing(500, "SEND OK\r\n");
		if (p == 2){
			resetIncoming();
			return true;
		}
		else{
			return false;
		}
	}
	else{
		return false;
	}
		
}

/****************************************************************************
	checkInComing, check/read data received by the esp8266
****************************************************************************/
uint8_t readInComing(int timeOut, const char* text1){ // ipv int
	
	char buf1[30] = {'\0'};
	uint8_t flag;
	
	uint8_t p; //unsigned char p;
	
	uint8_t lengthUsrDef;
	uint8_t lengthBtn1On, lengthBtn1Off, lengthBtn2On, lengthBtn2Off, lengthBtn3On, lengthBtn3Off, lengthBtn4On, lengthBtn4Off;
	
	uint8_t posUsrDef = 0;
	uint8_t posBtn1On = 0;
	uint8_t posBtn1Off = 0;
	uint8_t posBtn2On = 0;
	uint8_t posBtn2Off = 0;
	uint8_t posBtn3On = 0;
	uint8_t posBtn3Off = 0;
	uint8_t posBtn4On = 0;
	uint8_t posBtn4Off = 0;
	
	lengthBtn1On = strlen(BTN1ONTEXT);
	lengthBtn1Off = strlen(BTN1OFFTEXT);
	lengthBtn2On = strlen(BTN2ONTEXT);
	lengthBtn2Off = strlen(BTN2OFFTEXT);
	lengthBtn3On = strlen(BTN3ONTEXT);
	lengthBtn3Off = strlen(BTN3OFFTEXT);
	lengthBtn4On = strlen(BTN4ONTEXT);
	lengthBtn4Off = strlen(BTN4OFFTEXT);
	
	
	if (text1 != NULL){
		strcpy(buf1, (char *) text1);
		lengthUsrDef = strlen(buf1);
	}
	else{
		lengthUsrDef = 0;
	}
	
	flag = checkIncomingFlag();			
	if (flag != NOTHINGSET){
		return flag;
	}

	
	if(timeOut != 0){ // user gives predefined time
		unsigned long stop = msCounter + timeOut;
		do{
			p = getChar();
			while (p != 0x0){
				
				posUsrDef = (p == buf1[posUsrDef]) ? posUsrDef + 1 : 0;
				if (lengthUsrDef > 0 && posUsrDef == lengthUsrDef){ // user defined
					rcvFlag1.userDefinedReceived = true;
					return USERDEFINEDRECEIVED;
				}
				p = getChar();
			}
		}while (msCounter < stop); } else if(timeOut == 0){ // if no time out set, used in the app to continuously check for incoming data p = getChar(); while (p != 0){ posUsrDef = (p == buf1[posUsrDef]) ? posUsrDef + 1 : 0; posBtn1On = (p == BTN1ONTEXT[posBtn1On]) ? posBtn1On + 1 : 0; posBtn1Off = (p == BTN1OFFTEXT[posBtn1Off]) ? posBtn1Off + 1 : 0; posBtn2On = (p == BTN2ONTEXT[posBtn2On]) ? posBtn2On + 1 : 0; posBtn2Off = (p == BTN2OFFTEXT[posBtn2Off]) ? posBtn2Off + 1 : 0; posBtn3On = (p == BTN3ONTEXT[posBtn3On]) ? posBtn3On + 1 : 0; posBtn3Off = (p == BTN3OFFTEXT[posBtn3Off]) ? posBtn3Off + 1 : 0; posBtn4On = (p == BTN4ONTEXT[posBtn4On]) ? posBtn4On + 1 : 0; posBtn4Off = (p == BTN4OFFTEXT[posBtn4Off]) ? posBtn4Off + 1 : 0; if (lengthUsrDef > 0 && posUsrDef == lengthUsrDef){ // user defined
				rcvFlag1.userDefinedReceived = true;
				return USERDEFINEDRECEIVED;
			}
			if (lengthBtn1On > 0 && posBtn1On == lengthBtn1On){ // button 1 on
				rcvFlag1.btn1RcvdOn = true;
				return BTN1RCVDON;
			}
			if (lengthBtn1Off > 0 && posBtn1Off == lengthBtn1Off){ // button 1 off
				rcvFlag1.btn1RcvdOff = true;
				return BTN1RCVDOFF;
			}
			if (lengthBtn2On > 0 && posBtn2On == lengthBtn2On){ // button 2 on
				rcvFlag1.btn2RcvdOn = true;
				return BTN2RCVDON;
			}
			if (lengthBtn2Off > 0 && posBtn2Off == lengthBtn2Off){ // button 2 off
				rcvFlag1.btn2RcvdOff = true;
				return BTN2RCVDOFF;
			}
			if (lengthBtn3On > 0 && posBtn3On == lengthBtn3On){ // button 3 on
				rcvFlag1.btn3RcvdOn = true;
				return BTN3RCVDON;
			}
			if (lengthBtn3Off > 0 && posBtn3Off == lengthBtn3Off){ // button 3 off
				rcvFlag1.btn3RcvdOff = true;
				return BTN3RCVDOFF;
			}
			if (lengthBtn4On > 0 && posBtn4On == lengthBtn4On){ // button 4 on
				rcvFlag1.btn4RcvdOn = true;
				return BTN4RCVDON;
			}
			if (lengthBtn4Off > 0 && posBtn4Off == lengthBtn4Off){ // button 4 off
				rcvFlag1.btn4RcvdOff = true;
				return BTN4RCVDOFF;
			}
			
			p = getChar();
		}
	}
		
	return NOTHINGSET;
}

/****************************************************************************
	checkIncomingFlag, check the current status of what message is received at last
****************************************************************************/
uint8_t checkIncomingFlag(){
	

	if(rcvFlag1.userDefinedReceived == true){
		return USERDEFINEDRECEIVED;
	}
	
	else if(rcvFlag1.btn1RcvdOn == true){
		return BTN1RCVDON;
	}
	
	else if(rcvFlag1.btn1RcvdOff == true){
		return BTN1RCVDOFF;
	}
	
	else if(rcvFlag1.btn2RcvdOn == true){
		return BTN2RCVDON;
	}
	
	else if(rcvFlag1.btn2RcvdOff == true){
		return BTN2RCVDOFF;
	}
	
	else if(rcvFlag1.btn3RcvdOn == true){
		return BTN3RCVDON;
	}
	
	else if(rcvFlag1.btn3RcvdOff == true){
		return BTN3RCVDOFF;
	}
	
	else if(rcvFlag1.btn4RcvdOn == true){
		return BTN4RCVDON;
	}
	
	else if(rcvFlag1.btn4RcvdOff == true){
		return BTN4RCVDOFF;
	}
	else{
		return NOTHINGSET;
	}
}

/****************************************************************************
	resetIncoming, reset all flags to prepare for the next expected data from the esp8266
****************************************************************************/

void resetIncoming(){
	
	rcvFlag1.userDefinedReceived  = false;
	rcvFlag1.btn1RcvdOn = false;
	rcvFlag1.btn1RcvdOff = false;
	rcvFlag1.btn2RcvdOn = false;
	rcvFlag1.btn2RcvdOff = false;
	rcvFlag1.btn3RcvdOn = false;
	rcvFlag1.btn3RcvdOff = false;
	rcvFlag1.btn4RcvdOn = false;
	rcvFlag1.btn4RcvdOff = false;
}


/****************************************************************************
	getIP, just to know the IP as i forgot to make it static instead of dhcp...
****************************************************************************/

char* getIP(){
	
	uint8_t p;
	int posIpReceived = 0;
	char matchStaip[] = "STAIP,\"";
	int ipCharsCounter = 0;
	int staIpLength = strlen(matchStaip);
	static char tempBuffer[16];
	bool matched = false;
	
	serialWrite("AT+CIFSR", CRLF);
	_delay_ms(200); // wait for all the data from this cmd
	p = getChar();
	while (p != 0x0){
		
		if(p == matchStaip[posIpReceived]){
			posIpReceived++;
		}
		else{
			posIpReceived = 0;
		}
		
		if (staIpLength > 0 && posIpReceived == staIpLength){ // STAIP received
			matched = true;
		}
		
		if(matched == true){
			tempBuffer[ipCharsCounter] = p;
			ipCharsCounter++;
			if (ipCharsCounter > 16){
				ipCharsCounter = 0;
				matched = false;
				return tempBuffer;
			}
			
		}
		p = getChar();
	}
	
	return NULL;
	
}

Uart.h

/*
 * uart.h
 *
 * Created: 8-12-2017 13:36:22
 *  Author: acer
 */


#ifndef UART_H_
#define UART_H_

/****************************************************************************
	Bit and byte definitions
****************************************************************************/
#define CLOCKSPEED_CPU   16000000
#define BAUD    57600
#define BRC     ((CLOCKSPEED_CPU/16/BAUD) - 1)
#define TX_BUFFER_SIZE  40
#define RX_BUFFER_SIZE  200 // it's a bit big because of the CIFSR cmd returns a lot of bytes used in getIp()
#define CR 1
#define LF 2
#define CRLF 3
#define NO_EOL 4



/****************************************************************************
	Function definitions
****************************************************************************/

void UART_Init();
void serialWrite(char c[], uint8_t eol);
void UART_Transmit(unsigned char data );
char getChar(void);


#endif /* UART_H_ */

Uart.c

/*
 * uart.c
 *
 * Created: 8-12-2017 13:36:09
 *  Author: acer
 */ 

#define F_CPU 16000000UL // 16 MHz clock speed


#include <avr/io.h>
#include <stdlib.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdbool.h>
#include "uart.h"


/****************************************************************************
	Bit and byte definitions
****************************************************************************/
char rxBuffer[RX_BUFFER_SIZE];
uint8_t rxWritePos = 0;
uint8_t rxReadPos = 0;


/****************************************************************************
	USART_Init, see important info below
****************************************************************************/
void UART_Init(){

	UBRR0H = (unsigned char)(BRC >> 8); //baud rate register set to 9600 BAUD
	UBRR0L = (unsigned char) BRC;

	UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1 << RXCIE0);
	UCSR0C = (1 << UCSZ01) | (3<<UCSZ00);
}


/****************************************************************************
	serialWrite, send data with optional crlf
****************************************************************************/
void serialWrite(char c[], uint8_t eol)
{
	int i;
	
	for(i = 0; i < strlen(c); i++)// put the string you want to write in a buffer. { UART_Transmit(c[i]); //put the char's one by one in the buffer } if (eol == CR){ UART_Transmit(0xd); //CR \r } else if (eol == LF){ UART_Transmit(0xa); //LF \n } else if (eol == CRLF){ UART_Transmit(0xd); //CR \r UART_Transmit(0xa); //LF \n } else if (eol == NO_EOL){ return; } } /**************************************************************************** ISR(USART_RX_vect), Fills up a buffer. ****************************************************************************/ ISR(USART_RX_vect) { rxBuffer[rxWritePos] = UDR0;// rxWritePos++; if(rxWritePos >= RX_BUFFER_SIZE)
	{
		rxWritePos = 0;
	}

}

/****************************************************************************
	USART_Transmit, 
****************************************************************************/
void UART_Transmit(unsigned char data ){
	
	while( !( UCSR0A & (1<<UDRE0)) ); // Wait for empty transmit buffer UDR0 = data; // Put data into buffer, sends the data } /**************************************************************************** getChar, ****************************************************************************/ char getChar(void) { char ret = '\0'; _delay_us(500); // to prevent reading faster then the bytes are coming in if(rxReadPos != rxWritePos) { ret = rxBuffer[rxReadPos]; rxReadPos++; if(rxReadPos >= RX_BUFFER_SIZE)//
		{
			rxReadPos = 0;
		}
	}
	
	return ret;
}

 

Thanks for reading!

Winsock c++ server app

Screendump of the server application

In this project I build a Windows application in c++ that can do some networking tasks. The application can start a webserver and that will be handy for testing purposes in the near future. In a future project I will create an android application to control a microcontroller via TCP/IP. To test this android application the webserver comes in handy to check which bytes are actually send or received. Some other features are a DNS resolver to get an IP address corrosponding to a site, a TCP/IP client that works a bit like a telnet client and an option to check the network services running in the background of the computer. This last feature comes in handy in cases where you can’t make an TCP/IP connection because some service in the background is holding the connection.

The software

I wrote the application using the Netbeans IDE. This IDE uses Mingw as a compiler for Windows. If you’re interessted in the code in can be download here including the servertool.exe file in the “server_tool\dist\Release\MinGW-Windows” directory.

The project can be loaded directly, but before the compiling works you have to add the following options in the linker options: -lws2_32 –liphlpapi -static-libgcc -static-libstdc++

The code is using the Windows winsock.dll, because this file got a lot of API’s to write some networking functionallity. The linker knows the application is using the winsock by linking the libraries with the options -lws2_32 –liphlpapi.

The linker options -static-libgcc -static-libstdc++   are needed to generate an executable with this library build into the executable. So basically it makes it possible to start the servertool.exe without any errors.

Proper linker options

 

The source code is not that much so it’s located all in 1 file, main.cpp

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/* 
 * File:   main.cpp
 * Author: acer
 *
 * Created on 9 februari 2018, 14:09
 */

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <iphlpapi.h>
#include <wchar.h> //for wcslen..

#pragma comment(lib,"ws2_32.lib") //Winsock Library
#pragma comment(lib,"Iphlpapi.lib")

#define FAIL 1
#define SUCCESS 0

int WSAinitialisatie();
int NameToIP();
int ClientTCPSocket(char *inet_address, int port_number);
int ClientUDPSocket(char *inet_address, int port_number);
int WhatToSend(int transportLayer, char ipAddress[], int portNum );
int Receiving();
int printMainMenu();
int startClientSession();
int getNetworkInfo();
int startTCPServer();
int getPortNumber();
int startUDPServer();
int initServer();

WSADATA wsa;
struct hostent *he;
struct in_addr **addr_list;
SOCKET s;
SOCKET new_socket;
struct sockaddr_in client;
struct sockaddr_in server;
struct sockaddr_in si_other;

char *message , server_reply[2000];
int recv_size;
char hostname[255];


/****************************************************************************
 main loop
****************************************************************************/
int main(int argc, char *argv[]) {
	
    int menuItem;
    int status;
 	
    if (WSAinitialisatie() == 0)
        printf("Initialised.\n");
    else{
        printf("Winsock.dll error\n");
    }
	
    while(1){
	
        menuItem = printMainMenu();
		
        switch (menuItem){
            case 1:
                getNetworkInfo();
		break;
                
            case 2:
                if (NameToIP() == SUCCESS)
                    printf("IP correctly resolved!\n");		
		break;
                
            case 3:
                if (startClientSession() == SUCCESS){
                    printf("Session correctly closed\n");
		}
		break;
                
            case 4:
                status = initServer();
		if (status == SUCCESS){
                    printf("Server closed\n");
                }
                else
                    printf("Number out of range (choose 1 or 2)\n");
                
                break;
                
            case 5:
            	printf("Use: netstat -p TCP -a -b in case running as administrator is not possible\n");
            	system("netstat -p TCP -a -b");
            	break;
            	
            case 6:
            	printf("Use: netstat -p TCP -a -b in case running as administrator is not possible\n");
            	system("netstat -p UDP -a -b");
            	break;
            	
            case 7:
                printf("Exit...\n");
		closesocket(s);
                return SUCCESS;
		break;
                
            default:
                closesocket(s);
                return 0;
                break;
        }
    }
    closesocket(s);
    WSACleanup();
    printf("Socket closed and winsock process closed.\n");
    return SUCCESS;
}

/****************************************************************************
 initServer(), initialize a server an starts the startTCPServer() or startUdpServer()
****************************************************************************/

int initServer(){
    char transpLyrSelected[10];
    int transpLyrCnvrt2Int;
    
    printf("Please enter transport layer:\n");
    printf("1. TCP\n");
    printf("2. UDP\n");
    fgets(transpLyrSelected,10,stdin);
    if (strlen(transpLyrSelected) > 2){
        printf("Error! to many chars\n");
        return FAIL;
    }
    
    if ((transpLyrSelected[0] > '2') || (transpLyrSelected[0] < '0') ){
        printf("choose a number (1 or 2)\n");
    }

    transpLyrCnvrt2Int = transpLyrSelected[0] - '0';
    if(transpLyrCnvrt2Int == 1){
        startTCPServer();
    }
    else if(transpLyrCnvrt2Int == 2){
        startUDPServer();
    }
    return SUCCESS;
}

/****************************************************************************
 startUDPServer(), start server for UDP packets
****************************************************************************/
int startUDPServer(){
    
    int slen , recv_len;
    char buf[1500];
    int receivePort;
    //struct sockaddr_in si_other;
    int iOptVal = 0;
    int iOptLen = sizeof (int);
    int iResult = 0;

    slen = sizeof(si_other);
	
    printf("Enter UDP port number: ");
    receivePort = getPortNumber();//convert char array to int
    printf("Entered port number is: %d\n", receivePort );
	//Create a socket
	//if((s = socket(AF_INET , SOCK_DGRAM , 0 )) == INVALID_SOCKET)
    if((s = socket(AF_INET , SOCK_DGRAM , 0 )) == INVALID_SOCKET)
    {
	printf("Could not create socket : %d" , WSAGetLastError());
    }
    printf("UDP Socket created.\n");
	
	//Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( receivePort );
    
	
	//Bind
    if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)
    {
	printf("Bind failed with error code : %d" , WSAGetLastError());
	return 1;
    }
    printf("Server is running, waiting for client...");

iResult = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *) &iOptVal, iOptLen);
    if (iResult == SOCKET_ERROR)
        wprintf(L"getsockopt for SO_KEEPALIVE failed with error: %u\n", WSAGetLastError());

    //keep listening for data
    while(1){
        printf("Waiting for data...");
	memset(buf,'\0', 1500);//clear buffer
		
	//try to receive some data, this is a blocking call
	//if ((recv_len = recvfrom(s, buf, 1500, 0, (struct sockaddr *) &si_other, &slen)) == SOCKET_ERROR){
	if ((recv_len = recvfrom(s, buf, 1500, 0, (struct sockaddr *) &si_other, &slen)) == SOCKET_ERROR){
            printf("recvfrom() failed with error code : %d" , WSAGetLastError());
            return 1;
	}
	printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
	printf("Data: %s\n" , buf);
	if (buf[0] == 'q' && strlen(buf) < 3){ printf("Client send exit command. Exit...\n"); return 0; } //now reply the client with the same data if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == SOCKET_ERROR){ printf("sendto() failed with error code : %d" , WSAGetLastError()); return 1; } } closesocket(s); WSACleanup(); return 0; } /**************************************************************************** getPortNumber(), gets a port number from the user ****************************************************************************/ int getPortNumber(){ char give_port[10]; int portNumber, lengthPortNumber; if(fgets(give_port, 10, stdin) != NULL){ if ((strlen(give_port)>0) && (give_port[strlen (give_port) - 1] == '\n'))
	        	give_port[strlen (give_port) - 1] = '\0';
        
        lengthPortNumber = strlen(give_port);
        if(lengthPortNumber > 5){
            printf("Number to big, max 5 digits\n");
            if(fgets(give_port, 10, stdin) == NULL){
                return FAIL;
            }
        }
        
        
        for(int i=0; i<lengthPortNumber; i++){ if ((give_port[i] > '9') || (give_port[i] < '0')){
                printf("Enter only numbers\n");
                return FAIL;
            }
        }
        portNumber = atoi (give_port);
    }
    else{
        return FAIL;
    }
    
    return portNumber;
} 
    
/****************************************************************************
 startTCPServer(), start server for TCP packets
****************************************************************************/
int startTCPServer(){
    int sizeCientStruct, i;
    int recPort;
    char replyChoise[1];
    char *portChoise;
    char messageSend[150];
    int slen;
    int receiveStatus;
    //struct sockaddr_in si_other;
    
    slen = sizeof(si_other);
    //Create a socket
    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d" , WSAGetLastError());
        return FAIL;
    }
  
    printf("Enter port number: ");
    recPort = getPortNumber();
    if (recPort == FAIL){
        return FAIL;
    }
    
    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( recPort );
      
    //Bind
    if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code : %d" , WSAGetLastError());
        return FAIL;
    }
    printf("Socket ok, TCP server is running...\n");
    //Listen to incoming connections
    listen(s , 3);
    //Accept and incoming connection
    printf("Waiting for incoming connections...\n");
      
    sizeCientStruct = sizeof(struct sockaddr_in);
    new_socket = accept(s, (struct sockaddr *) &client, &sizeCientStruct);
    if (new_socket == INVALID_SOCKET)
    {
        printf("accept failed with error code : %d" , WSAGetLastError());
        return 1;
    }
    
    //initializing the messageSend buffer with 0
    for(i=0; i<150; i++){
    	messageSend[i] = 0;
    }
    
    printf("Connected\n");
    printf("1. automatic reply\n");
    printf("2. choose reply\n");
    fgets(replyChoise, 10, stdin);
    //printf("a, Waiting for message....\n");
    switch(replyChoise[0]){
  		
    case '1':
        printf("Waiting for data...(q is quit)");
  	while(1){
            //receiveStatus = recv_size = recvfrom(new_socket , server_reply , 2000 , 0, (struct sockaddr *) &si_other, &slen);
            if((recv_size = recvfrom(new_socket , server_reply , 2000 , 0, (struct sockaddr *) &si_other, &slen)) == SOCKET_ERROR){
                puts("recv failed");
                return FAIL;
            }
            else if (recv_size == 0){
                printf("The client closed the connection...bye\n");
                closesocket(s);
		return SUCCESS;
            }
            server_reply[recv_size] = '\0';
            
            printf("Received:\t %s\nWaiting for message... ", server_reply);
            if ((server_reply[0] == 'q') && (strlen(server_reply) <= 2) ){
		printf("The client closed the connection...bye\n");
                closesocket(s);
		return SUCCESS;
            }
            send(new_socket , server_reply , strlen(server_reply) , 0);
	}
        break;
    case '2':
        //printf("b, Waiting for message....\n");
    	while (1){
            printf("Waiting for message....");
            if((recv_size = recvfrom(new_socket , server_reply , 2000 , 0, (struct sockaddr *) &si_other, &slen)) == SOCKET_ERROR){
		printf("recv failed");
                return FAIL;
            }
            server_reply[recv_size] = '\0';
            //printf("\nReceived from %s:%d %s\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), server_reply);
            printf("\nReceived:\t\t %s", server_reply);
            if (server_reply[0] == 'q' && (strlen(server_reply) < 3) ){
		printf("Received exit form client...");
		closesocket(s);
		return SUCCESS;
            }
            printf("Send:\t\t\t ");
            fgets(messageSend, 150, stdin);
            //printf("d, Waiting for message....\n");
            if ((messageSend[0] == 'q') && (strlen(messageSend) <= 2)){ printf("Server closed the connection...\n"); closesocket(s); return SUCCESS; } send(new_socket , messageSend , strlen(messageSend) , 0); } break; default: break; } closesocket(s); } /**************************************************************************** getNetworkInfo(), collects all kind of computers network info ****************************************************************************/ int getNetworkInfo(){ IP_ADAPTER_INFO *pAdapterInfo; IP_ADAPTER_ADDRESSES *pAdapterAddresses; FIXED_INFO *pFixedInfo; IP_ADDR_STRING *pIPAddr; PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; char *bufferDescription; size_t lengteDescription; int cmpReturn; ULONG ulOutBufLen; ULONG ulOutBufLenAdapter; DWORD dwRetVal; ULONG flags = GAA_FLAG_INCLUDE_PREFIX; ULONG family = AF_UNSPEC; ULONG outBufLen = 0; ULONG Iterations = 0; UINT i; time_t leaseObtained; time_t leaseExpires; pFixedInfo = (FIXED_INFO *) malloc(sizeof (FIXED_INFO)); pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) ); ulOutBufLen = sizeof (FIXED_INFO); ulOutBufLenAdapter = sizeof(IP_ADAPTER_INFO); outBufLen = sizeof(IP_ADAPTER_ADDRESSES); //Get the networkParams if (GetNetworkParams(pFixedInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { free(pFixedInfo); pFixedInfo = (FIXED_INFO *) malloc(ulOutBufLen); if (pFixedInfo == NULL) { printf("Error allocating memory needed to call GetNetworkParams\n"); } } if (dwRetVal = GetNetworkParams(pFixedInfo, &ulOutBufLen) != NO_ERROR) { printf("GetNetworkParams failed with error %d\n", dwRetVal); if (pFixedInfo) { free(pFixedInfo); } } //Get the adapterInfo if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLenAdapter) != ERROR_SUCCESS) { free (pAdapterInfo); pAdapterInfo = (IP_ADAPTER_INFO *) malloc ( ulOutBufLenAdapter ); } if ((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLenAdapter)) != ERROR_SUCCESS) { printf("GetAdaptersInfo call failed with %d\n", dwRetVal); } //Start printing the info printf("Host Name: \t%s\n", pFixedInfo->HostName);
if (strlen(pFixedInfo->DomainName) != 0){
    printf("\tDomain Name: %s\n", pFixedInfo->DomainName);
}

printf("Node Type: ");
switch (pFixedInfo->NodeType) {
case 1:
    printf("\t%s, best setting for small LAN\n", "Broadcast");
    break;
case 2:
    printf("\t%s check wins is active else other systems can't be find on LAN\n", "Peer to peer");
    break;
case 4:
    printf("\t%s\n", "Mixed");
    break;
case 8:
    printf("\t%s\n", "Hybrid");
    break;
default:
    printf("\n");
}
        
printf("DNS Servers:\n");
printf("\t\t%s\n", pFixedInfo->DnsServerList.IpAddress.String);

pIPAddr = pFixedInfo->DnsServerList.Next;
while (pIPAddr) {
    printf("\t\t%s\n", pIPAddr->IpAddress.String);
    pIPAddr = pIPAddr->Next;
}



if (strlen(pFixedInfo->ScopeId) != 0){
    printf("***********************\n");
    printf("\tNetBIOS Scope ID: %s\n", pFixedInfo->ScopeId);
    printf("HUGE ERROR! This option is from the winNT netBIOS stone age\n");
    printf("Remove this in WINS config tab in TCP/IP properties\n");
    printf("***********************\n");
}


if (pFixedInfo->EnableRouting){
    printf("***********************\n");
    printf("\tIP Routing Enabled: YES\n");
    printf("Possible 2 NICS and therefor routing enabled\n");
    printf("Use route print command for more info\n");
    printf("DISABLE at HKEY_LOCAL_MACHINE SYSTEM CurrentControlSet  Services Tcpip Parameters IPEnableRouter\n");
    printf("***********************\n");
    
}
//else
//    printf("\tIP Routing Enabled: No\n");

if (pFixedInfo->EnableProxy){
    printf("***********************\n");
    printf("\tWINS Proxy Enabled: YES\n");
    printf("Error possible Internet connection sharing (ICS) used\n");
    printf("if not? disable WINS Proxy\n");
    printf("HKEY_LOCAL_MACHINE SYSTEM CurrentControlSet Services NetBT Parameters EnableProxy\n");
    printf("***********************\n");
}

if (pFixedInfo->EnableDns){
    printf("***********************\n");
    printf("\tNetBIOS Resolution Uses DNS: Yes\n");
    printf("NetBIOS still used...Disable at WINS tab in TCP/IP settings.\n");
    printf("check also LMHOST file\n");
    printf("***********************\n");
}


//Get adapterAdresses
do {
    pAdapterAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen);
    if (pAdapterAddresses == NULL) {
        printf
            ("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
        exit(1);
    }
    dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAdapterAddresses, &outBufLen);
    if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
        free(pAdapterAddresses);
        pAdapterAddresses = NULL;
    } else {
        break;
    }
    Iterations++;
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < 3)); PIP_ADAPTER_INFO pAdapter = pAdapterInfo; printf("\n"); //next chapter after DNS info while (pAdapter) { pCurrAddresses = pAdapterAddresses; while (pCurrAddresses) { lengteDescription = wcslen(pCurrAddresses->Description);
        bufferDescription = (char *) malloc((lengteDescription+1) * sizeof(char));
        if (bufferDescription == NULL){
            printf("We are fucked!\n");
        }
        WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, pCurrAddresses->Description, lengteDescription, bufferDescription, lengteDescription, NULL, NULL);
        bufferDescription[lengteDescription] = 0;
        cmpReturn = strcmp(bufferDescription, pAdapter->Description);
        if (cmpReturn == 0){
            if (wcslen(pCurrAddresses->DnsSuffix) != 0){
                printf("DNS Suffix:\t%S\n", pCurrAddresses->DnsSuffix);
            }
            printf("Friendly name:\t%S\n", pCurrAddresses->FriendlyName);
            if(pCurrAddresses->OperStatus == 1){
                
                //system("COLOR 2");
                printf("Status:\t\tUP!\n");
            }
            else
                printf("Status:\t\tDOWN\n");
          
            
        }
    pCurrAddresses = pCurrAddresses->Next;
    free(bufferDescription);
    }
    
    printf("Adapter name: \t%s\n", pAdapter->Description);
    printf("Adapter Addr: \t");
    for (i = 0; i < pAdapter->AddressLength; i++) {
        if (i == (pAdapter->AddressLength - 1))
            printf("%.2X\n",(int)pAdapter->Address[i]);
        else
            printf("%.2X-",(int)pAdapter->Address[i]);
    }
    printf("IP Address: \t%s\n", pAdapter->IpAddressList.IpAddress.String);
    printf("IP Mask: \t%s\n", pAdapter->IpAddressList.IpMask.String);
    printf("Gateway: \t%s\n", pAdapter->GatewayList.IpAddress.String);
    
    if (pAdapter->DhcpEnabled) {
        
        if(strlen(pAdapter->DhcpServer.IpAddress.String) != 0){
            printf("DHCP Server: \t%s\n", pAdapter->DhcpServer.IpAddress.String);
            leaseObtained = pAdapter->LeaseObtained;
            leaseExpires = pAdapter->LeaseExpires;
            printf("Lease from:\t%s", ctime(&leaseObtained));
            printf("Lease till:\t%s", ctime(&leaseExpires));
        }
        else
           printf("\t\tDHCP ENABLED\n");
    }
    else
        printf("\tDHCP DISABLED\n");
	
    pAdapter = pAdapter->Next;
    printf("\t***\n");
}


        
if (pAdapterInfo)
    free(pAdapterInfo);
if (pFixedInfo) {
    free(pFixedInfo);
    pFixedInfo = NULL;
}
    
    
    
}

/****************************************************************************
 startClientSession(), initialize a session followed by ClientTCPSocket() of ClientUDPSocket
****************************************************************************/
int startClientSession(){
	
	int n;
	char give_ip[50];
	char giveSocketType[10];
	int portNumber;
	int stopSending = 0;
        int socketSelected;
	
	printf("Enter IP (1 for default 127.0.0.1):\n");
	fgets(give_ip, 50, stdin);
	n = strlen(give_ip);
	if (n < 2){ printf("ERROR To less chars quiting...\n"); return FAIL; } if ((strlen(give_ip)>0) && (give_ip[strlen (give_ip) - 1] == '\n'))
	        	give_ip[strlen (give_ip) - 1] = '\0';
	if ((give_ip[0] == '1') && (n < 3)){ strcpy(give_ip, "127.0.0.1"); } printf("\nEnter a port number: \n"); portNumber = getPortNumber(); if(portNumber == FAIL){ return FAIL; } printf("\nPlease choose type of socket to create:\n"); printf("1, TCP\n"); printf("2. UDP\n"); fgets(giveSocketType, 10, stdin); if ((strlen(giveSocketType)>0) &&
                (giveSocketType[strlen (giveSocketType) - 1] == '\n'))
                    giveSocketType[strlen (giveSocketType) - 1] = '\0';
	n = strlen(giveSocketType);
	if (n > 1){
            printf("Use only 1 digit, choose a number (1 or 2)\n");
            return FAIL;
        }
		
        if ((giveSocketType[0] > '2') || (giveSocketType[0] < '0') ){ printf("choose a number (1 or 2)\n"); return FAIL; } socketSelected = giveSocketType[0] - '0'; if(socketSelected == 1){ if (ClientTCPSocket(give_ip, portNumber) == 0){ printf("TCP Socket OK! with IP %s and port %d\n", give_ip, portNumber); } else{ printf("\nFailed to create socket, please check ip and port %s:%d\n", give_ip, portNumber); return FAIL; } } else if(socketSelected == 2){ if (ClientUDPSocket(give_ip, portNumber) == 0){ printf("UDP Socket OK! with IP %s and port %d\n", give_ip, portNumber); } else{ printf("\nFailed to create socket, please check ip and port %s:%d\n", give_ip, portNumber); return FAIL; } } WhatToSend(socketSelected, give_ip, portNumber); } /**************************************************************************** printMainMenu(), displays the main menu interface ****************************************************************************/ int printMainMenu(){ char inputMenu[10]; int itemSelected; int inputLength; do { printf("***********************************************\n"); printf("1. Get network information\n"); printf("2. Resolve hostname to IP (DNS check)\n"); printf("3. Client connect socket (Firewall test TX)\n"); printf("4. Server connect socket (Firewall test RX)\n"); printf("5. List services occupying TCP ports\n"); printf("6. List services occupying UDP ports\n"); printf("7. Exit\n"); printf("***********************************************\n"); fgets(inputMenu, 10, stdin); inputLength = strlen(inputMenu); }while(inputLength == 1); if (inputLength > 2)
		printf("Number to big, use only 1 digit\n");
        
        if(inputMenu[0] > '8' || inputMenu[0] < '1'){ printf("Number out of range.\n"); } if ((strlen(inputMenu)>0) && (inputMenu[strlen (inputMenu) - 1] == '\n'))
	        	inputMenu[strlen (inputMenu) - 1] = '\0';
	
	itemSelected = inputMenu[0] - '0';
	return itemSelected;
}

/****************************************************************************
 Receiving(), used to receive data from a server. Used for client mode
****************************************************************************/
int Receiving(){
	int i;
	
	for(i=0; i<2000; i++){//empty buffer for just in case
		server_reply[i] = 0;
	}
	
	if((recv_size = recv(s , server_reply , 2000 , 0)) == SOCKET_ERROR)
	{
		printf("recv failed");
		return 1;
	}
	
	printf("Reply received:");
	//Add a NULL terminating character to make it a proper string before printing
	server_reply[recv_size] = '\0';
	printf("%s\n", server_reply);
	if ((server_reply[0] == 'q') && (strlen(server_reply) < 3 )){ return 2; //2 means quit has send by server } return 0; } /**************************************************************************** WhatToSend(), some options to send predefined data ****************************************************************************/ int WhatToSend(int transportLayer, char ipAddress[], int portNum ){ char a[10]; int n; int itemSelected; char your_message[200]; //struct sockaddr_in si_other; int slen=sizeof(si_other); char buf[1500]; if(transportLayer == 1){ printf("Please select what to send?\n"); printf("1. GET / HTTP/1.1\\r\\n\\r\\n\n"); printf("2. Send a written message\n"); printf("3. Stop sending\n"); fgets(a,10,stdin); if (strlen(a) > 2){
		printf("ERROR, To many chars.\n");
		return FAIL;
	}
	
	itemSelected = a[0] - '0';
	printf("De itemSelected is: %d\n", itemSelected);
	switch (itemSelected){
		
            case 1:
		message = "GET / HTTP/1.1\r\n\r\n";
		if( send(s , message , strlen(message) , 0) < 0)
		{
                    printf("Send failed");
                    return FAIL;
		}
		Receiving();
		break;
            case 2:
			//while ((your_message[0] != 'q') && (strlen(your_message) <= 2)){ while (1){ printf("\nPlease write a message (q to quit): "); fgets(your_message, 200, stdin); n = strlen(your_message); if ((strlen(your_message)>0) && (your_message[strlen (your_message) - 1] == '\n'))
	   	    	your_message[strlen (your_message) - 1] = '\0';
	   	    
                                
                    if( send(s , your_message , n , 0) < 0)
                    {	
			printf("Send failed");
			return FAIL;
                    }
                    if (Receiving() == 2){
			printf("Server closed the connection...bye\n");
			return SUCCESS;
                    }
                    if(your_message[0] == 'q' && n<3){
                        return SUCCESS;
                    }	
		}
			
                    break;
		case 3:
                    printf("Stopped sending\n");
                    closesocket(s);
                    return 2;
                    break;
		default:
                    printf("Stopped sending\n");
                    closesocket(s);
                    return 2;
                    break;	
		
	}
    }	
    else if (transportLayer == 2){

        while(1){
    
            printf("\nPlease write a message (q to quit): ");
            fgets(your_message, 200, stdin);
            printf("send:\t%s\n", your_message);	
            //send the message
            if (sendto(s, your_message, strlen(your_message) , 0 , (struct sockaddr *) &si_other, slen) == SOCKET_ERROR){
		printf("sendto() failed with error code : %d" , WSAGetLastError());
		return FAIL;
            }
            if (your_message[0] == 'q')
                return SUCCESS;
            memset(buf,'\0', 1500);
            //try to receive some data, this is a blocking call
            if (recvfrom(s, buf, 1500, 0, (struct sockaddr *) &si_other, &slen) == SOCKET_ERROR){
		printf("recvfrom() failed with error code : %d" , WSAGetLastError());
		return FAIL;
            }
	
            printf("%s", buf);
        }
    
    }	
	
    return SUCCESS;
}

/****************************************************************************
 ClientTCPSocket(), build the TCP socket
****************************************************************************/
int ClientTCPSocket(char *inet_address, int port_number){
	
    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET){
        printf("Could not create socket : %d" , WSAGetLastError());
    }
	
    server.sin_addr.s_addr = inet_addr(inet_address);//"212.61.147.8"
    server.sin_family = AF_INET;
    server.sin_port = htons( port_number );//80
	//Connect to remote server
    if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0){ printf("connect error %s %ui", inet_address, port_number); return 1; } printf("Connected to IP %s:%d\n",inet_address, port_number); return SUCCESS; } /**************************************************************************** ClientUDPSocket(), build the UDP socket ****************************************************************************/ int ClientUDPSocket(char *inet_address, int port_number){ //Create a socket if((s = socket(AF_INET , SOCK_DGRAM, IPPROTO_UDP )) == INVALID_SOCKET){ printf("Could not create socket : %d" , WSAGetLastError()); } //server.sin_addr.s_addr = inet_addr(inet_address);//"212.61.147.8" si_other.sin_addr.S_un.S_addr = inet_addr(inet_address);//"212.61.147.8" si_other.sin_family = AF_INET; si_other.sin_port = htons( port_number );//80 //Connect to remote server //printf("Ready to transmit UDP on IP %s and port %d\n",inet_address, port_number); return 0; } /**************************************************************************** NameToIP(), resolve ip from name. Usefull to analyze the http1.1 protocol ****************************************************************************/ int NameToIP(){ char ip[100]; int i = 0; printf("Give a hostname to get the IP address\n"); fgets(hostname, 255, stdin); if ((strlen(hostname)>0) && (hostname[strlen (hostname) - 1] == '\n'))
       	hostname[strlen (hostname) - 1] = '\0';
	
    printf("\nHostname %s ", hostname);
    if ( (he = gethostbyname( hostname ) ) == NULL){
	printf("gethostbyname failed : %d" , WSAGetLastError());
	return 1;
    }
	
    //Cast the h_addr_list to in_addr , since h_addr_list also has the ip address in long format only
    addr_list = (struct in_addr **) he->h_addr_list;
	
    for(i = 0; addr_list[i] != NULL; i++) {
        strcpy(ip , inet_ntoa(*addr_list[i]) );
    }
    //DeIP = ip;
    printf("is resolved to : %s\n\n" , ip);
    return 0;
}

/****************************************************************************
 WSAinitialisatie(), winsock.dll needs to be initialized
****************************************************************************/
int WSAinitialisatie(){
	
    printf("\nInitialising Winsock...\n");
    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0){
        printf("Failed. Error Code : %d",WSAGetLastError());
	return FAIL;
    }
    return SUCCESS;
}






 

I hope you can use this code for your own projects and thanks for reading!

 

Programming an IoT webserver

 

In this project I build a webserver with the ESP8266 as the interface between the TCP/IP protocol and the Atmega328 microcontroller. The interesting part is that both parts are very cheap and that makes it a practical way to connect home devices to the internet. It’s a bit basic now with only a few buttons to turn 4 led’s on or off via a webpage, but the led’s can be replaced with relay’s and a proper current driver for the relays. Then it will be possible to control all your home equipment remotely from any location in the world with an internet connection.

The hardware setup

In the schematic I removed the connection details of the lcd display to get the schematic on 1 page. In this blog you can find these connections. Keep in mind that the logic level converter and ESP8266 requires 3.3V instead of 5V.

Schematic esp8266 with the Atmega328p

 

The software

I preconfigured the esp8266 in my last blog with the settings that will not change like the ssid and WiFi password and the baudrate. All other settings are set in the esp8266_init() function in the esp8266 library. This library also contains a function to send data to the esp. The esp requires a noticiation of the amount of bytes that are going to be send and after an ok is received by the microcontroller the bytes can be send so just sending with the uart send function doesn’t work.

The basic working of the program is text getting send to the esp by an internet browser. The microcontroller reads it and checks it against some predefined text. If it matches then the display will show this and a GPIO gets turned on or off.

Because I only have 2Kb of data memory and a basic page with 8 buttons takes a lot of bytes, i used the PROGMEM macro to save the page in the program memory, because i have enough free space there.

The incoming UART data gets automatically saved via interrupts. To save some space I used a smaller buffer then the actual bytes that get send by an web browser. For this project only the first ca. 50 bytes send by the browser are interesting, because these bytes tells which button is pressed. So the rest of the bytes will not be used and a buffer overflow var is set. After the incoming message is handelled the buffer overflow can be reset by the flushBuffer function.

Download the full code here or have a look below.

esp8266.h

/*
 * esp8266.h
 *
 * Created: 8-2-2018 21:08:06
 *  Author: acer
 */ 


#ifndef ESP8266_H_
#define ESP8266_H_

#define GETRECEIVED 1
#define USERDEFINEDRECEIVED 2
#define BTN1RCVDON 3
#define BTN1RCVDOFF 4
#define BTN2RCVDON 5
#define BTN2RCVDOFF 6
#define BTN3RCVDON 7
#define BTN3RCVDOFF 8
#define BTN4RCVDON 9
#define BTN4RCVDOFF 10
#define NOTHINGSET 11



void esp8266_init(void);
bool esp8266_send(const char* text1);
uint8_t readInComing(int timeout, const char* text1);
void resetIncoming(void);
uint8_t checkIncomingFlag(void);
char* getIP(void);

extern unsigned long int msCounter;
struct RcvFlag rcvFlag1;

struct RcvFlag {
	bool getReceived;
	bool userDefinedReceived;
	bool btn1RcvdOn;
	bool btn1RcvdOff;
	bool btn2RcvdOn;
	bool btn2RcvdOff;
	bool btn3RcvdOn;
	bool btn3RcvdOff;
	bool btn4RcvdOn;
	bool btn4RcvdOff;
};


#endif /* ESP8266_H_ */

esp8266.c

/*
 * esp8266.c
 *
 * Created: 8-2-2018 21:07:55
 *  Author: acer
 */ 

#define F_CPU 16000000UL // 16 MHz clock speed

#include <avr/io.h>
#include <util/delay.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <avr/pgmspace.h>
#include "timer4clocklib.h"
#include "i2cdisplay.h"
#include "uart.h"
#include "esp8266.h"

/****************************************************************************
	Bit and byte definitions
****************************************************************************/

char getReceived[] = "GET / HTTP/1.1";
char btn1RcvdOn[] = "GET /?p1=on HTTP/1.1";
char btn1RcvdOff[] = "GET /?p1=off HTTP/1.1";
char btn2RcvdOn[] = "GET /?p2=on HTTP/1.1";
char btn2RcvdOff[] = "GET /?p2=off HTTP/1.1";
char btn3RcvdOn[] = "GET /?p3=on HTTP/1.1";
char btn3RcvdOff[] = "GET /?p3=off HTTP/1.1";
char btn4RcvdOn[] = "GET /?p4=on HTTP/1.1";
char btn4RcvdOff[] = "GET /?p4=off HTTP/1.1";

/****************************************************************************
	esp8266_init(), initialize the esp8266
****************************************************************************/
void esp8266_init(){
	
	uint8_t p;
	
	// Check the status of the ESP8266 if not ok reset
	_delay_ms(500);
	serialWrite("AT", CRLF);
	p = readInComing(1000, "OK\r\n");
	if (p != 2){
		writeI2cDisplayLine("Reset needed!", 0, 1);
		_delay_ms(1000);
		serialWrite("AT+RST", CRLF);
		p = readInComing(2000, "OK\r\n");
		if (p == 2){
			writeI2cDisplayLine("Reset done OK!", 0, 1);
			_delay_ms(1000);
		}
	}
	else{
		writeI2cDisplayLine("ESP8266 conn. OK", 0, 1);
		_delay_ms(1000);
	}
	
	// reset all flags generated by incoming data from the ESP8266 and empty the buffer.
	resetIncoming();
	flushBuffer();
	
	// configure the server and activate the server on the ESP8266
	serialWrite("AT+CIPMUX=1", CRLF);
	p = readInComing(1000, "OK\r\n");
	if (p == 2){
		resetIncoming();
		flushBuffer();
		writeI2cDisplayLine("AT+CIPMUX=1 set", 0, 1);
		_delay_ms(1000);
		serialWrite("AT+CIPSERVER=1,80", CRLF); //\r\n
		p = readInComing(1000, "OK\r\n");
		if (p == 2){
			resetIncoming();
			writeI2cDisplayLine("AT+CIPSERVER set", 0, 1);
			//_delay_ms(1000);
		}
	}
	
	// init is done so clean up all flags again.
	resetIncoming();
}

/****************************************************************************
	esp8266_send, send uart data to the esp8266
****************************************************************************/
bool esp8266_send(const char* text1){
	
	int p;
	int textLength;
	char buf1[700] = {'\0'};
	char itoaBuf[3];

	// the PROGMEM memory for saving the data memory usage.
	if (text1 != NULL){
		strcpy_P(buf1, (char *) text1);
		textLength = strlen(buf1);
	}
	else{
		textLength = 0;
	}
	
	// the esp8266 requires the amount of bytes that will be send in chars.
	itoa(textLength+2, itoaBuf ,10);
	serialWrite("AT+CIPSEND=0,", NO_EOL);
	serialWrite(itoaBuf, CRLF);
	
	// the data is send wait on the esp8266 for a confirmation
	p = readInComing(1000, "OK\r\n");
	if (p == 2){
		resetIncoming();
		flushBuffer();
		serialWrite(buf1, CRLF);
		p = readInComing(500, "SEND OK\r\n");
		if (p == 2){
			resetIncoming();
			flushBuffer();
			return true;
		}
		else{
			return false;
		}
	}
	else{
		return false;
	}
		
}

/****************************************************************************
	checkInComing, check/read data received by the esp8266
****************************************************************************/
uint8_t readInComing(int timeOut, const char* text1){ // ipv int
	
	char buf1[30] = {'\0'};
	uint8_t flag;
	
	uint8_t p; //unsigned char p;
	
	uint8_t lengthGetRcvd;
	uint8_t lengthUsrDef;
	uint8_t lengthBtn1On, lengthBtn1Off, lengthBtn2On, lengthBtn2Off, lengthBtn3On, lengthBtn3Off, lengthBtn4On, lengthBtn4Off;
	
	
	uint8_t posGetRcvd = 0;
	uint8_t posUsrDef = 0;
	uint8_t posBtn1On = 0;
	uint8_t posBtn1Off = 0;
	uint8_t posBtn2On = 0;
	uint8_t posBtn2Off = 0;
	uint8_t posBtn3On = 0;
	uint8_t posBtn3Off = 0;
	uint8_t posBtn4On = 0;
	uint8_t posBtn4Off = 0;
	
	lengthGetRcvd = strlen(getReceived);
	lengthBtn1On = strlen(btn1RcvdOn);
	lengthBtn1Off = strlen(btn1RcvdOff);
	lengthBtn2On = strlen(btn2RcvdOn);
	lengthBtn2Off = strlen(btn2RcvdOff);
	lengthBtn3On = strlen(btn3RcvdOn);
	lengthBtn3Off = strlen(btn3RcvdOff);
	lengthBtn4On = strlen(btn4RcvdOn);
	lengthBtn4Off = strlen(btn4RcvdOff);
	
	
	
	if (text1 != NULL){
		strcpy(buf1, (char *) text1);
		lengthUsrDef = strlen(buf1);
	}
	else{
		lengthUsrDef = 0;
	}
	
	flag = checkIncomingFlag();			
	if (flag != NOTHINGSET){
		return flag;
	}

	
	if(timeOut != 0){ // user gives predefined time
		unsigned long stop = msCounter + timeOut;
		do{
			p = getChar();
			while (p != 0x0){
				
				posUsrDef = (p == buf1[posUsrDef]) ? posUsrDef + 1 : 0;
				if (lengthUsrDef > 0 && posUsrDef == lengthUsrDef){ // user defined
					rcvFlag1.userDefinedReceived = true;
					return USERDEFINEDRECEIVED;
				}
				p = getChar();
			}
		}while (msCounter < stop); } else if(timeOut == 0){ // if no time out set, used in the app to continuously check for incoming data p = getChar(); while (p != 0){ //writeI2cDisplay(p, DATAREGISTER); posGetRcvd = (p == getReceived[posGetRcvd]) ? posGetRcvd + 1 : 0; posUsrDef = (p == buf1[posUsrDef]) ? posUsrDef + 1 : 0; posBtn1On = (p == btn1RcvdOn[posBtn1On]) ? posBtn1On + 1 : 0; posBtn1Off = (p == btn1RcvdOff[posBtn1Off]) ? posBtn1Off + 1 : 0; posBtn2On = (p == btn2RcvdOn[posBtn2On]) ? posBtn2On + 1 : 0; posBtn2Off = (p == btn2RcvdOff[posBtn2Off]) ? posBtn2Off + 1 : 0; posBtn3On = (p == btn3RcvdOn[posBtn3On]) ? posBtn3On + 1 : 0; posBtn3Off = (p == btn3RcvdOff[posBtn3Off]) ? posBtn3Off + 1 : 0; posBtn4On = (p == btn4RcvdOn[posBtn4On]) ? posBtn4On + 1 : 0; posBtn4Off = (p == btn4RcvdOff[posBtn4Off]) ? posBtn4Off + 1 : 0; if (lengthGetRcvd > 0 && posGetRcvd == lengthGetRcvd){ // "GET / HTTP/1.1"
				rcvFlag1.getReceived = true;
				return 1;
			}
			if (lengthUsrDef > 0 && posUsrDef == lengthUsrDef){ // user defined
				rcvFlag1.userDefinedReceived = true;
				return 2;
			}
			if (lengthBtn1On > 0 && posBtn1On == lengthBtn1On){ // button 1 on
				rcvFlag1.btn1RcvdOn = true;
				return 3;
			}
			if (lengthBtn1Off > 0 && posBtn1Off == lengthBtn1Off){ // button 1 off
				rcvFlag1.btn1RcvdOff = true;
				return 4;
			}
			if (lengthBtn2On > 0 && posBtn2On == lengthBtn2On){ // button 2 on
				rcvFlag1.btn2RcvdOn = true;
				return 5;
			}
			if (lengthBtn2Off > 0 && posBtn2Off == lengthBtn2Off){ // button 2 off
				rcvFlag1.btn2RcvdOff = true;
				return 6;
			}
			if (lengthBtn3On > 0 && posBtn3On == lengthBtn3On){ // button 3 on
				rcvFlag1.btn3RcvdOn = true;
				return 7;
			}
			if (lengthBtn3Off > 0 && posBtn3Off == lengthBtn3Off){ // button 3 off
				rcvFlag1.btn3RcvdOff = true;
				return 8;
			}
			if (lengthBtn4On > 0 && posBtn4On == lengthBtn4On){ // button 4 on
				rcvFlag1.btn4RcvdOn = true;
				return 9;
			}
			if (lengthBtn4Off > 0 && posBtn4Off == lengthBtn4Off){ // button 4 off
				rcvFlag1.btn4RcvdOff = true;
				return 10;
			}
			
			p = getChar();
		}
	}
		
	return NOTHINGSET;
}

/****************************************************************************
	checkIncomingFlag, check the current status of what message is received at last
****************************************************************************/
uint8_t checkIncomingFlag(){
	
	
	if(rcvFlag1.getReceived == true){
		return GETRECEIVED;
	}
	else if(rcvFlag1.userDefinedReceived == true){
		return USERDEFINEDRECEIVED;
	}
	
	else if(rcvFlag1.btn1RcvdOn == true){
		return BTN1RCVDON;
	}
	
	else if(rcvFlag1.btn1RcvdOff == true){
		return BTN1RCVDOFF;
	}
	
	else if(rcvFlag1.btn2RcvdOn == true){
		return BTN2RCVDON;
	}
	
	else if(rcvFlag1.btn2RcvdOff == true){
		return BTN2RCVDOFF;
	}
	
	else if(rcvFlag1.btn3RcvdOn == true){
		return BTN3RCVDON;
	}
	
	else if(rcvFlag1.btn3RcvdOff == true){
		return BTN3RCVDOFF;
	}
	
	else if(rcvFlag1.btn4RcvdOn == true){
		return BTN4RCVDON;
	}
	
	else if(rcvFlag1.btn4RcvdOff == true){
		return BTN4RCVDOFF;
	}
	else{
		return NOTHINGSET;
	}
}

/****************************************************************************
	resetIncoming, reset all flags to prepare for the next expected data from the esp8266
****************************************************************************/

void resetIncoming(){
	
	
	rcvFlag1.getReceived = false;
	rcvFlag1.userDefinedReceived  = false;
	rcvFlag1.btn1RcvdOn = false;
	rcvFlag1.btn1RcvdOff = false;
	rcvFlag1.btn2RcvdOn = false;
	rcvFlag1.btn2RcvdOff = false;
	rcvFlag1.btn3RcvdOn = false;
	rcvFlag1.btn3RcvdOff = false;
	rcvFlag1.btn4RcvdOn = false;
	rcvFlag1.btn4RcvdOff = false;
}


/****************************************************************************
	getIP, just to know the IP as i forgot to make it static instead of dhcp...
****************************************************************************/

char* getIP(){
	
	uint8_t p;
	int posIpReceived = 0;
	char matchStaip[] = "STAIP,\"";
	int ipCharsCounter = 0;
	int staIpLength = strlen(matchStaip);
	static char tempBuffer[16];
	bool matched = false;
	
	serialWrite("AT+CIFSR", CRLF);
	_delay_ms(200); // wait for all the data from this cmd
	p = getChar();
	while (p != 0x0){
		
		if(p == matchStaip[posIpReceived]){
			posIpReceived++;
		}
		else{
			posIpReceived = 0;
		}
		
		if (staIpLength > 0 && posIpReceived == staIpLength){ // STAIP received
			matched = true;
		}
		
		if(matched == true){
			tempBuffer[ipCharsCounter] = p;
			ipCharsCounter++;
			if (ipCharsCounter > 16){
				ipCharsCounter = 0;
				matched = false;
				flushBuffer();
				return tempBuffer;
			}
			
		}
		p = getChar();
	}
	
	return NULL;
	
}

application1.c

/*
 * application1.c
 *
 * Created: 8-2-2018 15:07:22
 * Author: HdH
 * Description: webserver controlling 4 outputs.
 */

#define F_CPU 16000000UL // 16 MHz clock speed for delay.h
#define PIN_LED1 10
#define PIN_LED2 9
#define PIN_LED3 6
#define PIN_LED4 8

#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdbool.h>
#include <avr/pgmspace.h>
#include "gpiolib.h"
#include "i2cdisplay.h"
#include "uart.h"
#include "esp8266.h"


void sendPage(void);

/****************************************************************************
	Bit and byte definitions
****************************************************************************/

const char htmlHeader[] PROGMEM = "HTTP/1.1 200 OK\r\nContent-Location: /buttons.html\r\nContent-Type: text/html\r\n";
const char htmlPart1[] PROGMEM = "<!DOCTYPE HTML><html><head><title>hdh</title></head><body>

<form method=\"GET\" >
"
"

 GPIO1 <input type=\"submit\" name=\"p1\" value=\"on\"><input type=\"submit\" name=\"p1\" value=\"off\">

"
"

 GPIO2 <input type=\"submit\" name=\"p2\" value=\"on\"><input type=\"submit\" name=\"p2\" value=\"off\">

"
"

 GPIO3 <input type=\"submit\" name=\"p3\" value=\"on\"><input type=\"submit\" name=\"p3\" value=\"off\">

"
"

 GPIO4 <input type=\"submit\" name=\"p4\" value=\"on\"><input type=\"submit\" name=\"p4\" value=\"off\">

"
"</form>


</body></html>\r\n";


extern int alarmCode;


void Application1(void){

uint8_t p;

	p = readInComing(0, NULL); // Checking incoming uart data
	switch (p){
		
		case GETRECEIVED:
			writeI2cDisplayLine("Get received",0,1);
			sendPage();
			break;
		case BTN1RCVDON:
			writeI2cDisplayLine("Button1 on pressed",0,1);
			writePin(PIN_LED1, HIGH);
			sendPage();
			break;
		case BTN1RCVDOFF:
			writeI2cDisplayLine("Button1 off pressed",0,1);
			writePin(PIN_LED1, LOW);
			sendPage();
			break;
		case BTN2RCVDON:
			writeI2cDisplayLine("Button2 on pressed",0,1);
			writePin(PIN_LED2, HIGH);
			sendPage();
			break;
		case BTN2RCVDOFF:
			writeI2cDisplayLine("Button2 off pressed",0,1);
			writePin(PIN_LED2, LOW);
			sendPage();
			break;
		case BTN3RCVDON:
			writeI2cDisplayLine("Button3 on pressed",0,1);
			writePin(PIN_LED3, HIGH);
			sendPage();
			break;
		case BTN3RCVDOFF:
			writeI2cDisplayLine("Button3 off pressed",0,1);
			writePin(PIN_LED3, LOW);
			sendPage();
			break;
		case BTN4RCVDON:
			writeI2cDisplayLine("Button4 on pressed",0,1);
			writePin(PIN_LED4, HIGH);
			sendPage();
			break;
		case BTN4RCVDOFF:
			writeI2cDisplayLine("Button4 off pressed",0,1);
			writePin(PIN_LED4, LOW);
			sendPage();
			break;
			
		default:
			break;
	}
}

void sendPage(){

	flushBuffer();
	resetIncoming();
	
	esp8266_send(htmlHeader);
	esp8266_send(htmlPart1);
	
	serialWrite("AT+CIPCLOSE=5", CRLF);
	
}

uart.h

/*
 * uart.h
 *
 * Created: 8-12-2017 13:36:22
 *  Author: acer
 */


#ifndef UART_H_
#define UART_H_

/****************************************************************************
	Bit and byte definitions
****************************************************************************/
#define CLOCKSPEED_CPU   16000000
#define BAUD    57600
#define BRC     ((CLOCKSPEED_CPU/16/BAUD) - 1)
#define TX_BUFFER_SIZE  40
#define RX_BUFFER_SIZE  300
#define CR 1
#define LF 2
#define CRLF 3
#define NO_EOL 4



/****************************************************************************
	Function definitions
****************************************************************************/

void UART_Init();
void serialWrite(char c[], uint8_t eol);
void UART_Transmit(unsigned char data );
uint8_t getChar(void);
void flushBuffer(void);


#endif /* UART_H_ */

uart.c

/*
 * uart.c
 *
 * Created: 8-12-2017 13:36:09
 *  Author: acer
 */ 

#define F_CPU 16000000UL // 16 MHz clock speed


#include <avr/io.h>
#include <stdlib.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdbool.h>
#include "uart.h"


/****************************************************************************
	Bit and byte definitions
****************************************************************************/
char rxBuffer[RX_BUFFER_SIZE];
uint8_t rxWritePos = 0;
uint8_t rxReadPos = 0;
bool bufferOverFlow = false;
long bufferOverFlowWaitTime;


/****************************************************************************
	USART_Init, see important info below
****************************************************************************/
void UART_Init(){

	UBRR0H = (unsigned char)(BRC >> 8); //baud rate register set to 9600 BAUD
	UBRR0L = (unsigned char) BRC;

	UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1 << RXCIE0);
	UCSR0C = (1 << UCSZ01) | (3<<UCSZ00);
}


/****************************************************************************
	serialWrite, send data with optional crlf
****************************************************************************/
void serialWrite(char c[], uint8_t eol)
{
	int i; //was uint8
	
	//_delay_us(100);
	for(i = 0; i < strlen(c); i++)// put the string you want to write in a buffer. { UART_Transmit(c[i]); //put the char's one by one in the buffer } if (eol == CR){ UART_Transmit(0xd); //CR \r } else if (eol == LF){ UART_Transmit(0xa); //LF \n } else if (eol == CRLF){ UART_Transmit(0xd); //CR \r UART_Transmit(0xa); //LF \n } else if (eol == NO_EOL){ return; } } /**************************************************************************** ISR(USART_RX_vect), Fills up a buffer, to keep the buffer not to big I used a buffer overflow var in case the buffer exceeds. Internet explorer sends almost 500 bytes. the buffer overflow gets a reset by the flushbuffer function. ****************************************************************************/ ISR(USART_RX_vect) { if(rxWritePos >= RX_BUFFER_SIZE - 1){
		bufferOverFlow = true;
	}
	if (bufferOverFlow == false){
		rxBuffer[rxWritePos+1] = 0x0;
		rxBuffer[rxWritePos] = UDR0;//
		rxWritePos++;
	}
}

/****************************************************************************
	USART_Transmit, 
****************************************************************************/
void UART_Transmit(unsigned char data ){
	
	while( !( UCSR0A & (1<<UDRE0)) ); // Wait for empty transmit buffer
	UDR0 = data; // Put data into buffer, sends the data
}


/****************************************************************************
	getChar, 
****************************************************************************/


uint8_t getChar(void)
{
	uint8_t ret = 0x0;  // '\0';
	
	_delay_us(500); // to prevent reading faster then the bytes are coming in
	if(rxWritePos != 0){
		ret = rxBuffer[rxReadPos];
		rxReadPos++;
		if(ret == 0){ // last byte read, start from the beginning
			flushBuffer();
		}
	}
	return ret;
}


void flushBuffer(void){
	
	for(int i=0; i<RX_BUFFER_SIZE; i++){
		rxBuffer[i] = 0;
	}
	rxWritePos = 0;
	rxReadPos = 0;
	bufferOverFlow = false;
}

 

Even a simple webpage uses relatively a lot of memory when using a microcontroller. To get a better user interface I will make an android application to control the microcontroller instead of a web browser in a later blog. So stay tuned and thanks for reading!

 

ESP8266 IoT programming

ESP8266 WiFi module

The ESP8266 WiFi module converts TCP/IP data to UART. This comes in handy if you want to connect a microcontroller to the internet. This module only costs a few euro’s and has a lot of functionallity. If you are interested in this module then i hope this blog will be usefull and will save you some time setting up the module. I’m planning to create an Android application for my phone to control different devices like lamps, computer, media set, etc, in my house. To get to this I already created a lot of software for the microcontroller (see my other blogs) so now it’s time to explore the ESP8266 and get this module working.

The ESP8266 hardware

There are a few things to keep in mind when you connect the ESP8266 to your computer.

  1. The module needs a lot of current specially when booting up. Use a power supply that can deliver at least 400mA. Symptoms of to less current is a blinking blue led, but you are able to communicate with the module or the unit keeps rebooting.
  2. The ESP8266 requires 3.3V for the rx and tx lines and your computer with rs232 uses about 12V for rx and tx. So make sure you level to the correct voltage, see example below. If you use more then 3.3V on the module it will burn!
  3. The module needs a 3.3V power supply and the microcontroller (Atmegaa328) needs 5V. A voltage regulator is needed at least for the 3.3V power supply and must be able to deliver at least 400mA.
ESP8266 setup
ESP8266 connected to a computer schematic

 

Above is the schematic how to connect the ESP8266 to a computer. Left is the module itself. To get the 3.3V power for the unit I used this  voltage regulator. It can provide 800mA with cooling. In this project no cooling is needed, because the module consumes less then 400mA.  The middle module is a level converter to convert from 3.3V to 5V and vice versa. It is needed for the Atmega328 microcontroller, but for testing and configuring the module on a computer only it is not needed yet and can be by-passed. For later testing it is nice to connect the microcontroller and a computer at the same time to see what is going on, but feel free to leave the logic level converter out for now and connect the ESP8266 directly to the rs232 logic converter. As I don’t have an rs232 port on my laptop I used an USB to rs232 converter.

Communicating with the ESP8266

The ESP8266 comes with different default baudrate settings. The best way to figure out what is the correct baudrate is trying them all… I used TeraTerm client to control and test the module and in my case the baudrate is default 115200 bps. The port number can be found at the device settings so in my case it’s com9.

Select the correct com port.

 

The module requires an “enter” after each AT command. This can be set in the terminal setup by changing the transmit new-line to CR+LF.

Tera term settings
Tera term port settings.

 

There is a complete list with commands for the module available here. For now I will connect the unit to my home WiFi modem and save the settings. After this it will always connect automatically and I don’t have to program the microcontroller to this. Another thing todo is to configure the module as a server and specify a port to listen to, but this will be done my the micrcontroller too so it’s only for testing now.

Commands to set up a permanent WiFi connection

 

AT+GMR – show the current firmware version. Just to check if the communication is working.
AT+CWMODE=3 – set’s the module to “SoftAP+Station mode”.
AT-CWLAP – gives a list with WiFi AP’s in the neighbourhood. I blacked it out a bit for security reasons.
AT+CWJAP_DEF=”TP-LINK_A6A8″,”password” – connects the module to the WiFi and saves the settings in the flash memory.

If a different baud rate is required then use the command “AT+UART_DEF=115200,8,1,0,0” to make it definite and get it saved in the flash memory.

The following configures the module as a server, all though this will be done by the micro controller later on.

Setting up the server.

 

AT-CIPMUX=1 – Enables multiple connections instead of just 1 connection at a time with the module.
AT+CIPSERVER=1,80 – The ‘1’ creates a server and listens to port 80.
AT+CIFSR – shows the ip the module received.

Open a browser and connect to the correct ip (in my case 192.168.0.101)  The module received the text send by the browser so the module works fine.  In the next blog I will connect the esp8266 to a microcontroller and make it possible to control devices via the web browser. Thanks for reading!

 

Access control with RFID

In my last blog I created a library for UART communication. Now it can be used for an 125KHz RFID module. This module uses the UART to communicate with a microcontroller. After reading an RFID key it will send a 14 digit code over the UART. In this project the software reads the codes and compares it with a hard coded RFID code. When the codes are the same the user gets access. The results are shown on a lcd in this project, but it can also be send to a computer or where ever you want. See the below movie to get a view of the RFID project.

 

Mostly these types of modules are used to give someone access to a building or get some user data. Another nice thing is setting up a predifined configuration at home. For example some particular lights at home will light up for a user1 and other lights will light up when an other user scans his RFID key. Besides that it would also be fun if the TV gets turned on a particular channel corrosponding to an RFID code. The plan is setting this up in a future blog. For now see the schematic for this project below.

Schematic RFID project

 

 

The RFID module used in this project

 

 

The source code only got some minor changes compared to the last blog. See the changes below or download the full code here.

initialize.c

/*
 * initialize.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: initialization off the project
 */ 

#define PIN_LED1 6
#define PIN_LED2 7
#define PIN_LED3 8

#include <avr/interrupt.h>
#include "gpiolib.h"
#include "timer4clocklib.h"
#include "i2c.h"
#include "i2cdisplay.h"
#include "uart.h"


void initialize(){
	
	char openingText[] = "www.hdhprojects.nl";
	char rfidText[] = "RFID:";
	int i;

	cli();//interrupts disable
	tmr2_init(); // initialize timer2
	i2c_init(); // initialize i2c protocol
	i2cLcd_Init(); // initialize i2c lcd
	UART_Init(); // initialize the UART
	sei();//interrupts enable
	
	clear();
	setCursor(1,0);
	for(i=0; i<18; i++){		
		writeI2cDisplay(openingText[i], DATAREGISTER);
	}
	
	setCursor(0,1);
	for(i=0; i<5; i++){
		writeI2cDisplay(rfidText[i], DATAREGISTER);
	}
	
	pinMode(PIN_LED1,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED2,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED3,PINMODEOUTPUT); //set pin to output	
}

application1.c

/*
 * application1.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: Application for multi threat, show lcd clock and adc
 */

#define F_CPU 16000000UL // 16 MHz clock speed for delay.h
#define KEY1 "28005A08572D"



#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include <string.h>
#include "i2c.h"
#include "i2cdisplay.h"
#include "uart.h"


/****************************************************************************
	Bit and byte definitions
****************************************************************************/

extern int alarmCode;
char sendText2a[] = "Write me!";
char accessGranted[] = "Access granted!";
char accessDenied[] = "Access denied";
char receivedCode[14];


void Application1(void){

int i;
int p;

	if (getRcvdMsgLength() > 0){
		clearDspRange(0, 2, 20); // Clear the receive line on the lcd
		
		setCursor(6,1);
		i=0;
		p = getMsgChar();
		while (p != 0){ // Write the received message on the lcd and in a buffer for comparing
			receivedCode[i] = p;
			writeI2cDisplay(p, DATAREGISTER);
			p = getMsgChar();
			i++;
		}
		
		setCursor(0, 2);
		if(strstr(receivedCode, KEY1) != NULL){ // code is ok		
			for(uint8_t q=0; q<14; q++){
				writeI2cDisplay(accessGranted[q], DATAREGISTER);
			}
		}
		else{ // code is wrong
			for(uint8_t q=0; q<13; q++){
				writeI2cDisplay(accessDenied[q], DATAREGISTER);
			}
		}
	}
}


 

 

Communication via the UART

In this project I set up a library to communicate, via the Atmega328p UART, with a computer. The Atmega328 sends a text via the UART to a computer and you can write text back to the Atmega328. For writing text to the Atmega328 I used the software Teraterm. It is a standard terminal emulator, but this one has a lot of options and it is free. The text received back by the Atmega328 gets shown on the i2c lcd.

Building the UART project

The Atmega328p build-in USART can send/receive data synchronized with a clock or without a clock by using start and stop bits. I used it in asynchrone mode therefore I don’t use a clock as reference. The below schematic only shows the input and output to the computer in text labels. That means RS232PIN_2 should be connected to pin 2 of the communication cable to the computer and RS232PIN_3 to pin 3 and ground goes to pin 5.

Pin-out for the computer connection seen from the front side
Schematic connecting the Atmega328p to a computer

 

The code

First thing to do is initializing the UART. To do this a few things needed to be known beforehand. The first thing is the baudrate. Very common for most devices is a baud rate of 9600 bps so I used this speed. The baud rate is set in the 12 bit UBRR0 register. The formula to calculate the bits for the UBRR0 register is ((CLOCKSPEED_CPU/16/BAUD) – 1), but it’s also possible to just look up the correct value in a table with most common values. This table can be found in the datasheet.

By default pin 1 and 2 are used as general GPIO pin’s. To get them working as sending and receiving pins they have to be enabled in the UCSR0B  register. In this register the rx pin gets enabled by setting the RXEN0 bit high. And tx gets enabled by setting the bit TXEN0 high. Both rx and tx can be used with interrupts. In this case I enable the interrupts only for rx by setting the bit RXCIE0 high. For tx, the bytes get send on demand without triggering any interrupts. The advantage for enabling the rx trigger bit is that we can catch all bytes that are send even if the Atmega328p is doing other things. Everytime a byte is received it will be put in a buffer for later usage.

UCSRnB register

There are a few other bits in this register, but these are not used in this project. More info can be found in the datasheet.

The last register for initialization is the UCSR0C register. In this register the UMSELn1 and UMSELn0 are set to 0 to get asynchrone communication. The UPMnx bits are zet to 0 to select no checks for parity bits. The USBSn bit is left to 0 to select 1 stop bit. UCSZ01 and UCSZ00 are set to 1 to select 8 bits character size for the communication. The last bit UCPOLn is not set, because it’s only used for synchronized communication.

UCSRnC register

So far the initialization. In this project the code for the UART_Init() initialization function is taking care of the whole initialization process.

void UART_Init(){

	UBRR0H = (unsigned char)(BRC >> 8); //baud rate register set to 9600 BAUD
	UBRR0L = (unsigned char) BRC;

	//TXEN0 enables tx so no gpio anymore, when disabled the buffer will be finished first
	//RXEN0 enables rx
	//RXCIE0 enables the interrupt to be used with the ISR(USART_RX_vect)
	UCSR0B = (1<<RXEN0) |(1<<TXEN0) | (1 << RXCIE0);

	//Set frame format: 8data, 1stop bit, no parity.
	//UMSEL01 and UMSEL00 are both 0 to get asynchronous communication
	//UPM01 and UPM00 used to set parity, it's 0 here so no parity
	//USBS0 set to 0 is 1 stop bit. set to 1 is 2 stop bits.
	//USCZ00 - USCZ02 is used to set the character size, here it's set to 8 bits
	//UCPOL0 is clock parity and only used for synchronized transmission, not used
	UCSR0C = (1 << UCSZ01) | (3<<UCSZ00);
}


After initialization the other functions of the uart library can be used. I used the rx interrupt function ISR(USART_RX_vect) for writing all the received bytes in a buffer and when an “enter” is received it will copy everything before the “enter” in another buffer for later use. The getRcvdMsgLength() function will retreive the length of the received bytes. It’s a bit simular like the serial.available() function with Arduino. The function is used mainly to check if something is received at all. If there is something received the getMsgChar() function can be called to get the bytes in the buffer, one by one.

For the tx part the serialWrite(char c[]) function is sending all the bytes over the UART. It takes the length of the bytes that are needed to be send and sends them one by one. As I don’t have a keyboard on the Atmega328 I keep sending the same message “write me” to the computer after a message has received from the computer.

Below contains the code that is different from the original framework. The complete source code can be downloaded here.

Initialize.c

/*
 * initialize.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: initialization off the project
 */ 

#define PIN_LED1 6
#define PIN_LED2 7
#define PIN_LED3 8

#include <avr/interrupt.h>
#include "gpiolib.h"
#include "timer4clocklib.h"
#include "i2c.h"
#include "i2cdisplay.h"
#include "uart.h"


void initialize(){
	
	char openingText[] = "www.hdhprojects.nl";
	char sendText[] = "Send:";
	char receiveText[] = "Received:";
	int i;

	cli();//interrupts disable
	tmr2_init(); // initialize timer2
	i2c_init(); // initialize i2c protocol
	i2cLcd_Init(); // initialize i2c lcd
	UART_Init(); // initialize the UART
	sei();//interrupts enable
	
	clear();
	setCursor(1,0);
	for(i=0; i<18; i++){		
		writeI2cDisplay(openingText[i], DATAREGISTER);
	}
	
	setCursor(0,1);
	for(i=0; i<5; i++){
		writeI2cDisplay(sendText[i], DATAREGISTER);
	}
	setCursor(0,2);
	for(i=0; i<9; i++){
		writeI2cDisplay(receiveText[i], DATAREGISTER);
	}
	
	pinMode(PIN_LED1,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED2,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED3,PINMODEOUTPUT); //set pin to output	
}

 

Application1.c

/*
 * application1.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: Application for multi threat, show lcd clock and adc
 */

#define F_CPU 16000000UL // 16 MHz clock speed for delay.h

#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include "i2c.h"
#include "i2cdisplay.h"
#include "uart.h"


/****************************************************************************
	Bit and byte definitions
****************************************************************************/

extern int alarmCode;
char sendText2a[] = "Write me!";


void Application1(void){

int i;
int p;

	if (getRcvdMsgLength() > 0){
		clearDspRange(0, 3, 20); // Clear the receive line on the lcd
		p = getMsgChar();
		while (p != 0){ // Write the received message on the lcd
			writeI2cDisplay(p, DATAREGISTER);
			p = getMsgChar();
		}
		
		setCursor(6,1); // Set the cursor at the send position on the lcd
		for(i=0; i<9; i++){ // Send the predefined text to send via the UART
			writeI2cDisplay(sendText2a[i], DATAREGISTER);
		}
		serialWrite("Write me!\n\r");
	}
}



uart.h

/*
 * uart.h
 *
 * Created: 8-12-2017 13:36:22
 *  Author: acer
 */


#ifndef UART_H_
#define UART_H_

/****************************************************************************
	Bit and byte definitions
****************************************************************************/
#define CLOCKSPEED_CPU   16000000
#define BAUD    9600
#define BRC     ((CLOCKSPEED_CPU/16/BAUD) - 1)
#define TX_BUFFER_SIZE  20
#define RX_BUFFER_SIZE  20


/****************************************************************************
	Function definitions
****************************************************************************/
void UART_Init();
void serialWrite(char c[]);
void UART_Transmit(unsigned char data );
uint8_t getRcvdMsgLength();
char getMsgChar();
void emptyRxBuffer();


#endif /* UART_H_ */

 

uart.c

/*
 * uart.c
 *
 * Created: 8-12-2017 13:36:09
 *  Author: acer
 */ 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include "uart.h"

/****************************************************************************
	Bit and byte definitions
****************************************************************************/
char rxBuffer[RX_BUFFER_SIZE];
char rxFinalReceived[RX_BUFFER_SIZE];
uint8_t rxWritePos = 0;
uint8_t rxWriteFinalPos = 0;
char tempRcvd;
uint8_t rcvdMssgLength;
uint8_t pntr2CharInMsg = 0;


/****************************************************************************
	USART_Init, see important info below
****************************************************************************/
void UART_Init(){

	UBRR0H = (unsigned char)(BRC >> 8); //baud rate register set to 9600 BAUD
	UBRR0L = (unsigned char) BRC;

	//TXEN0 enables tx so no gpio anymore, when disabled the buffer will be finished first
	//RXEN0 enables rx
	//RXCIE0 enables the interrupt to be used with the ISR(USART_RX_vect)
	UCSR0B = (1<<RXEN0) |(1<<TXEN0) | (1 << RXCIE0);

	//Set frame format: 8data, 1stop bit, no parity.
	//UMSEL01 and UMSEL00 are both 0 to get asynchronous communication
	//UPM01 and UPM00 used to set parity, it's 0 here so no parity
	//USBS0 set to 0 is 1 stop bit. set to 1 is 2 stop bits.
	//USCZ00 - USCZ02 is used to set the character size, here it's set to 8 bits
	//UCPOL0 is clock parity and only used for synchronized transmission, not used
	UCSR0C = (1 << UCSZ01) | (3<<UCSZ00);
}


/****************************************************************************
	serialWrite, 
****************************************************************************/
void serialWrite(char c[])
{
	uint8_t i;
	for(i = 0; i < strlen(c); i++)// put the string you want to write in a buffer.
	{
		UART_Transmit(c[i]); //put the char's one by one in the buffer
	}
	
}


/****************************************************************************
	ISR(USART_RX_vect), Fills up a buffer, when an enter is received the msg 
	get copied to another buffer so you will receive bytes send in the mean time.
****************************************************************************/
ISR(USART_RX_vect)
{
	int i;
	int k;
	
	i = UDR0;
	
	rxBuffer[rxWritePos] = i; //save the received byte
		
	if (i == 0xa){ // \n = 0xa
		if (tempRcvd == 0xd){ // \r = 0xd
			for(k=0; k<rxWriteFinalPos; k++){ rxFinalReceived[k] = rxBuffer[k]; } rcvdMssgLength = rxWriteFinalPos - 1; // minus the \r\n rxWriteFinalPos = 0; rxWritePos = 0; // Start at the beginning of the buffer for the next message return; } } tempRcvd = i; // Remember byte before last byte to check if \r\n is received rxWritePos++; rxWriteFinalPos++; if(rxWritePos >= RX_BUFFER_SIZE) // If the buffer is full start at the beginning again
	{
		rxWritePos = 0;
	}
	
}

/****************************************************************************
	USART_Transmit, 
****************************************************************************/
void UART_Transmit(unsigned char data ){
	
	while( !( UCSR0A & (1<<UDRE0)) ); // Wait for empty transmit buffer UDR0 = data; // Put data into buffer, sends the data } /**************************************************************************** getRcvdMsgLength, Get the length until receiving a \n\r ****************************************************************************/ uint8_t getRcvdMsgLength(){ if (rcvdMssgLength == 0){ return 0; } return rcvdMssgLength; } /**************************************************************************** getMsgChar, Get the first char and point to the next char ****************************************************************************/ char getMsgChar(){ char rcvdChar; rcvdChar = rxFinalReceived[pntr2CharInMsg]; if(pntr2CharInMsg >= rcvdMssgLength ){
		rcvdMssgLength = 0;
		pntr2CharInMsg = 0;
		emptyRxBuffer();
		return 0;
	}
	
	pntr2CharInMsg++;
	return rcvdChar;
	
}

/****************************************************************************
	emptyBuffer, just to clear the rx buffer
****************************************************************************/
void emptyRxBuffer (){
	
	int i;
	
	for (i=0; i<=RX_BUFFER_SIZE; i++)
	{
		rxFinalReceived[i] = 0x0;
	}
}


For a future project I need to check if an “enter” is received therefor I already added the code for this in the uart library. To send text from the computer to the Atmega328p the following settings have to be set to be able to send text. Specially the “new line” transmit CR+LF in the menu setup -> terminal.

Teraterm settings

Now the UART is working fine I can use this library for receiving an access code by a Rfid module and arrange user control. I hope to get this working in a next blog. The UART is also used for the ESP8266 WiFi module to connect your home devices to the internet and have remote access. This also will be a future project. Thanks for reading!

Atmega328 with ADC

In this project I tested the analog to digital converter (ADC) from the Atmega328p. It read’s out an analog pin and shows the value on an i2c lcd. There are 6 analog ports and 2 are already used for the lcd. To change the voltage at the analog pin I used a 10k potmeter. The ADC can be set to free running mode or single conversion mode. Free running mode means the ADC samples continuously and single conversion mode means, only sample when you order to do so. I use the single conversion mode in this project.

 

Atmega328 with ADC and LCD schematic.

 

 

The working of the ADC

The first thing that’s needed to be done is initializing the ADC. I used a separate function called adc_init() in the file adc.c to get this done. For a proper initialization a few registers have to be set. For single conversion mode not all registers are used therefor I only describe the used registers in this project.

The ADMUX register is dealing with the voltage reference and the analog pin(s) that will be used.


Bit 6 and 7 tells the Atmega328p against what reference the voltage on the pin should be measured. This is important for the precision of the 10 bit read value. For example, it is possible to put 2V as a reference on the AREF pin. In that case the 10 bits (value up to 1023) will be divided over 2V. It’s more presice. It also means that every voltage above 2V on the analog pin will be presented as 1023. In this project I used Avcc (5V) as a reference therefor bit REFS0 is set.



The last register I use for initializing is the ADCSRA register.


Bit 7 is the ADC enable and needs to be set to activate the ADC. Bit 6 is the ADC start conversion, this is used in the application every time you need to read a value from the analog pin. Bit 5-3 is used for interrupts and triggering on certain conditions. Not used in this project. Bit 2 – 0 sets a prescaler. The 16MHz external clock gets divided by this prescaler to set the sampling rate. In this project all 3 bits are set, which gives a division factor of 128. The Atmega328p ADC circuit needs a clock below 200 KHz to get a 10 bit resolution and by using a higher frequentie the resolution will only be 8 bits, but then you will have a faster sampling rate. In this project I use 16 MHz / 128 to get 125 KHz. This is below 200 KHz so it will provide the full 10 bits resolution.


The initialization function uses the below code to initialize the ADC for Single conversion mode.

void adc_init(){
	
	ADMUX = (1<<REFS0);
	ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

For reading a pin it’s necessary to set ADMUX again with the analog pin you want to use. After that the conversion gets started by setting the ADSC bit in the ADCSRA register. When the conversion is done this bit is automatically unset by the Atmega328p hardware so wait for this in a while loop. Last thing is returning the value to do something with it. Because it’s a 10 bit value the return value is 2 bytes placed in ADC.

uint16_t adc_read(int pin){
	if(pin < 0 || pin > 7){ //ADC0 - ADC7 is available
		return 1; //pin number is out of range
	}
	
	pin &= 0x7;
	ADMUX = (ADMUX & 0xF8)|pin;
	
	ADCSRA |= (1<<ADSC);
	
	while(ADCSRA & (1<<ADSC)); //as long as ADSC pin is 1 just wait.
	
	return (ADC);
}

 

The application1.c file contains the code that deals with reading the analog pin and writing the value to the lcd.

Application1.c

/*
 * application1.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: Application for multi threat, show lcd clock and adc
 */ 

#include <avr/io.h>
#include <stdlib.h>
#include "i2c.h"
#include "i2cdisplay.h"
#include "timer4clocklib.h"
#include "adc.h"

#define ADCPIN 2
#define DELAYMILIS 150 // Led blinking wait time in milliseconds
#define TIMER_IS_NOT_SET 0
#define TIMER_IS_SET 1

void showTime(); // showing the time
void showAdcValue(); // showing the ADC value

/****************************************************************************
	Public vars
****************************************************************************/

extern int alarmCode;
extern int timeSecApp1;
extern int timeMinApp1;
int oldTime;


int timerSet4Adc = TIMER_IS_NOT_SET;
unsigned long int endTimeAdc = 0;


void Application1(void){

	if(timerSet4Adc == TIMER_IS_NOT_SET){
		endTimeAdc = msCounter + DELAYMILIS;
		timerSet4Adc = TIMER_IS_SET;
		showAdcValue();
	}
	if(endTimeAdc < msCounter){ // Timer is expired
		timerSet4Adc = TIMER_IS_NOT_SET; // reset timer
		return;
	}

	
	showTime();
	
}

void showTime(){
	int seconds;
	int minutes;
	
	
	seconds = timeSecApp1;
	minutes = timeMinApp1;
	
	if (oldTime == timeSecApp1){
		return;
	}
	if (seconds >= 10){
		setCursor(11,2);
		writeI2cDisplay(seconds/10+48, DATAREGISTER);
		setCursor(12,2);
		writeI2cDisplay(seconds%10+48, DATAREGISTER);
		if (minutes>=10){
			setCursor(8,2);
			writeI2cDisplay(minutes/10+48, DATAREGISTER);
			setCursor(9,2);
			writeI2cDisplay(minutes%10+48, DATAREGISTER);
		}
		if (minutes<10){
			setCursor(8,2);
			writeI2cDisplay('0', DATAREGISTER);
			setCursor(9,2);
			writeI2cDisplay(minutes+48, DATAREGISTER);
		}
	}
	else if (seconds < 10){
		setCursor(12,2);
		writeI2cDisplay(seconds+48, DATAREGISTER);
		setCursor(11,2);
		writeI2cDisplay('0', DATAREGISTER);
		if (minutes>=10){
			setCursor(8,2);
			writeI2cDisplay(minutes/10+48, DATAREGISTER);
			setCursor(9,2);
			writeI2cDisplay(minutes%10+48, DATAREGISTER);
		}
		if (minutes<10){
			setCursor(8,2);
			writeI2cDisplay('0', DATAREGISTER);
			setCursor(9,2);
			writeI2cDisplay(minutes+48, DATAREGISTER);
		}
	}
	oldTime = timeSecApp1;
}

void showAdcValue(){
	
	uint16_t adcReadedValue;
	uint8_t hundreds;
	uint8_t tens;
	
	adcReadedValue = adc_read(ADCPIN);
	setCursor(8,3);
	
	if(adcReadedValue > 999){
		writeI2cDisplay('1', DATAREGISTER);
		adcReadedValue -= 1000;
	}
	else{
		writeI2cDisplay('0', DATAREGISTER);
	}
	hundreds = adcReadedValue / 100;
	adcReadedValue %= 100;
	writeI2cDisplay(hundreds + 48, DATAREGISTER);
	tens = adcReadedValue /10;
	adcReadedValue %= 10;
	writeI2cDisplay(tens + 48, DATAREGISTER);
	writeI2cDisplay(adcReadedValue + 48, DATAREGISTER);
	
}

initialize.c

/*
 * initialize.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: initialization off the project
 */ 

#define PIN_LED1 6
#define PIN_LED2 7
#define PIN_LED3 8
#define ADCPIN 0

#include <avr/interrupt.h>
#include "gpiolib.h"
#include "timer4clocklib.h"
#include "i2c.h"
#include "i2cdisplay.h"
#include "adc.h"


void initialize(){
	
	char openingText[] = "www.hdhprojects.nl";
	char clockText[] = "Clock";
	char adcText[] = "ADC";
	int i;

	cli();//interrupts disable
	tmr2_init(); // initialize timer2
	i2c_init(); // initialize i2c protocol
	i2cLcd_Init(); // initialize i2c lcd
	adc_init(); // initialize the ADC
	sei();//interrupts enable
	
	clear();
	setCursor(1,0);
	for(i=0; i<18; i++){		
		writeI2cDisplay(openingText[i], DATAREGISTER);
	}
	
	setCursor(1,2);
	for(i=0; i<5; i++){
		writeI2cDisplay(clockText[i], DATAREGISTER);
	}
	
	setCursor(10,2);
	writeI2cDisplay(':', DATAREGISTER);
	
	setCursor(1,3);
	for(i=0; i<3; i++){
		writeI2cDisplay(adcText[i], DATAREGISTER);
	}
	
	
	pinMode(PIN_LED1,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED2,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED3,PINMODEOUTPUT); //set pin to output	
}

adc.c

/*
 * adc.c
 *
 * Created: 4-12-2017 18:01:49
 *  Author: acer
 */ 

#include <avr/io.h>
#include "adc.h"

/****************************************************************************
	adc_init(), enables the ADC conversion and set's the prescaler
****************************************************************************/
void adc_init(){
	
	ADMUX = (1<<REFS0);
	ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

/****************************************************************************
	adc_read(uint8_t pin), returns a value from 0 - 1023
****************************************************************************/
uint16_t adc_read(int pin){
	if(pin < 0 || pin > 7){ //ADC0 - ADC7 is available
		return 1; //pin number is out of range
	}
	
	pin &= 0x7;
	ADMUX = (ADMUX & 0xF8)|pin;
	
	ADCSRA |= (1<<ADSC);
	
	while(ADCSRA & (1<<ADSC)); //as long as ADSC pin is 1 just wait.
	
	return (ADC);
}

adc.h

/*
 * adc.h
 *
 * Created: 4-12-2017 18:02:01
 *  Author: acer
 */ 


#ifndef ADC_H_
#define ADC_H_

/****************************************************************************
	Bit and byte definitions
****************************************************************************/



/****************************************************************************
	Function definitions
****************************************************************************/
void adc_init();
uint16_t adc_read(int pin);




#endif /* ADC_H_ */

 

 

 

 

I2C lcd with clock

In this project I created a library for the i2c lcd. I used the Arduino library as a reference, but I found that library a bit to complicated to use it completely. The Arduino library also have a lot of functions that I’m not planning to use, like creating your own characters. The ASCII characters are enough for my projects for example. So I wrote a new library.

Communicating with the i2c lcd test build.

 

Again I used the same framework to get the lcd working simultanesly with other devices in the future and for now I only added 1 led to test the multi threating.

The lcd unit has a backpack attached used to communicate with the lcd via i2c. The original lcd uses more then 11 pin’s and that’s a lot for a microcontroller like the Atmega328p. The backpack uses the PCA8574, which is a 8 bit i2c pin expander to reduce the amount of pins to be used on a microcontroller. The lcd on it’s own has already 8 pins for writing to a register, but it is possible to set the lcd to use 4 bits instead. That means there are 4 pins left on the PCA8574 and these are used for selecting a register, read or write, enable puls and the backlight. See the below schematic how to connect.

I2c lcd project schematic.

 

Working of the display

Communicating with the display is a bit complicated because we have to use it in 4 bits  mode. If the backpack used a 16 bit expander instead of 8 bit then life whas much easier, but 8 bits expanders are cheaper… That means to write to an 8 bit register we have to send the high 4 bits of a byte first and then the last 4 bits. There are 2 registers to write to. That’s the data register DR and the instruction register IR. Text you want to get on the display have to be set in the data register. Command’s like, changing the cursor position or clearing the display, have to be set in the instruction register. The register select RS pin will let you choose between both the registers. Puttting the pin low (0V) selects the IR and high (5V) selects the DR.

It is possible to read an address location or to read the busy flag BF. The BF is used to check if the lcd is still working on an internal command. If so, it’s not possible to send a new instruction. I didn’t do anything with the BF yet or reading the address locations. For writing text on the lcd it’s not needed. I used delays according the datasheet to wait for the time the lcd needs.

There is a read write RW pin. To read from the lcd this pin should be high and for writing low. There is also a pin for the backlight BL, but my display requires this to be activated all the times else you won’t see anything on the display.

To set a byte into a register the enable EN pin have to be set high. This works like a latched flip flop, that means whatever voltage you put on the pins they will only be read by the lcd if the enable pin is high. Writing an instruction or a character are basically the same. The only difference is the register select pin. Basically to write to a register the following steps have to be done:

  1. Put the correct voltages on all the lcd pins for the first 4 bits of a byte you want to send.
  2. Set the enable pin high for more then 450 nano seconds and keep the rest of the pins the same.
  3. Set the enable pin low for more then 37 micro seconds and keep the rest of the pins the same.
  4. Do the last 3 steps again, but now for the last 4 bits of 8.

In code it looks like this:

i2c_start();
i2c_write((LCD_I2C_ADDR<<1)|0);
i2c_write(highnib | LCD_BACKLIGHT | registerselect);
i2c_stop();
i2c_start();
i2c_write((LCD_I2C_ADDR<<1)|0); i2c_write(highnib | LCD_BACKLIGHT | registerselect | ENABLE); i2c_stop(); _delay_us(1); // enable pulse must be >450ns
i2c_start();
i2c_write((LCD_I2C_ADDR<<1)|0); i2c_write((highnib | LCD_BACKLIGHT | registerselect ) & ~ENABLE); i2c_stop(); _delay_us(50); // commands need > 37us to settle
	
// same as above but now for last 4 bits
i2c_start();
i2c_write((LCD_I2C_ADDR<<1)|0);
i2c_write(lownib | LCD_BACKLIGHT | registerselect);
i2c_stop();
i2c_start();
i2c_write((LCD_I2C_ADDR<<1)|0); i2c_write(lownib | LCD_BACKLIGHT | registerselect | ENABLE); i2c_stop(); _delay_us(1); // enable pulse must be >450ns
i2c_start();
i2c_write((LCD_I2C_ADDR<<1)|0); i2c_write((lownib | LCD_BACKLIGHT | registerselect ) & ~ENABLE); i2c_stop(); _delay_us(50); // commands need > 37us to settle

 

Initializing the display

This subject needs a separate chapter, because its a bit complicated. The datasheet is not very clear about this or it is my understanding… Default the display is set to 8 bit mode therefor we have to put it on 4 bit mode first and this can’t be changed later on, only by rebooting the lcd. The Arduino library sends the same command 3 times with different times between the instructions. I send the instruction only once, but I use a delay before proceding. This first instruction have to be send in 1 time 4 bits and all other instructions have to be send in 2 times 4 bits. After that the lcd works in 4 bit mode and then the lcd lines and character dots configuration can be send in 2 times 4 bits.  These settings can be send only once and after that all other instructions can be send any time you need.

The next settings are the cursor on/off and blinking of the cursor. The last instruction set up the direction of the characters. For western countries, my case, that’s from left to right. After that the display initial settings are set and the lcd can be used. I made a seperate function called i2cLcd_init() to get this all done.

The application

For testing I wrote my name on the display and a clock. This clock only counts for 1 hour and then starts over again. Minor changes are needed to write a 24 hour clock, but for now I thought this is a good start. My clock is not so accurate at the moment. It’s about 2 seconds a minute to fast. To get the clock more accurate you can change the CMR_SECOND and the CMR_MINUT number in the timer4clocklib.h.

The source code

See the source code of the changed files here. All other files remain the same as before.

initialize.c

/*
 * initialize.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: initialization off the project
 */ 

#define PIN_LED1 6
#define PIN_LED2 7
#define PIN_LED3 8

#include <avr/interrupt.h>
#include "gpiolib.h"
#include "timer4clocklib.h"
#include "i2c.h"
#include "i2cdisplay.h"


void initialize(){
	
	char openingText[] = "www.hdhprojects.nl";
	char clockText[] = "Clock";
	int i;

	cli();//interrupts disable
	tmr2_init(); // initialize timer2
	i2c_init(); // initialize i2c protocol
	i2cLcd_Init(); // initialize i2c lcd
	sei();//interrupts enable
	
	clear();
	setCursor(1,0);
	for(i=0; i<18; i++){		
		writeI2cDisplay(openingText[i], DATAREGISTER);
	}
	setCursor(1,2);
	for(i=0; i<5; i++){
		writeI2cDisplay(clockText[i], DATAREGISTER);
	}
	setCursor(10,2);
	writeI2cDisplay(':', DATAREGISTER);
	
	
	pinMode(PIN_LED1,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED2,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED3,PINMODEOUTPUT); //set pin to output	
}

 

application1.c

/*
 * operational.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: Application for multi threat, show lcd clock
 */ 

#include <avr/io.h>
#include "i2c.h"
#include "i2cdisplay.h"

void showTime(); // showing the time

/****************************************************************************
	Public vars
****************************************************************************/

extern int alarmCode;
extern int timeSecApp1;
extern int timeMinApp1;
int oldTime;

void Application1(void){

	showTime();
	
}

void showTime(){
	int seconds;
	int minutes;
	
	
	seconds = timeSecApp1;
	minutes = timeMinApp1;
	
	if (oldTime == timeSecApp1){
		return;
	}
	if (seconds >= 10){
		setCursor(11,2);
		writeI2cDisplay(seconds/10+48, DATAREGISTER);
		setCursor(12,2);
		writeI2cDisplay(seconds%10+48, DATAREGISTER);
		if (minutes>=10){
			setCursor(8,2);
			writeI2cDisplay(minutes/10+48, DATAREGISTER);
			setCursor(9,2);
			writeI2cDisplay(minutes%10+48, DATAREGISTER);
		}
		if (minutes<10){
			setCursor(8,2);
			writeI2cDisplay('0', DATAREGISTER);
			setCursor(9,2);
			writeI2cDisplay(minutes+48, DATAREGISTER);
		}
	}
	else if (seconds < 10){ setCursor(12,2); writeI2cDisplay(seconds+48, DATAREGISTER); setCursor(11,2); writeI2cDisplay('0', DATAREGISTER); if (minutes>=10){
			setCursor(8,2);
			writeI2cDisplay(minutes/10+48, DATAREGISTER);
			setCursor(9,2);
			writeI2cDisplay(minutes%10+48, DATAREGISTER);
		}
		if (minutes<10){
			setCursor(8,2);
			writeI2cDisplay('0', DATAREGISTER);
			setCursor(9,2);
			writeI2cDisplay(minutes+48, DATAREGISTER);
		}
	}
	oldTime = timeSecApp1;
}


 

display_main.c

/*
* main.c
*
* Created: 20-5-2017
* Author: HdH
* Description: main, initializing en starting the app
* Global functions:
*
*
*/

#define WATCHDOG_ALARM 2
#define NO_ALARM 0

#include <avr/io.h>
#include "i2cdisplay.h"

void errorHandle(int receivedAlarm);
int operational(void);
void initialize(void);

/****************************************************************************
	Public vars
****************************************************************************/

int errorCode = NO_ALARM; //
char alarmText[] = "Alarm in app";

int main(void)
{
	initialize(); //start only once (setup with Arduino)
	
	errorCode = operational(); //the main loop
	if (errorCode == WATCHDOG_ALARM){ // you only end up here if there is an alarm.
		errorHandle(errorCode);
	}
	else{
		errorHandle(errorCode); //one of the apps is returning the error
	}
}

void errorHandle(int receivedAlarm){
	
	int i;
	
	while(1){ //blink some leds or show an error message on a display..
		
		clear();
		setCursor(1,0);
		for(i=0; i<12; i++){
			writeI2cDisplay(alarmText[i], DATAREGISTER);
		}
	}
}

 

i2cdiplay.h

/*
 * i2cdisplay.h
 *
 * Created: 30-11-2017 19:18:52
 *  Author: acer
 */ 


#ifndef I2CDISPLAY_H_
#define I2CDISPLAY_H_

/****************************************************************************
	Bit and byte definitions
****************************************************************************/

// Display function settings can only be changed once
#define LCD_I2C_ADDR 0x3f // i2c address of the lcd
#define LCD_FUNCTIONSET 0x20
#define LCD_BITMODE 0x00 //4bit is 0x0, 8bit is 0x10
#define LCD_LINE 0x08 // 2 line is 0x08, 1 line is 0x00
#define LCD_DOTS 0x00 // 5x8 dots is 0x00, 5x10 dots is 0x04
#define LCD_ROWS 4 // Number of LCD rows
#define LCD_COLUMNS 20 // Number of LCD columns
#define DATAREGISTER 1
#define INSTRUCTIONREGISTER 0
#define ENABLE 4
#define READ_BF 2

// Display control settings
#define LCD_DISPLAYCONTROL 0x08 // Use this cmd combined with the below optional settings
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
#define LCD_BACKLIGHT 0x08
#define LCD_NOBACKLIGHT 0x00

// Display entry mode settings
#define LCD_ENTRYMODESET 0x04 // Use this cmd combined with the below optional settings. 
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// Send below commands to the Instruction register IR.
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_SETDDRAMADDR 0x80 // Used to point to a position on the display
#define LCD_CURSORSHIFT 0x10
#define LCD_DISPLAYMOVE 0x08
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00



/****************************************************************************
	Function definitions
****************************************************************************/

void i2cLcd_Init();
void writeI2cDisplay(uint8_t, uint8_t);
uint8_t checkBusyFlag(); // not used yet

void clear();
void home();
void setCursor(uint8_t, uint8_t);
void noDisplay();
void display();
void noCursor();
void cursor();
void noBlink();
void blink();
void scrollDisplayLeft();
void scrollDisplayRight();
void leftToRight();
void rightToLeft();
void autoscroll();
void noAutoscroll();

#endif /* I2CDISPLAY_H_ */

 

i2cdisplay.c

/*
* i2cDisplay.c
*
* Created: 14-6-2017
* Author: HdH
* Description: functions to control the I2C Dsiplay
* Global functions:
*
*/

#define F_CPU 16000000UL // 16 MHz clock speed

#include <avr/io.h>
#include <util/delay.h>
#include "i2c.h"
#include "i2cdisplay.h"


/****************************************************************************
	Public vars
****************************************************************************/

uint8_t displayMainSettings;
uint8_t displayControl;
uint8_t displayEntryMode;
uint8_t bfPin7;


void i2cLcd_Init(){
	
	_delay_ms(50); // to get the display powered up (pg. 46 datasheet)
	
	i2c_start(); // below is needed to change the lcd from 8 bit to 4 bit
	i2c_write((LCD_I2C_ADDR<<1)|0);
	i2c_write(LCD_FUNCTIONSET);
	i2c_stop();
	i2c_start();
	i2c_write((LCD_I2C_ADDR<<1)|0); i2c_write(LCD_FUNCTIONSET | ENABLE); i2c_stop(); _delay_us(1); // enable pulse must be >450ns
	i2c_start();
	i2c_write((LCD_I2C_ADDR<<1)|0);
	i2c_write(LCD_FUNCTIONSET & ~ENABLE);
	i2c_stop();
	_delay_ms(10); // pg. 46, instead of sending the functionseet 3 times
	
	displayMainSettings = LCD_FUNCTIONSET | LCD_BITMODE | LCD_LINE | LCD_DOTS;
	writeI2cDisplay(displayMainSettings, INSTRUCTIONREGISTER); // these display settings can't be changed after this.
		
	_delay_us(100); // pg. 46
	displayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
	writeI2cDisplay(displayControl | LCD_DISPLAYCONTROL, INSTRUCTIONREGISTER);
	
	displayEntryMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
	writeI2cDisplay(displayEntryMode | LCD_ENTRYMODESET, INSTRUCTIONREGISTER);
	home();
}

void writeI2cDisplay(uint8_t value, uint8_t registerselect){
	
	uint8_t highnib=value&0xf0;
	uint8_t lownib=(value<<4)&0xf0;
	
	// first byte send, set/prepare lcd pins
	// second byte send, same and turn enable on
	// third byte send, same and turn enable off
	i2c_start();
	i2c_write((LCD_I2C_ADDR<<1)|0);
	i2c_write(highnib | LCD_BACKLIGHT | registerselect);
	i2c_stop();
	i2c_start();
	i2c_write((LCD_I2C_ADDR<<1)|0); i2c_write(highnib | LCD_BACKLIGHT | registerselect | ENABLE); i2c_stop(); _delay_us(1); // enable pulse must be >450ns
	i2c_start();
	i2c_write((LCD_I2C_ADDR<<1)|0); i2c_write((highnib | LCD_BACKLIGHT | registerselect ) & ~ENABLE); i2c_stop(); _delay_us(50); // commands need > 37us to settle
	
	// same as above but now for last 4 bits
	i2c_start();
	i2c_write((LCD_I2C_ADDR<<1)|0);
	i2c_write(lownib | LCD_BACKLIGHT | registerselect);
	i2c_stop();
	i2c_start();
	i2c_write((LCD_I2C_ADDR<<1)|0); i2c_write(lownib | LCD_BACKLIGHT | registerselect | ENABLE); i2c_stop(); _delay_us(1); // enable pulse must be >450ns
	i2c_start();
	i2c_write((LCD_I2C_ADDR<<1)|0); i2c_write((lownib | LCD_BACKLIGHT | registerselect ) & ~ENABLE); i2c_stop(); _delay_us(50); // commands need > 37us to settle
	
}

uint8_t checkBusyFlag(){
	
	// Will be updated later
	return 0;
}

void clear(){
	writeI2cDisplay(LCD_CLEARDISPLAY, INSTRUCTIONREGISTER);// clear display
	_delay_us(2000);  // this command takes a long time!
}

void home(){
	writeI2cDisplay(LCD_RETURNHOME, INSTRUCTIONREGISTER);  // set cursor position to zero
	_delay_ms(2);  // this command takes a long time!
}

void setCursor(uint8_t col, uint8_t row){
	int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
	if ( row > LCD_ROWS ) {
		row = LCD_ROWS-1;    // we count rows starting w/0
	}
	writeI2cDisplay(LCD_SETDDRAMADDR | (col + row_offsets[row]), INSTRUCTIONREGISTER);
}

void noDisplay() {
	displayControl &= ~LCD_DISPLAYON;
	writeI2cDisplay(LCD_DISPLAYCONTROL | displayControl, INSTRUCTIONREGISTER);
}

void display() {
	displayControl |= LCD_DISPLAYON;
	writeI2cDisplay(LCD_DISPLAYCONTROL | displayControl, INSTRUCTIONREGISTER);
}

void noCursor() {
	displayControl &= ~LCD_CURSORON;
	writeI2cDisplay(LCD_DISPLAYCONTROL | displayControl, INSTRUCTIONREGISTER);
}

void cursor() {
	displayControl |= LCD_CURSORON;
	writeI2cDisplay(LCD_DISPLAYCONTROL | displayControl, INSTRUCTIONREGISTER);
}

void noBlink() {
	displayControl &= ~LCD_BLINKON;
	writeI2cDisplay(LCD_DISPLAYCONTROL | displayControl, INSTRUCTIONREGISTER);
}

void blink() {
	displayControl |= LCD_BLINKON;
	writeI2cDisplay(LCD_DISPLAYCONTROL | displayControl, INSTRUCTIONREGISTER);
}

void scrollDisplayLeft(void) {
	writeI2cDisplay(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT, INSTRUCTIONREGISTER);
}

void scrollDisplayRight(void) {
	writeI2cDisplay(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT, INSTRUCTIONREGISTER);
}

void leftToRight(void) {
	displayEntryMode |= LCD_ENTRYLEFT;
	writeI2cDisplay(LCD_ENTRYMODESET | displayEntryMode, INSTRUCTIONREGISTER);
}

void rightToLeft(void) {
	displayEntryMode &= ~LCD_ENTRYLEFT;
	writeI2cDisplay(LCD_ENTRYMODESET | displayEntryMode, INSTRUCTIONREGISTER);
}

void autoscroll(void) {
	displayEntryMode |= LCD_ENTRYSHIFTINCREMENT;
	writeI2cDisplay(LCD_ENTRYMODESET | displayEntryMode, INSTRUCTIONREGISTER);
}

void noAutoscroll(void) {
	displayEntryMode &= ~LCD_ENTRYSHIFTINCREMENT;
	writeI2cDisplay(LCD_ENTRYMODESET | displayEntryMode, INSTRUCTIONREGISTER);
}

 

The lcd comes in very handy as a debug tool. You can write everything to the display that might be important for trouble shooting. Besides that, there are many more reasons to use an lcd in your projects. I think my next blog will be an ADC library for the analog ports and I will write this on a display to understand the working of the analog pins.