Monday, December 31, 2018

ESP8266 library for EFM8 microcontrollers - Server mode

In previous article ESP8266 library for EFM8 microcontrollers was presented the ESP8266 module and how can be interfaced with a microcontroller in client mode. This article covers ESP8266 in server mode and how to send commands to ESP8266 over the internet.

Of course this is not a real web server although with an SD card whole HTML pages could be served. The purpose of this library is to extract queries from a URL requested by a client. This way for example you could change color and brightness of an RGB led using a color wheel in a phone app or control home appliances from anywhere in the world.




ESP8266 library functions in server mode

ESP_StartServer(const char *port)

Function
Start the ESP8266 server at the specified port
Arguments
port: string of characters that denotes the port number used by web clients to connect to the ESP. The port must be forwarded in router. I believe by default ESP8266 uses the 333 port but port 80 is better
Returns
true if successful or false if not
 
ESP_DeleteServer(void)

Function
Deletes the server
Arguments
void
Returns
true if successful or false if not

ESP_ClientConnected(void)

Function
Check if a client is connected to the server and return true or false
Arguments
none
Returns
true if successful or false if not

ESP_GetQueryData(query_names[], query_values_return[NR_OF_URL_QUERIES][URL_QUERY_VALUE_LENGTH])

Function
Takes a list of URL query names and extract their values in the provided array
Arguments
query_names: (char *) a pointer to an array with query names. See the example bellow
query_values_return: (char) a 2D array in which the query values corresponding to provided query names will be storred
Returns
true if URL queries were found or false if not. Web browsers will ask for a favicon and if this returns false a 404 response can be sent

ESP_ServerReply(html_data[], HTTP_Headers)

Function
Send an HTTP response to the client together with some HTML content
Arguments
html_data: (char) HTML content. It can also be a null character
HTTP_Headers: (char *) the HTTP response. The following macros are available HTTP_200_OK or HTTP_404_NOT_FOUND
Returns
true if successful or false if not




In the main c file first add the following defines. It is important to read the description of each define and set them properly.

main.c
//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
#define F_CPU        24500000 // CPU FREQUENCY IN Hz
#define BAUD_RATE    115200

/* Server mode */
// How many queries to search in URL. ex: http://192.168.100.5:333?var1=10&var2=4
// In this example there are 2 queries names - var1 and var2
#define NR_OF_URL_QUERIES   3 // rows

// How many characters will query value have (after =)
// For example if var2 will have only 1 character but var2 2 characters, then the greatest length is taken
// In this case 3 plus 1 for null character so we have 4
#define URL_QUERY_VALUE_LENGTH   4 // columns (add 1 for null terminator)

// How many characters has the longest query name. var1 has 4 characters
#define URL_QUERY_NAME_LENGTH   1

Now some includes

#include <SI_EFM8BB1_Register_Enums.h> // SFR declarations
#include "globals.h"
#include "delay.h"
#include "ESP8266.h"
#include "uart.h"

Links for these files can be found in the main article here ESP8266 library for EFM8 microcontrollers.


Example on how to use the ESP8266 library in server mode, setup the EFM8BB1 microcontroller

This example describes how to control the brightness and color of an RGB led over the internet by extracting the query values and printing them on the terminal. The Termite terminal can be used for testing purposes.

main.c

//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
#define F_CPU        24500000 // CPU FREQUENCY IN Hz
#define BAUD_RATE    115200

/* Server mode */
// How many queries to search in URL. For example to change the color and brightness of an RGB led: 
// http://192.168.100.5:301?r=10&g=4&b=49&a=100
// In this example there are 4 query names - r, g, b and a
#define NR_OF_URL_QUERIES   4 // rows

// How many characters will query value have (after =)
// For example if r will have only 1 character but g 2 characters, then the greatest length is taken
// In this case 3 plus 1 for null character so we have 4
#define URL_QUERY_VALUE_LENGTH   4 // columns (add 1 for null terminator)

// How many characters has the longest query name. r, g, b and a are all 1 character long
#define URL_QUERY_NAME_LENGTH   1

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <SI_EFM8BB1_Register_Enums.h> // SFR declarations
#include "globals.h"
#include "delay.h"
#include "ESP8266.h"
#include "uart.h"

//-----------------------------------------------------------------------------
// Global VARIABLES
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Global CONSTANTS
//-----------------------------------------------------------------------------
SI_SBIT(LED, SFR_P0, 0); // LED on Port 0: Bit 0

//-----------------------------------------------------------------------------
// Function PROTOTYPES
//-----------------------------------------------------------------------------
void Port_Init(void);

//-----------------------------------------------------------------------------
// SiLabs_Startup() Routine
// ----------------------------------------------------------------------------
// This function is called immediately after reset, before the initialization
// code is run in SILABS_STARTUP.A51 (which runs before main() ). This is a
// useful place to disable the watchdog timer, which is enable by default
// and may trigger before main() in some instances.
//-----------------------------------------------------------------------------
void SiLabs_Startup(void){
 // Disable Watchdog with key sequence
 WDTCN = 0xDE; // First key
 WDTCN = 0xAD; // Second key
}


//-----------------------------------------------------------------------------
// main() Routine
// ----------------------------------------------------------------------------
// Note: the software watchdog timer is not disabled by default in this
// example, so a long-running program will reset periodically unless
// the timer is disabled or your program periodically writes to it.
//-----------------------------------------------------------------------------
int main(void){
 //-------------------------
 // Local variables
 // ------------------------
 // Server mode
 // Data from URL queries will be stored here
 char query_values[NR_OF_URL_QUERIES][URL_QUERY_VALUE_LENGTH];
 // Query names to search for in the incoming URL
 char *URL_queries[] = {"r", "g", "b", "a"};

 bool esp_setup_ok = false;

 //-------------------------
 // Initialization
 // ------------------------
 Port_Init();
 UART_Begin(BAUD_RATE);

 // Delay added for debugging purposes. Can be removed.
 _delay_ms(2000);

 // Set ESP in server mode
 esp_setup_ok = ESP_Setup(STATION_MODE, SERVER);


 //-------------------------
 // while loop
 // ------------------------
 while(1){
  // Check if there is an incoming connection
  if(ESP_ClientConnected()){
   // Extract the values from incoming URL using provided queries (URL_queries) and store the data in the provided array (query_values)
   if(ESP_GetQueryData(URL_queries, query_values)){
    
    // Send a HTTP 200 OK response and some HTML data
    ESP_ServerReply("<h1>ESP OK</h1>", HTTP_200_OK);

    // Print on the terminal received RGB values for debugging purposes
    // These values could be used to control an RGB LED but first the values must be converted from
    // string to integer using stringToInt() function located in globals.h
    UART_Send("\nRED: ");
    UART_Send(query_values[0]);
    UART_Send("\nGREEN: ");
    UART_Send(query_values[1]);
    UART_Send("\nBLUE: ");
    UART_Send(query_values[2]);
    UART_Send("\n");
    UART_Send("\nBRIGHTNESS: ");
    UART_Send(query_values[3]);
    UART_Send("\n");
   }else{
    // The function will return false when no queries were found in URL
    // Browsers will send a request for favicon so here you could send an embedded message as a favicon or return 404
    // In any case a response must be sent
    ESP_ServerReply("\0", HTTP_404_NOT_FOUND);
   }
   }
  }
}


//-----------------------------------------------------------------------------
// Functions
//-----------------------------------------------------------------------------
void Port_Init(void){
 // -------------------------------------------------------------
 // 1. Select the input mode (analog or digital) for all port pins
 // using the Port Input Mode register (PnMDIN)
 // -------------------------------------------------------------

 // --- Configure a pin as a digital input
 // -------------------------------------------------------------
 // 1.1 Set the bit associated with the pin in the PnMDIN register to 1.
 // This selects digital mode for the pin.
 //P0MDIN |= (1<<0);

 // 2.1 Clear the bit associated with the pin in the PnMDOUT register to 0.
 // This configures the pin as open-drain.
 //P0MDOUT &= ~(1<<0);

 // 3.1 Set the bit associated with the pin in the Pn register to 1.
 // This tells the output driver to “drive” logic high. Because the pin is
 // configured as open-drain, the high-side driver is disabled,
 // and the pin may be used as an input.
 //P0 |= (1<<0);


 // --- To configure a pin as a digital, push-pull output:
 // -------------------------------------------------------------
 // 1.1 Set the bit associated with the pin in the PnMDIN register to 1.
 // This selects digital mode for the pin.
 P0MDIN |= (1<<0);


 // 2.1 Set the bit associated with the pin in the PnMDOUT register to 1.
 // This configures the pin as push-pull.
 P0MDOUT |= (1<<0);


 // --- To configure a pin as analog, the following steps should be taken:
 // -------------------------------------------------------------
 // 1.1 Clear the bit associated with the pin in the PnMDIN register to 0.
 // This selects analog mode for the pin.
 //P0MDIN &= ~(1<<0);

 // 2.1 Set the bit associated with the pin in the Pn register to 1.
 //P0 |= (1<<0);

 // 3.1 Skip the bit associated with the pin in the PnSKIP register
 // to ensure the crossbar does not attempt to assign a function to the pin.
 //P0SKIP |= (1<<0);

 // -------------------------------------------------------------
 // 2. Select any pins to be skipped by the I/O crossbar
 // using the Port Skip registers (PnSKIP)
 // -------------------------------------------------------------
 //P0SKIP = 0xFF; // Skip all port pins
 //P1SKIP = 0xFF; // Skip all port pins


 // -------------------------------------------------------------
 // 3. Assign port pins to desired peripherals
 // -------------------------------------------------------------

 // -------------------------------------------------------------
 // 4. Enable crossbar and weak pull-ups
 // -------------------------------------------------------------
 XBR2 |= 0x40;

 // Make all pins low to save power. The pins to be used can be set afterwards
 //P0 = 0;
 //P1 = 0;
 //P2 = 0;

 // SYSCLK - CPU speed
 CLKSEL = CLKSEL_CLKSL__HFOSC | CLKSEL_CLKDIV__SYSCLK_DIV_1;
}


//-----------------------------------------------------------------------------
// Interrupts
//-----------------------------------------------------------------------------






No comments:

Post a Comment