Saturday, December 8, 2018

ESP8266 library for EFM8BB1 Busy Bee microcontrollers

ESP8266 is a low-cost serial to Wi-Fi module that is very popular among electronics hobbyists. It can be used to connect a microcontroller to internet over Wi-Fi. The things you can do with it are numerous. You could monitor and log various sensor data like atmospheric pressure, temperature and humidity together with geo locations on websites like thingspeak.com. Or you could retrieve information from internet such as weather, number of subscribers on YouTube, send tweets with your microcontroller, send email notification if your plants get dry, etc.


The module consists mainly of a 32-bit microcontroller with full TCP/UDP stack support and an external flash memory chip from 0.5MB to 8MB depending on the module type. The cheapest one is ESP8266-01.
The module can be used in two ways:
1. With an external microcontroller that sends commands to ESP using so called AT commands. This is the simplest way. Communication between ESP and microcontroller is done via UART. A serial terminal such as Termite terminal can be used to manually test the module.
2. The ESP can also be programmed and used without an external MCU since already contains a powerful MCU. The easiest way to program it is by using Arduino IDE with appropriate libraries. Once the chip is reprogrammed it cannot take AT commands anymore. The disadvantage is that the cheap modules have only one or two general purpose pins so you can't do much without an external microcontroller.

ESP8266 pinout and power

Power

The voltage to the ESP8266 must not exceed 3.3V on supply and communication lines. So make sure that the USB to UART or the microcontroller used for interface also uses 3.3V or use a voltage level translator. Also the power supply must be able to source at least 170mA - the peak current of the ESP.

Some modules are not breadboard compatible. You can use an adapter or simply temporarily solder some wire jumpers to the pins and connect them to breadboard for testing.

To communicate with the ESP8266 via a serial terminal you can make a USB to serial adapter like in this post.

Pinout

On the back side of the ESP there is the marking for the pins as follow

Pin Number Pin Name Alternate Name Normally used for Alternate purpose
1 GND Power ground
2 TX GPIO–1 UART Transmit.
Connected to Rx pin of programmer/u
Can act as a General purpose
input/output pin when not used as TX
3 IO2 General purpose input/output pin 2
4 EN Chip Enable – active high
5 IO0 Flash General purpose input/output pin 0 Takes module into serial programming
when held low during start up
6 RST Resets the module
7 RX GPIO-3 UART Receive
Connected to Tx pin of programmer/uC
Can act as a General purpose
input/output pin when not used as RX
8 3V3 VCC. Connect to +3.3V only

Schematic

ESP8266 schematic

R16 is a 10k resistor used to pull-up the Reset pin. VCC and EN (enable) pins are connected to 3.3V. The Reset pin is connected also to a momentary switch and when pressed it resets the switch. GPIO2 can be connected to a header but is optional. Same for GPIO0 except this pin is pulled high by R1 a 10k resistor. SW32 is another momentary switch that pulls GPIO0 to ground when pressed and is used only for programming (flashing) the chip. If you are using only AT commands, the two switches can be omitted.



ESP8266 library using AT commands

This library depends on the following UART library that can be downloaded from here.
For now this library is only made for EFM8BB1 Busy Bee microcontrollers but in the future it will be adapted for AVR also.

In the ESP8266.h file there are a few optionalsettings

#define DEBUGGING               1 // default 0

If set to 1 some error messages will be displayed over UART in case of setup errors.
Debugging tip: when testing the interface between the MCU and ESP you can use the Rx (receive0 pin of an USB to serial adapter to see the communication messages between the two. If you put the Rx pin on Tx of the microcontroller then you can see what MCU is transmitting. If Rx is connected to Rx of the microcontroller then you can see what ESP8266 is transmitting.

#define USE_SERVER_MODE  1

Set this to 0 if not using the ESP in server mode, to save some space.

For setting the ESP8266 in server mode read this article ESP8266 library for EFM8BB1 Busy Bee microcontrollers - Server mode

Library setup

In the library header put the router's SSID and password. And that's it.

ESP8266.h
//=================================================================================
// Global CONSTANTS
//=================================================================================
// SSID + KEY
static const uint8_t xdata ESP_SSID[] = "YOUR SSID";  // YOUR SSID
static const uint8_t xdata ESP_SSID_KEY[] = "YOUR PASSWORD"; // YOUR PASSWORD

In your main c file add the following define. These defines must be added before the includes

main.c
//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
#define F_CPU                     24500000 // CPU FREQUENCY IN Hz
#define BAUD_RATE                 115200
#define RECEIVED_DATA_ARRAY_SIZE  41 // size of array where to store the incoming data
#define URL_VALUE_NR_OF_DIGITS    4

BAUD_RATE is the speed that the microcontroller will communicate with the ESP. Usually 9600 or 115200 is used. If you see gibberish in the serial terminal, try another baud rate.
RECEIVED_DATA_ARRAY_SIZE is the size of the array declared later bellow, in which the received data from internet will be stored. Be sure to leave 1 byte for the null character that indicates the end of string.
URL_VALUE_NR_OF_DIGITS will be discussed later, but in short is just a placeholder containing 0's for the URL query values.


Then include the following. The delay.h can be downloaded from here.

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

Library Functions


ESP_Setup(wifi_mode, tcp_ip_mode)

Function:
sets up the UART at declared baud rate, checks if ESP module is responding and then sets up the Wi-Fi mode, TCP/IP mode and connects the ESP to the router with offered credentials.

Arguments:
wifi_mode: one of the available macros STATION_MODE, ACCESS_POINT_MODE or AP_AND_ST_MODE. For now only STATION_MODE is supported by the library.

STATION_MODE - ESP connects to the Internet through router (most used)
ACCESS_POINT_MODE - ESP is used to connect other devices to the Internet through router
AP_AND_ST_MODE - Both modes
tcp_ip_mode: one of the available macros CLIENT (most used) or SERVER. For now only CLIENT mode is supported by the library.
Returns:
true if successful or false if not. If an error occurs during all the steps, the ESP will be reset 2 times and if still the error persist the function will return false indicating that ESP setup failed

ESP_Connect(const char *domain, const char *port, const char *payload)

Function:
connect to a URL to send or retrieve data
Arguments:
domain: domain name
port: port in the form of a string, usually 80
payload: the URL to connect to without the domain. Here the URL query and HTTP headers are sent
Returns:
true if successful or false if not

ESP_ParseAndCheck(char *keywords[],  bool match_all, bool uppercase_all, uint16_t timeout_ms, char data_ret[], uint8_t array_size)

Function:
used by the above two functions to check the response from the ESP8266 if it contains certain keywords like CONNECT OK or ERROR.
After ESP_Connect(0 exits, the ESP will start dumping data in the UART Rx buffer  so this function must be executed as soon as possible in order to process the incoming data.
This function is very useful to extract some data between two keywords. Also the user can provide up to 4 keywords and it will return true or false if one or all keywords are found in the incoming data from the internet
Arguments:
keywords: a pointer to an array of up to 4 keywords to be searched. See the examples bellow to see how to use this
match_all: 1 or 0. If 1, the function will return true only if all the keywords are found in the incoming string of data. If you want to know if only one keyword was found then send 0
uppercase_all: if true then the keywords and the incoming data will be converted to uppercase characters. Is useful when say for example you want to find OK and the data contains Ok. Without uppercase the function will return false - not found
timeout_ms: timeout in milliseconds. Depending of how much data you expect to receive set this to a few seconds. If the keywords are found the function will exit before timeout. If the keywords are not found the function will wait until timeout
data_ret: an array that user provides in which the data between two keywords will be stored
array_size: the size of the data_ret array. The array must be sized to fit expected data + the size of second keyword + 1 for null character
Returns:
true if a keyword or all keywords are found - depending of match_all argument, false if not

ESP_UpdateQueryString(char URL[], const char *query, int16_t new_value, uint8_t nrOfDigits)

Function:
Modifies the values of  an URL query string. See examples bellow
Arguments:
URL: the URL
query: the URL query string including = for which the value needs to be modified
value: the new value after the = sign
nrOfDigits: how many digits is the value formed of
Returns:
nothing



Extra Functions


ESP_DisconnectAP(void)

Function:
Disconnects the ESP from the router. To reconnect it again, run ESP_Setup() again.
Arguments:
none
Returns:
true if successful or false if not

Practical Examples

Log temperature and humidity on thingspeak.com using ESP8266

In the following code are presented two examples of how to use this ESP8266 library.
URL_1 is used to log temperature and humidity on thingspeak.com. You can use the api keys that I provided and see the results by visiting this public channel that I've made for testing purposes https://thingspeak.com/channels/429357.

URL_2 is another example that uses an app from thingspeak to receive a command string. This string is extracted from the received data and the HTTP headers and other unnecessary data is discarded. This way you can parse megabytes of received data and keep only a small part that matters, in a small array.

A small delay of a few milliseconds should be added between connections because after the connection is established, the ESP has to receive at least some HTTP headers in response. After that you can initiate a new connection.
Also depending of what sites you use for IoT, there needs to be a waiting time between connections like 5 seconds to prevent the site being flooded with too many connections in a short time.

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

//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
#define F_CPU          24500000 // CPU FREQUENCY IN Hz
#define BAUD_RATE        115200
#define RECEIVED_DATA_ARRAY_SIZE   41 // size of array where to store the incoming data
#define URL_VALUE_NR_OF_DIGITS    4

//-----------------------------------------------------------------------------
// Global VARIABLES
//-----------------------------------------------------------------------------
int8_t field1_temperature = 27;
int8_t field2_humidity = 65;

//-----------------------------------------------------------------------------
// Global CONSTANTS
//-----------------------------------------------------------------------------
// URLs (IMPORTANT: don't forget to add the HTTP headers after the URL HTTP/1.0\r\n\r\n)
char xdata URL_1[] = "GET /update?api_key=2CY50F6OFB6RWP06&field1=0000&field2=0000 HTTP/1.0\r\n\r\n";
char xdata URL_2[] = "GET /talkbacks/23510/commands/14569888.json?api_key=0JAXMR1PR130D6XM HTTP/1.0\r\n\r\n";
const char *URL_1_DOMAIN = "api.thingspeak.com";
const char *ESP_PORT = "80";

//-----------------------------------------------------------------------------
// 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
  // ------------------------
  uint8_t xdata receivedData[RECEIVED_DATA_ARRAY_SIZE] = {'\0'};
  // Number of keywords in this case is 2 but the array must be of size 3 so one byte is for null terminator
  char *keywords[3] = {"command_string\":\"", "\",\"position"};
  bool esp_setup_ok = false;
  bool esp_url_sent_ok = false;
 
  //-------------------------
  // Initialization
  // ------------------------
  Port_Init();
 
  _delay_ms(2000);
 
  esp_setup_ok = ESP_Setup(STATION_MODE, CLIENT);
 
  // Update and send measured temperature and humidity
  if(esp_setup_ok){
  // Update the URL's query string with a new value 
  ESP_UpdateQueryString(URL_1, "field1=", field1_temperature, URL_VALUE_NR_OF_DIGITS);
  
  // Update the URL's query string with a new value
  ESP_UpdateQueryString(URL_1, "field2=", field2_humidity, URL_VALUE_NR_OF_DIGITS);
  
  // Send measured temperature and humidity
  ESP_Connect(URL_1_DOMAIN, ESP_PORT, URL_1);
  
  _delay_ms(1000);
  
  // Receive and parse data from an URL
  if(ESP_Connect(URL_1_DOMAIN, ESP_PORT, URL_2)){
   // Incoming data will be
   // {"id":14569888,"command_string":"Important string that needs to be parsed","position":1,"executed_at":null,"created_at":"2018-11-29T03:46:03Z"}
   // and we want to extract this: Important string that needs to be parsed
   // So the first keyword will be <command_string":"> and the second keyword will be <","position> The " must be escaped by putting \ in front of them
   if(ESP_ParseAndCheck(keywords, true, false, 10000, receivedData, RECEIVED_DATA_ARRAY_SIZE)){
    // Display received and parsed data
    UART_Send("\n");
    UART_Send(receivedData);
    UART_Send("\nKeyword found\n");
   }else{
    UART_Send("\nKeyword not found\n");
   }
  }
 }
 
 
 // Some servers don't allow many queries in a short time so add the necessary delay
 _delay_ms(2000);
 
 // Change the keywords if necessary
 keywords[0] = "id\":";
 keywords[1] = ",\"";
 
 // Receive and parse data from another URL
 if(esp_setup_ok){
  if(ESP_Connect(URL_1_DOMAIN, ESP_PORT, URL_2)){
   // Incoming data will be
   // {"id":14569888,"command_string":"Important string that needs to be parsed","position":1,"executed_at":null,"created_at":"2018-11-29T03:46:03Z"}
   // and we want to extract id: 14569888
   // So the first keyword will be <id":> and the second keyword will be <,"> The " must be escaped by putting \ in front of them
   if(ESP_ParseAndCheck(keywords, false, false, 10000, receivedData, RECEIVED_DATA_ARRAY_SIZE)){ 
    // Display received and parsed data
    UART_Send("\n");
    UART_Send(receivedData);
    UART_Send("\nKeyword found\n");
   }else{
    UART_Send("\nKeyword not found\n");
   }
  }
 }
 
 // Update and send measured temperature and humidity
 if(esp_setup_ok){
  field1_temperature = -5;
  field2_humidity = 55;
  
  // Update the URL's query string with a new value
  ESP_UpdateQueryString(URL_1, "field1=", field1_temperature, URL_VALUE_NR_OF_DIGITS);
  
  // Update the URL's query string with a new value
  ESP_UpdateQueryString(URL_1, "field2=", field2_humidity, URL_VALUE_NR_OF_DIGITS);
  
  // Send measured temperature and humidity
  esp_url_sent_ok = ESP_Connect(URL_1_DOMAIN, ESP_PORT, URL_1);
 }
 
  //-------------------------
  // while loop
  // ------------------------
  while(1){
 
 }
}


//-----------------------------------------------------------------------------
// 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)
 // -------------------------------------------------------------
 
 // -------------------------------------------------------------
 // 3. Assign port pins to desired peripherals
 // -------------------------------------------------------------
 
 // -------------------------------------------------------------
 // 4. Enable crossbar and weak pull-ups
 // -------------------------------------------------------------
 XBR2 |= 0x40;
 
 // SYSCLK - CPU speed
 CLKSEL = CLKSEL_CLKSL__HFOSC | CLKSEL_CLKDIV__SYSCLK_DIV_1;
}


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



You can leave questions or report bugs in the comment section bellow.

Download

v2.0
+ support for server mode
- fixed some errors in the search functions
ESP8266.h 

v1.0
ESP8266.h

Other resources

delay.h
globals.h

Setting the ESP8266 in server mode
ESP8266 library for EFM8BB1 Busy Bee microcontrollers - Server mode

AT instruction set
https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf

No comments:

Post a Comment

AddToAny

Related Posts Plugin for WordPress, Blogger...