Taillieu.Info

More Than a Hobby..

Arduino

  • ESP8266 SPI Flash File System

    Up until now, we've always included the HTML for our web pages as string literals in our sketch. This makes our code very hard to read, and you'll run out of memory rather quickly.
    If you remember the introduction, I mentioned the Serial Peripheral Interface Flash File System, or SPIFFS for short. It's a light-weight file system for microcontrollers with an SPI flash chip. The on-board flash chip of the ESP8266 has plenty of space for your webpages, especially if you have the 1MB, 2MB or 4MB version.
     
    SPIFFS let's you access the flash memory as if it was a normal file system like the one on your computer (but much simpler of course): you can read and write files, create folders...
     
    The easiest way to learn how to use SPIFFS is to look at some examples. But a file server with no files to serve is pretty pointless, so I'll explain how to upload files to the SPIFFS first.

    Uploading files to SPIFFS

    To select the right files to upload, you have to place them in a folder called data, inside the sketch folder of your project: Open your sketch in the Arduino IDE, and hit CTRL+K. Wait for a file explorer window to open, and create a new folder named data. Copy your files over to this folder. (Only use small files like text files or icons. There's not enough space for large photos or videos.)
    Next, select all files in the folder (CTRL+A) and check the size of all files combined (don't forget subfolders). Go to the Arduino IDE again, and under Tools > Flash Size, select an option with the right flash size for your board, and a SPIFFS size that is larger than the size of your data folder.
    Then upload the sketch. When that's finished, make sure that the Serial Monitor is closed, then open the Tools menu, and click ESP8266 sketch data upload. If your ESP has auto-reset and auto-program, it should work automatically, if you don't have auto-program, you have to manually enter program mode before uploading the data to SPIFFS. The procedure is exactly the same as entering program mode before uploading a sketch.
     
    If you get an error saying  
    SPIFFS_write error(-10001): File system is full
     , this means that your files are too large to fit into the SPIFFS memory. Select a larger SPIFFS size under Tools > Flash Size, or delete some files. 
    Even if your computer says that the files are smaller than the selected SPIFFS size, you can still get this error: this has to do with block sizes, and metadata like file and folder names that take up space as well.
     
    If you change the SPIFFS size, you have to reupload your sketch, because when you change the SPIFFS size, the memory location will be different. The program has to know the updated SPIFFS address offset to be able to read the files.

    SPIFFS File Server

    The following example is a very basic file server: it just takes the URI of the HTTP request, checks if the URI points to a file in the SPIFFS, and if it finds the file, it sends it as a response.
    #include <ESP8266WiFi.h>
    #include <WiFiClient.h>
    #include <ESP8266WiFiMulti.h>
    #include <ESP8266mDNS.h>
    #include <ESP8266WebServer.h>
    #include <FS.h>   // Include the SPIFFS library
    
    ESP8266WiFiMulti wifiMulti;     // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'
    
    ESP8266WebServer server(80);    // Create a webserver object that listens for HTTP request on port 80
    
    String getContentType(String filename); // convert the file extension to the MIME type
    bool handleFileRead(String path);       // send the right file to the client (if it exists)
    
    void setup() {
      Serial.begin(115200);         // Start the Serial communication to send messages to the computer
      delay(10);
      Serial.println('\n');
    
      wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");   // add Wi-Fi networks you want to connect to
      wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
      wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
    
      Serial.println("Connecting...");
      int i = 0;
      while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
        delay(250);
        Serial.print('.');
      }
      Serial.println('\n');
      Serial.print("Connected to ");
      Serial.println(WiFi.SSID());              // Tell us what network we're connected to
      Serial.print("IP address:\t");
      Serial.println(WiFi.localIP());           // Send the IP address of the ESP8266 to the computer
    
      if (MDNS.begin("esp8266")) {              // Start the mDNS responder for esp8266.local
        Serial.println("mDNS responder started");
      } else {
        Serial.println("Error setting up MDNS responder!");
      }
    
      SPIFFS.begin();                           // Start the SPI Flash Files System
      
      server.onNotFound([]() {                              // If the client requests any URI
        if (!handleFileRead(server.uri()))                  // send it if it exists
          server.send(404, "text/plain", "404: Not Found"); // otherwise, respond with a 404 (Not Found) error
      });
    
      server.begin();                           // Actually start the server
      Serial.println("HTTP server started");
    }
    
    void loop(void) {
      server.handleClient();
    }
    
    String getContentType(String filename) { // convert the file extension to the MIME type
      if (filename.endsWith(".html")) return "text/html";
      else if (filename.endsWith(".css")) return "text/css";
      else if (filename.endsWith(".js")) return "application/javascript";
      else if (filename.endsWith(".ico")) return "image/x-icon";
      return "text/plain";
    }
    
    bool handleFileRead(String path) { // send the right file to the client (if it exists)
      Serial.println("handleFileRead: " + path);
      if (path.endsWith("/")) path += "index.html";         // If a folder is requested, send the index file
      String contentType = getContentType(path);            // Get the MIME type
      if (SPIFFS.exists(path)) {                            // If the file exists
        File file = SPIFFS.open(path, "r");                 // Open it
        size_t sent = server.streamFile(file, contentType); // And send it to the client
        file.close();                                       // Then close the file again
        return true;
      }
      Serial.println("\tFile Not Found");
      return false;                                         // If the file doesn't exist, return false
    }
    As you can see, we don't use 
    server.on
     in this example. Instead, we use 
    server.onNotFound
     : this will match any URI, since we didn't declare any specific URI handlers like in the previous server examples.
    When a URI is requested, we call the function 
    handleFileRead
     . This function checks if the URI of the HTTP request is the path to an existing file in the SPIFFS. If that's the case, it sends the file back to the client. If the path doesn't exist, it returns false, and a 404 (Not Found) HTTP status will be sent.
     
    The MIME type for the different files is based on the file extension.
    You could add other file types as well. For instance:
    String getContentType(String filename){
      if(filename.endsWith(".htm")) return "text/html";
      else if(filename.endsWith(".html")) return "text/html";
      else if(filename.endsWith(".css")) return "text/css";
      else if(filename.endsWith(".js")) return "application/javascript";
      else if(filename.endsWith(".png")) return "image/png";
      else if(filename.endsWith(".gif")) return "image/gif";
      else if(filename.endsWith(".jpg")) return "image/jpeg";
      else if(filename.endsWith(".ico")) return "image/x-icon";
      else if(filename.endsWith(".xml")) return "text/xml";
      else if(filename.endsWith(".pdf")) return "application/x-pdf";
      else if(filename.endsWith(".zip")) return "application/x-zip";
      else if(filename.endsWith(".gz")) return "application/x-gzip";
      return "text/plain";
    }
     
    This example is adapted from the FSBrowser example by Hristo Gochkov.

    Compressing files

    The ESP8266's flash memory isn't huge, and most text files, like html, css etc. can be compressed by quite a large factor. Modern web browsers accept compressed files as a response, so we'll take advantage of this by uploading compressed versions of our html and icon files to the SPIFFS, in order to save space and bandwidth.
     
    To do this, we need to add the GNU zip file type to our list of MIME types:
    String getContentType(String filename){
      if(filename.endsWith(".html")) return "text/html";
      else if(filename.endsWith(".css")) return "text/css";
      else if(filename.endsWith(".js")) return "application/javascript";
      else if(filename.endsWith(".ico")) return "image/x-icon";
      else if(filename.endsWith(".gz")) return "application/x-gzip";
      return "text/plain";
    }
    And we need to change our  
    handleFileRead
      function as well:
    bool handleFileRead(String path){  // send the right file to the client (if it exists)
      Serial.println("handleFileRead: " + path);
      if(path.endsWith("/")) path += "index.html";           // If a folder is requested, send the index file
      String contentType = getContentType(path);             // Get the MIME type
      String pathWithGz = path + ".gz";
      if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){  // If the file exists, either as a compressed archive, or normal
        if(SPIFFS.exists(pathWithGz))                          // If there's a compressed version available
          path += ".gz";                                         // Use the compressed version
        File file = SPIFFS.open(path, "r");                    // Open the file
        size_t sent = server.streamFile(file, contentType);    // Send it to the client
        file.close();                                          // Close the file again
        Serial.println(String("\tSent file: ") + path);
        return true;
      }
      Serial.println(String("\tFile Not Found: ") + path);
      return false;                                          // If the file doesn't exist, return false
    }
    
    Now, try compressing some of the files to the GNU zip format (.gz), and uploading them to SPIFFS. Or you can just download the new data folder (unzip it first).
    Every time a client requests a certain file, the ESP will check if a compressed version is available. If so, it will use that instead of the uncompressed file. The output in the Serial Monitor should look something like this:
    handleFileRead: /
    	Sent file: /index.html.gz
    handleFileRead: /main.css
    	Sent file: /main.css
    handleFileRead: /JavaScript.js
    	Sent file: /JavaScript.js
    handleFileRead: /folder/JavaScript.js
    	Sent file: /folder/JavaScript.js
    handleFileRead: /favicon.ico
    	Sent file: /favicon.ico.gz
     It automatically detected that it had to send the compressed versions of index.html and favicon.ico.
  • ESP8266 Uploading files to the server

    There are scenarios where you may want to upload new files to the server from within a browser, without having to connect to the ESP8266 over USB in order to flash a new SPIFFS image.
    In this chapter, I'll show you how to use HTML forms and POST requests to upload or edit files to our little ESP server.

    Client: HTML form

    The easiest way to upload files is by using an HTML form, just like in the first server examples, where we used forms to turn on/off LEDs, and to send the login credentials back to the server. If you choose a file input, you automatically get a file picker, and the browser will send the right POST request to the server, with the file attached.
    <form method="post" enctype="multipart/form-data">
        <input type="file" name="name">
        <input class="button" type="submit" value="Upload">
    </form>

    Server

    In the ESP code, we have to add a handler to our server that handles POST requests to the /upload URI. When it receives a POST request, it sends a status 200 (OK) back to the client to start receiving the file, and then write it to the SPIFFS. When the file is uploaded successfully, it redirects the client to a success page.
    The relevant new code is found in the 
    setup
     and the 
    handleFileUpload
     function.
    #include <ESP8266WiFi.h>
    #include <WiFiClient.h>
    #include <ESP8266WiFiMulti.h>
    #include <ESP8266mDNS.h>
    #include <ESP8266WebServer.h>
    #include <FS.h>   // Include the SPIFFS library
    
    ESP8266WiFiMulti wifiMulti;     // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'
    
    ESP8266WebServer server(80);    // Create a webserver object that listens for HTTP request on port 80
    
    File fsUploadFile;              // a File object to temporarily store the received file
    
    String getContentType(String filename); // convert the file extension to the MIME type
    bool handleFileRead(String path);       // send the right file to the client (if it exists)
    void handleFileUpload();                // upload a new file to the SPIFFS
    
    void setup() {
      Serial.begin(115200);         // Start the Serial communication to send messages to the computer
      delay(10);
      Serial.println('\n');
    
      wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");   // add Wi-Fi networks you want to connect to
      wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
      wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
    
      Serial.println("Connecting...");
      int i = 0;
      while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
        delay(1000);
        Serial.print(++i); Serial.print(' ');
      }
      Serial.println('\n');
      Serial.print("Connected to ");
      Serial.println(WiFi.SSID());              // Tell us what network we're connected to
      Serial.print("IP address:\t");
      Serial.println(WiFi.localIP());           // Send the IP address of the ESP8266 to the computer
    
      if (!MDNS.begin("esp8266")) {             // Start the mDNS responder for esp8266.local
        Serial.println("Error setting up MDNS responder!");
      }
      Serial.println("mDNS responder started");
    
      SPIFFS.begin();                           // Start the SPI Flash Files System
    
      server.on("/upload", HTTP_GET, []() {                 // if the client requests the upload page
        if (!handleFileRead("/upload.html"))                // send it if it exists
          server.send(404, "text/plain", "404: Not Found"); // otherwise, respond with a 404 (Not Found) error
      });
    
      server.on("/upload", HTTP_POST,                       // if the client posts to the upload page
        [](){ server.send(200); },                          // Send status 200 (OK) to tell the client we are ready to receive
        handleFileUpload                                    // Receive and save the file
      );
    
      server.onNotFound([]() {                              // If the client requests any URI
        if (!handleFileRead(server.uri()))                  // send it if it exists
          server.send(404, "text/plain", "404: Not Found"); // otherwise, respond with a 404 (Not Found) error
      });
    
      server.begin();                           // Actually start the server
      Serial.println("HTTP server started");
    }
    
    void loop() {
      server.handleClient();
    }
    
    String getContentType(String filename) { // convert the file extension to the MIME type
      if (filename.endsWith(".html")) return "text/html";
      else if (filename.endsWith(".css")) return "text/css";
      else if (filename.endsWith(".js")) return "application/javascript";
      else if (filename.endsWith(".ico")) return "image/x-icon";
      else if (filename.endsWith(".gz")) return "application/x-gzip";
      return "text/plain";
    }
    
    bool handleFileRead(String path) { // send the right file to the client (if it exists)
      Serial.println("handleFileRead: " + path);
      if (path.endsWith("/")) path += "index.html";          // If a folder is requested, send the index file
      String contentType = getContentType(path);             // Get the MIME type
      String pathWithGz = path + ".gz";
      if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { // If the file exists, either as a compressed archive, or normal
        if (SPIFFS.exists(pathWithGz))                         // If there's a compressed version available
          path += ".gz";                                         // Use the compressed verion
        File file = SPIFFS.open(path, "r");                    // Open the file
        size_t sent = server.streamFile(file, contentType);    // Send it to the client
        file.close();                                          // Close the file again
        Serial.println(String("\tSent file: ") + path);
        return true;
      }
      Serial.println(String("\tFile Not Found: ") + path);   // If the file doesn't exist, return false
      return false;
    }
    
    void handleFileUpload(){ // upload a new file to the SPIFFS
      HTTPUpload& upload = server.upload();
      if(upload.status == UPLOAD_FILE_START){
        String filename = upload.filename;
        if(!filename.startsWith("/")) filename = "/"+filename;
        Serial.print("handleFileUpload Name: "); Serial.println(filename);
        fsUploadFile = SPIFFS.open(filename, "w");            // Open the file for writing in SPIFFS (create if it doesn't exist)
        filename = String();
      } else if(upload.status == UPLOAD_FILE_WRITE){
        if(fsUploadFile)
          fsUploadFile.write(upload.buf, upload.currentSize); // Write the received bytes to the file
      } else if(upload.status == UPLOAD_FILE_END){
        if(fsUploadFile) {                                    // If the file was successfully created
          fsUploadFile.close();                               // Close the file again
          Serial.print("handleFileUpload Size: "); Serial.println(upload.totalSize);
          server.sendHeader("Location","/success.html");      // Redirect the client to the success page
          server.send(303);
        } else {
          server.send(500, "text/plain", "500: couldn't create file");
        }
      }
    }
    The 
    handleFileUpload
     function just writes the file attached to the POST request to SPIFFS.
     
    If you wan to use other file types as well, you can just add them to the  
    getContentType
     function.

    Uploading files

    To upload a new file to the ESP, or to update an existing file, just go to http://esp8266.local/upload, click the Choose File button, select the file you wish to upload, and click Upload. You can now enter the URL into the URL bar, and open the new file.

    A note on safety

    This example isn't very secure (obviously). Everyone that can connect to the ESP can upload new files, or edit the existing files and insert XSS code, for example. There's also not a lot of error checking/handling, like checking if there's enough space in the SPIFFS to upload a new file, etc. 

    Advanced example

    The code for these SPIFFS server examples comes (for the most part) from an example written by Hristo Gochkov. You can find it under File > Examples > ESP8266WebServer > FSBrowser. It has a web interface for browsing and editing files in your browser, and has some other nice features as well.
  • ESP8266 Web Server

    Being able to ping the ESP is quite an achievement if you look at it from a technical point of view, but for most people, it's not that exciting, and not really useful.
    In this chapter, I'll cover the basics of a web server, and teach you how to host a web page on the ESP.
     

    Web servers

    A web server is an Internet-connected device that stores and serves files. Clients can request such a file or another piece of data, and the server will then send the right data/files back to the client. Requests are made using HTTP. 

    HTTP

    HTTP or the Hypertext Transfer Protocol is the text-based protocol used to communicate with (web) servers. There are multiple HTTP request methods, but I'll only cover the two most widely used ones: GET and POST. 

    HTTP GET

    GET requests are used to retrieve data from a server, a web page for instance. It shouldn't change anything on the server, it just gets the data from the server, without side effects.
     
    When you open a webpage in your browser, it will take the URL and put it in an HTTP GET request. This is just plain text. Then it will send the request to the right server using TCP. The server will read the request, check the URL, and send the right HTTP response for that URL back to the browser.

    The anatomy of a GET request

    The most important parts of a GET request are the request line and the host header. Let's take a look at an example:
    If you click the following link: https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html, your browser will send out the following HTTP request:
    GET /Protocols/rfc2616/rfc2616-sec5.html HTTP/1.1
    Host: www.w3.org
    Connection: keep-alive
    Pragma: no-cache
    Cache-Control: no-cache
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    DNT: 1
    Referer: https://www.google.be/
    Accept-Encoding: gzip, deflate, sdch, br
    Accept-Language: en-US,en;q=0.8
    The first line is the request line: it contains the request method: GET, in this case, the URI or Uniform Resource Identifier: /Protocols/rfc2616/rfc2616-sec5.html, and the HTTP version: 1.1.
    The second line is the host header, it specifies the domain name of the host (server).
    There are many other headers as well, but they're not really important when using an ESP8266.
    Most servers will check if the URI is a file on their file system, and if that's the case, they'll send that file as a response.

    Viewing HTTP headers in the browser

    If you want to check the headers your browser sends, you can press F12, go to the network tab, reload the page, and click the request you want to inspect. If you want, you can click 'view source', this will show you the actual HTTP text.
    Here's what that looks like in Chrome:

    http_chrome.png

    Sending extra information to the server

    Sometimes, you might want to add extra information to the GET request. You can send key-value pairs by adding a question mark (?) to the URI, followed by key=value. Multiple pairs are separated by an ampersand (&).
    For example:
    GET /get-phone-number.php?firstName=John&lastName=Doe HTTP/1.1
    Host: www.phonebook.example.com
    ...
    If you use any special characters in the key or value names, you have to URL-encode them.

    HTTP POST

    POST requests are used to send data to the server, for example, to send your user name and password to the server when you log in, or when you upload a photo. Unlike GET, POST can change the data on the server or the state of the server. 
    POST has a body that can contain data that is sent to the server.

    The anatomy of a POST request

    For example, the login page of your favorite site might send something like this when you enter your credentials and click the login button:
    POST /login.php HTTP/1.1
    Host: www.example.com
    Connection: keep-alive
    Content-Length: 480
    Origin: http://www.example.com
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQNEJOasMvgAOg8Kt
    ...
    As you can see, the request line now has the POST method in it, and is still followed by a URI, /login.php, and the HTTP version, 1.1. The host header still contains just the domain name.
     
    The real difference is the request body: a GET request has no payload, while you can add a lot of data to the body of a POST request. This data could be normal key-value pairs, like a username and a password, or actual files that are being uploaded. 
    Also note the Content-Type header: it tells the server what kind of data can be found in the body of the POST request.
     
    Let's take a look at the body of the login example: 
    ------WebKitFormBoundaryQNEJOasMvgAOg8Kt
    Content-Disposition: form-data; name="username"
    
    John Doe
    ------WebKitFormBoundaryQNEJOasMvgAOg8Kt
    Content-Disposition: form-data; name="password"
    
    p@ssw0rd123
    ------WebKitFormBoundaryQNEJOasMvgAOg8Kt
    Content-Disposition: form-data; name="token"
    
    9i9ZoLHl5pkRAeuKCEu76TbaCnMphwYkPEovEUY9PHk=
    ------WebKitFormBoundaryQNEJOasMvgAOg8Kt--
    As you can see, there are three parameters inside the body, every parameter has a name (e.g. username), and a value (e.g. John Doe).
     
    You could also use the same syntax we used before when adding parameters to a GET request:
    POST /add-user.php HTTP/1.1
    Host: www.example.com
    Content-Length: 27
    Content-Type: application/x-www-form-urlencoded
    ...
    And the payload:
    firstName=John&lastName=Doe
    As you can see, the Content-Typeheader is different, indicating that the encoding of the values in the payload is different.
     

    HTTP status codes

    A server should answer all requests with an HTTP status code. This is a 3-digit number indicating if the request was successful or telling the client what went wrong. Here's a table with some of the most important and useful ones.
    Status CodeMeaning
    200 OK: the request was successful
    303 See Other: used to redirect to a different URI, after a POST request, for instance
    400 Bad Request: the server couldn't understand the request, because the syntax was incorrect
    401 Unauthorized: user authentication is required
    403 Forbidden: the server refuses to execute the request, authorization won't help
    404 Not Found: the requested URI was not found
    500 Internal Server Error: The server encountered an unexpected condition and couldn't fulfill the request

    TCP & UDP Ports

    In most cases, one device has many different services, for example, a web server, an email server, an FTP server, a Spotify streaming service,...
    If the device had just an IP address, it would be impossible to know which application a packet was sent to. That's why every service has a port number. It's an identifier for all different services or applications on a single device. In the example above, the web server will only listen for requests on port 80, the email server only on port 25, the FTP server only on port 20, Spotify will only receive streams on port 4371...
    To specify a certain port, you can use a colon after the IP address of after the domain name. But most of the time, you don't have to add it explicitly. For example, all web servers listen on port 80, so a web browser will always connect to port 80.
  • ESP8266 WebSocket communication

    Up until now, we've always used links (GET) and HTML forms (POST) to get data from the ESP, or to send data to it. This always resulted in a browser navigation action. There are many situations where you want to send data to the ESP without refreshing the page. 
     
    One way to do this is by using AJAX and XMLHTTP requests. The disadvantage is that you have to establish a new TCP connection for every message you send. This adds a load of latency.
    WebSocket is a technology that keeps the TCP connection open, so you can constantly send data back and forth between the ESP and the client, with low latency. And since it's TCP, you're sure that the packets will arrive intact. 

    Controlling RGB LEDs from a web interface using WebSocket

    To learn how to use WebSockets, I created this comprehensive example, it uses pretty much everything we've covered so far.
     
    The ESP hosts a webpage with three sliders to set the red, green and blue levels of an RGB LED (or LED strip). There's also a button to turn on a rainbow effect that cycles through the entire color wheel. Color data is transmitted from the browser to the ESP via a WebSocket connection. 
    You can connect to the ESP directly, using it as an AP, or let the ESP connect to a different AP. You can use mDNS to open the webpage, by browsing to http://esp8266.local.
    All files are stored in the ESP's SPIFFS, and you can upload new files, or update files via a web interface
    You can also use the OTA service to upload new firmware (sketches) over Wi-Fi.

    Improving readability

    When dealing with large and complicated programs, it's a good idea to make abstraction of some things, and create functions with a descriptive name instead of endless lines of meaningless code. 
     
    Even if you have lots of comments in your code, it'll be very hard to preserve an overview. Using functions will greatly improve the readability of your code.
    So just split up the code into different parts and move all pieces to functions at the bottom of your sketch, or even to different files. 

    In the following example, the setup was very long and cluttered, so I split it up into several different functions: one to connect to the Wi-Fi, one to start the OTA update service, one to start the SPIFFS... and so on. 

    Downloading WebSockets for Arduino

    We'll be using the arduinoWebSockets library by Links2004. Download it from GitHub and install it. (Sketch > Include Library > Add .ZIP Library...)

    Libraries, constants and globals

    At the top of the sketch we'll include the necessary libraries, create some global server and file objects like in the previous examples, and some constants for the host name, AP ssid, passwords, LED pins...
    #include <ESP8266WiFi.h>
    #include <ESP8266WiFiMulti.h>
    #include <ArduinoOTA.h>
    #include <ESP8266WebServer.h>
    #include <ESP8266mDNS.h>
    #include <FS.h>
    #include <WebSocketsServer.h>
    
    ESP8266WiFiMulti wifiMulti;       // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'
    
    ESP8266WebServer server = ESP8266WebServer(80);       // create a web server on port 80
    WebSocketsServer webSocket = WebSocketsServer(81);    // create a websocket server on port 81
    
    File fsUploadFile;                                    // a File variable to temporarily store the received file
    
    const char *ssid = "ESP8266 Access Point"; // The name of the Wi-Fi network that will be created
    const char *password = "thereisnospoon";   // The password required to connect to it, leave blank for an open network
    
    const char *OTAName = "ESP8266";           // A name and a password for the OTA service
    const char *OTAPassword = "esp8266";
    
    #define LED_RED     15            // specify the pins with an RGB LED connected
    #define LED_GREEN   12
    #define LED_BLUE    13
    
    const char* mdnsName = "esp8266"; // Domain name for the mDNS responder
    You should already be familiar with most of this code. The only new part is the WebSocket server library that is included, and the WebSocket server object, but this shouldn't be a problem.

    Setup

    void setup() {
      pinMode(LED_RED, OUTPUT);    // the pins with LEDs connected are outputs
      pinMode(LED_GREEN, OUTPUT);
      pinMode(LED_BLUE, OUTPUT);
    
      Serial.begin(115200);        // Start the Serial communication to send messages to the computer
      delay(10);
      Serial.println("\r\n");
    
      startWiFi();                 // Start a Wi-Fi access point, and try to connect to some given access points. Then wait for either an AP or STA connection
      
      startOTA();                  // Start the OTA service
      
      startSPIFFS();               // Start the SPIFFS and list all contents
    
      startWebSocket();            // Start a WebSocket server
      
      startMDNS();                 // Start the mDNS responder
    
      startServer();               // Start a HTTP server with a file read handler and an upload handler
      
    }
    As you can see, the setup is now much more condensed and gives a much better overview of what it's doing. To understand the program, you don't have to know each individual step that is required to connect to a Wi-Fi network, it's enough to know that it will connect to a Wi-Fi network, because that's what the startWiFi function does.

    Loop

    bool rainbow = false;             // The rainbow effect is turned off on startup
    
    unsigned long prevMillis = millis();
    int hue = 0;
    
    void loop() {
      webSocket.loop();                           // constantly check for websocket events
      server.handleClient();                      // run the server
      ArduinoOTA.handle();                        // listen for OTA events
    
      if(rainbow) {                               // if the rainbow effect is turned on
        if(millis() > prevMillis + 32) {          
          if(++hue == 360)                        // Cycle through the color wheel (increment by one degree every 32 ms)
            hue = 0;
          setHue(hue);                            // Set the RGB LED to the right color
          prevMillis = millis();
        }
      }
    }
    Same goes for the loop: most of the work is done by the first three functions that handle the WebSocket communication, HTTP requests and OTA updates. When such an event happens, the appropriate handler functions will be executed. These are defined elsewhere.
     
    The second part is the rainbow effect. If it is turned on, it cycles through the color wheel and sets the color to the RGB LED. 
    If you don't understand why I use millis(), you can take a look at the Blink Without Delay example.

    Setup functions

    void startWiFi() { // Start a Wi-Fi access point, and try to connect to some given access points. Then wait for either an AP or STA connection
      WiFi.softAP(ssid, password);             // Start the access point
      Serial.print("Access Point \"");
      Serial.print(ssid);
      Serial.println("\" started\r\n");
    
      wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");   // add Wi-Fi networks you want to connect to
      wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
      wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
    
      Serial.println("Connecting");
      while (wifiMulti.run() != WL_CONNECTED && WiFi.softAPgetStationNum() < 1) {  // Wait for the Wi-Fi to connect
        delay(250);
        Serial.print('.');
      }
      Serial.println("\r\n");
      if(WiFi.softAPgetStationNum() == 0) {      // If the ESP is connected to an AP
        Serial.print("Connected to ");
        Serial.println(WiFi.SSID());             // Tell us what network we're connected to
        Serial.print("IP address:\t");
        Serial.print(WiFi.localIP());            // Send the IP address of the ESP8266 to the computer
      } else {                                   // If a station is connected to the ESP SoftAP
        Serial.print("Station connected to ESP8266 AP");
      }
      Serial.println("\r\n");
    }
    
    void startOTA() { // Start the OTA service
      ArduinoOTA.setHostname(OTAName);
      ArduinoOTA.setPassword(OTAPassword);
    
      ArduinoOTA.onStart([]() {
        Serial.println("Start");
        digitalWrite(LED_RED, 0);    // turn off the LEDs
        digitalWrite(LED_GREEN, 0);
        digitalWrite(LED_BLUE, 0);
      });
      ArduinoOTA.onEnd([]() {
        Serial.println("\r\nEnd");
      });
      ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
        Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
      });
      ArduinoOTA.onError([](ota_error_t error) {
        Serial.printf("Error[%u]: ", error);
        if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
        else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
        else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
        else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
        else if (error == OTA_END_ERROR) Serial.println("End Failed");
      });
      ArduinoOTA.begin();
      Serial.println("OTA ready\r\n");
    }
    
    void startSPIFFS() { // Start the SPIFFS and list all contents
      SPIFFS.begin();                             // Start the SPI Flash File System (SPIFFS)
      Serial.println("SPIFFS started. Contents:");
      {
        Dir dir = SPIFFS.openDir("/");
        while (dir.next()) {                      // List the file system contents
          String fileName = dir.fileName();
          size_t fileSize = dir.fileSize();
          Serial.printf("\tFS File: %s, size: %s\r\n", fileName.c_str(), formatBytes(fileSize).c_str());
        }
        Serial.printf("\n");
      }
    }
    
    void startWebSocket() { // Start a WebSocket server
      webSocket.begin();                          // start the websocket server
      webSocket.onEvent(webSocketEvent);          // if there's an incomming websocket message, go to function 'webSocketEvent'
      Serial.println("WebSocket server started.");
    }
    
    void startMDNS() { // Start the mDNS responder
      MDNS.begin(mdnsName);                        // start the multicast domain name server
      Serial.print("mDNS responder started: http://");
      Serial.print(mdnsName);
      Serial.println(".local");
    }
    
    void startServer() { // Start a HTTP server with a file read handler and an upload handler
      server.on("/edit.html",  HTTP_POST, []() {  // If a POST request is sent to the /edit.html address,
        server.send(200, "text/plain", ""); 
      }, handleFileUpload);                       // go to 'handleFileUpload'
    
      server.onNotFound(handleNotFound);          // if someone requests any other file or page, go to function 'handleNotFound'
                                                  // and check if the file exists
    
      server.begin();                             // start the HTTP server
      Serial.println("HTTP server started.");
    }
    These are the function definitions of the functions used in the setup. Nothing new here, apart from the startWebSocket function. You just have to start the WebSocket server using the begin method, and then give it a callback function that is executed when the ESP receives a WebSocket message.

    Server handlers

    This is the code that is executed on certain server-related events, like when an HTTP request is received, when a file is being uploaded, when there's an incoming WebSocket message... etc. 
    void handleNotFound(){ // if the requested file or page doesn't exist, return a 404 not found error
      if(!handleFileRead(server.uri())){          // check if the file exists in the flash memory (SPIFFS), if so, send it
        server.send(404, "text/plain", "404: File Not Found");
      }
    }
    
    bool handleFileRead(String path) { // send the right file to the client (if it exists)
      Serial.println("handleFileRead: " + path);
      if (path.endsWith("/")) path += "index.html";          // If a folder is requested, send the index file
      String contentType = getContentType(path);             // Get the MIME type
      String pathWithGz = path + ".gz";
      if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { // If the file exists, either as a compressed archive, or normal
        if (SPIFFS.exists(pathWithGz))                         // If there's a compressed version available
          path += ".gz";                                         // Use the compressed verion
        File file = SPIFFS.open(path, "r");                    // Open the file
        size_t sent = server.streamFile(file, contentType);    // Send it to the client
        file.close();                                          // Close the file again
        Serial.println(String("\tSent file: ") + path);
        return true;
      }
      Serial.println(String("\tFile Not Found: ") + path);   // If the file doesn't exist, return false
      return false;
    }
    
    void handleFileUpload(){ // upload a new file to the SPIFFS
      HTTPUpload& upload = server.upload();
      String path;
      if(upload.status == UPLOAD_FILE_START){
        path = upload.filename;
        if(!path.startsWith("/")) path = "/"+path;
        if(!path.endsWith(".gz")) {                          // The file server always prefers a compressed version of a file 
          String pathWithGz = path+".gz";                    // So if an uploaded file is not compressed, the existing compressed
          if(SPIFFS.exists(pathWithGz))                      // version of that file must be deleted (if it exists)
             SPIFFS.remove(pathWithGz);
        }
        Serial.print("handleFileUpload Name: "); Serial.println(path);
        fsUploadFile = SPIFFS.open(path, "w");            // Open the file for writing in SPIFFS (create if it doesn't exist)
        path = String();
      } else if(upload.status == UPLOAD_FILE_WRITE){
        if(fsUploadFile)
          fsUploadFile.write(upload.buf, upload.currentSize); // Write the received bytes to the file
      } else if(upload.status == UPLOAD_FILE_END){
        if(fsUploadFile) {                                    // If the file was successfully created
          fsUploadFile.close();                               // Close the file again
          Serial.print("handleFileUpload Size: "); Serial.println(upload.totalSize);
          server.sendHeader("Location","/success.html");      // Redirect the client to the success page
          server.send(303);
        } else {
          server.send(500, "text/plain", "500: couldn't create file");
        }
      }
    }
    
    void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { // When a WebSocket message is received
      switch (type) {
        case WStype_DISCONNECTED:             // if the websocket is disconnected
          Serial.printf("[%u] Disconnected!\n", num);
          break;
        case WStype_CONNECTED: {              // if a new websocket connection is established
            IPAddress ip = webSocket.remoteIP(num);
            Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
            rainbow = false;                  // Turn rainbow off when a new connection is established
          }
          break;
        case WStype_TEXT:                     // if new text data is received
          Serial.printf("[%u] get Text: %s\n", num, payload);
          if (payload[0] == '#') {            // we get RGB data
            uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);   // decode rgb data
            int r = ((rgb >> 20) & 0x3FF);                     // 10 bits per color, so R: bits 20-29
            int g = ((rgb >> 10) & 0x3FF);                     // G: bits 10-19
            int b =          rgb & 0x3FF;                      // B: bits  0-9
    
            analogWrite(LED_RED,   r);                         // write it to the LED output pins
            analogWrite(LED_GREEN, g);
            analogWrite(LED_BLUE,  b);
          } else if (payload[0] == 'R') {                      // the browser sends an R when the rainbow effect is enabled
            rainbow = true;
          } else if (payload[0] == 'N') {                      // the browser sends an N when the rainbow effect is disabled
            rainbow = false;
          }
          break;
      }
    }
    Again, most of the code is adapted from the previous examples, only the WebSocket part is new.
     
    There are different types of WebSocket messages, but we're only interested in the text type, because the JavaScript code at the client side sends the color data in text format, as a hexadecimal number, starting with a '#' sign.
    Each color is a 10-bit number, so in total, it gives us a 30-bit number for the RGB value.
     
    When the rainbow function is enabled, JavaScript sends an 'R' character, and when it's disabled, it sends a 'N' character. 
     
    Let's take a look at the HTML and JavaScript code as well:

    HTML

    <!DOCTYPE html>
    <html>
    <head>
      <title>LED Control</title>
      <link href='https://fonts.googleapis.com/css?family=Roboto:300' rel='stylesheet' type='text/css'>
      <link href='main.css' rel='stylesheet' type='text/css'>
      <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
      <link rel="icon" type="image/png" sizes="144x144"  href="/favicon-144x144.png">
      <link rel="icon" type="image/png" sizes="48x48" href="/favicon.ico">
      <link rel="manifest" href="/manifest.json">
      <meta name="theme-color" content="#00878f">
      <meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
      <script src="/WebSocket.js" type="text/javascript"></script>
    </head>
    
    <body>
      <center>
        <header>
          <h1>LED Control</h1>
        </header>
        <div>
          <table>
            <tr>
              <td style="width:14.4px; text-align: right">R: </td>
              <td><input class="enabled" id="r" type="range" min="0" max="1023" step="1" oninput="sendRGB();" value="0"></td>
            </tr>
            <tr>
              <td style="width:14.4px; text-align: right">G: </td>
              <td><input class="enabled" id="g" type="range" min="0" max="1023" step="1" oninput="sendRGB();" value="0"></td>
            </tr>
            <tr>
              <td style="width:14.4px; text-align: right">B: </td>
              <td><input class="enabled" id="b" type="range" min="0" max="1023" step="1" oninput="sendRGB();" value="0"></td>
            </tr>
          </table>
          <p style="margin:8px 0px">
            <button id="rainbow" class="button" style="background-color:#999" onclick="rainbowEffect();">Rainbow</button>
          </p>
        </div>
      </center>
    </body>
    </html>

    There's really not much to it, just 3 sliders and a button linked to JavaScript functions.

    JavaScript

    var rainbowEnable = false;
    var connection = new WebSocket('ws://' + location.hostname + ':81/', ['arduino']);
    connection.onopen = function () {
      connection.send('Connect ' + new Date());
    };
    connection.onerror = function (error) {
      console.log('WebSocket Error ', error);
    };
    connection.onmessage = function (e) {
      console.log('Server: ', e.data);
    };
    connection.onclose = function () {
      console.log('WebSocket connection closed');
    };
    
    function sendRGB () {
      var r = document.getElementById('r').value** 2 / 1023;
      var g = document.getElementById('g').value** 2 / 1023;
      var b = document.getElementById('b').value** 2 / 1023;
    
      var rgb = r << 20 | g << 10 | b;
      var rgbstr = '#' + rgb.toString(16);
      console.log('RGB: ' + rgbstr);
      connection.send(rgbstr);
    }
    
    function rainbowEffect () {
      rainbowEnable = ! rainbowEnable;
      if (rainbowEnable) {
        connection.send("R");
        document.getElementById('rainbow').style.backgroundColor = '#00878F';
        document.getElementById('r').className = 'disabled';
        document.getElementById('g').className = 'disabled';
        document.getElementById('b').className = 'disabled';
        document.getElementById('r').disabled = true;
        document.getElementById('g').disabled = true;
        document.getElementById('b').disabled = true;
      } else {
        connection.send("N");
        document.getElementById('rainbow').style.backgroundColor = '#999';
        document.getElementById('r').className = 'enabled';
        document.getElementById('g').className = 'enabled';
        document.getElementById('b').className = 'enabled';
        document.getElementById('r').disabled = false;
        document.getElementById('g').disabled = false;
        document.getElementById('b').disabled = false;
        sendRGB();
      }
    }
    We just create a WebSocket connection object to send data to the ESP. 
    Then every time a slider is moved, we take the values of the three sliders and we square the color values to get a smoother and more natural curve. We then combine them into a 30-bit number (10 bits per color). Finally, the RGB value gets converted to a hexadecimal string, a '#' is added, and it's sent to the ESP.
    When the rainbow button is pressed, the sliders are disabled, and an 'R' is sent to the ESP. When the rainbow button is pressed again, the sliders are enabled, and an 'N' is sent.

    Helper functions

    Back to the ESP8266 Arduino code again. We need some other functions as well, to convert bytes to KB and MB, to determine file types based on file extensions and to convert a hue angle to RGB values.
    String formatBytes(size_t bytes) { // convert sizes in bytes to KB and MB
      if (bytes < 1024) {
        return String(bytes) + "B";
      } else if (bytes < (1024 * 1024)) {
        return String(bytes / 1024.0) + "KB";
      } else if (bytes < (1024 * 1024 * 1024)) {
        return String(bytes / 1024.0 / 1024.0) + "MB";
      }
    }
    
    String getContentType(String filename) { // determine the filetype of a given filename, based on the extension
      if (filename.endsWith(".html")) return "text/html";
      else if (filename.endsWith(".css")) return "text/css";
      else if (filename.endsWith(".js")) return "application/javascript";
      else if (filename.endsWith(".ico")) return "image/x-icon";
      else if (filename.endsWith(".gz")) return "application/x-gzip";
      return "text/plain";
    }
    
    void setHue(int hue) { // Set the RGB LED to a given hue (color) (0° = Red, 120° = Green, 240° = Blue)
      hue %= 360;                   // hue is an angle between 0 and 359°
      float radH = hue*3.142/180;   // Convert degrees to radians
      float rf, gf, bf;
      
      if(hue>=0 && hue<120){        // Convert from HSI color space to RGB              
        rf = cos(radH*3/4);
        gf = sin(radH*3/4);
        bf = 0;
      } else if(hue>=120 && hue<240){
        radH -= 2.09439;
        gf = cos(radH*3/4);
        bf = sin(radH*3/4);
        rf = 0;
      } else if(hue>=240 && hue<360){
        radH -= 4.188787;
        bf = cos(radH*3/4);
        rf = sin(radH*3/4);
        gf = 0;
      }
      int r = rf*rf*1023;
      int g = gf*gf*1023;
      int b = bf*bf*1023;
      
      analogWrite(LED_RED,   r);    // Write the right color to the LED output pins
      analogWrite(LED_GREEN, g);
      analogWrite(LED_BLUE,  b);
    }
    To convert from hue to RGB, we use sines and cosines, because the sum of their squares is always one, so the total intensity will always be more or less the same.
     
    This results in the following RGB curves:
    RGB_rainbow.png
     

    Using the example

    Download the example from GitHub and open it in the Arduino IDE. Then add your Wi-Fi credentials (lines 83-85).
    Connect an RGB LED with red to pin 15, green to pin 12 and blue to pin 13. Don't forget the current limiting resistors!
    Select the SPIFFS size (64KB should be enough, but if you want to upload more files later, you should set it higher). Upload the sketch over Serial, and then upload the SPIFFS files using Tools > ESP8266 Sketch Data Upload.
     
    Wait for it to connect to a Wi-Fi network, or connect to the ESP8266 Access Point using the password 'thereisnospoon', and go to http://esp8266.local. You should get a page that looks like this:
     

    LED_Control.png

    Use the sliders to adjust the color levels of the LED, and press the Rainbow button to enable the rainbow effect.
     
    If you go to http://esp8266.local/edit.html, you can upload or update files:

    Edit.png

  • ESP8266 Wi-Fi

    Using the ESP8266 as a simple microcontroller is great, but the reason why most people use it, is its Wi-Fi capabilities. In this chapter, we'll dive into the wonderful world of network protocols, like Wi-Fi, TCP, UDP, HTTP, DNS... All these acronyms might intimidate you, but I'll try my best to explain them step-by-step and in an easy way.
     
    Some paragraphs are in italic. These provide some extra information, but are not critical to understanding the ESP's Wi-Fi functions, so don't get frustrated if there are things you don't understand.
     
    It's really hard to give a clear explanation, without over-complicating things and while keeping it short enough as well. If you've got any feedback or remarks, be sure to leave a comment to help improve this article. Thanks!

    The TCP/IP stack

    The system most people refer to as 'The Internet' isn't just one protocol: it's an entire stack of layers of protocols, often referred to as the TCP/IP stack. We'll go over these different layers, because we need to understand how our ESP8266 communicates with other devices on the network.
     
    LayerProtocols
    Application HTTP, FTP, mDNS, WebSocket, OSC...
    Transport TCP, UDP
    Internet IP
    Link Ethernet, Wi-Fi...

    The Link layer

    The link layer contains the physical link between two devices, an Ethernet cable, for example, or a Wi-Fi connection. This is the layer that is closest to the hardware.
    To connect an ESP8266 to the network, you have to create a Wi-Fi link. This can happen in two different ways:
    1. The ESP8266 connects to a wireless access point (WAP or simply AP). The AP can be built-in to your modem or router, for example. 
      In this configuration, the ESP acts like a wireless station. 
    2. The ESP8266 acts as an access point and wireless stations can connect to it. These stations could be your laptop, a smartphone, or even another ESP in station mode.
    Once the Wi-Fi link is established, the ESP8266 is part of a local area network (LAN). All devices on a LAN can communicate with each other.
    Most of the time, the AP is connected to a physical Ethernet network as well, this means that the ESP8266 can also communicate with devices that are connected to the AP (modem/router) via a wired Ethernet connection (desktop computers, gaming consoles and set-top boxes, for instance).
    If the ESP8266 is in station mode, it can communicate with any station that is connected to it, and two stations (e.g. a laptop and a smartphone) can also communicate with each other.
     
    The ESP can be used in AP-only, station-only, or AP+station mode.
     

    TL;DR

    The link layer is the physical link between devices: in the case of the ESP8266, this is a WiFi connection. The ESP can act as a station and connect to an access point, or act as an access point and let other devices connect to it.
     

    The Internet or Network layer

    Although the devices are now physically connected (either through actual wires (Ethernet) or through radio waves (Wi-Fi)), they can't actually talk to each other yet, because they have no way of knowing where to send the message to. 
    That's where the Internet Protocol (IP) comes in. Every device on the network has a personal IP address. The DHCP server (Dynamic Host Configuration Protocol Server) makes sure that these addresses are unique. 
    This means that you can now send a message to a specific address.
     
    There are two versions of the Internet Protocol: IPv4 and IPv6. IPv6 is an improved version of IPv4 and has much more addresses than IPv4 (because there are much more devices than available IPv4 addresses). In this article, we'll only talk about IPv4 addresses, since most LANs still use them.
     
    The IP address consists of 4 numbers, for example 192.168.1.5 is a valid IPv4 address. It actually consists of two parts: the first part is 192.168.1, this is the address of the local network. The last digit, 5 in this case, is specific to the device. 
     
    By using IP addresses, we can find the ESP8266 on the network, and send messages to it. The ESP can also find our computer or our phone, if it knows their respective IP addresses.
     

    Sub-net mask (optional)

    This subdivision of the IP address is determined by the sub-net mask, often written as 255.255.255.0. You can see that it consists of four numbers, just like the IP address. If a part of the sub-net mask is 255, it means that the corresponding part of the IP address is part of the network address, if it's 0, the corresponding IP part is part of the address of the specific address. A different notation to "IP: 192.168.1.5, sub-net mask: 255.255.255.0" would be "192.168.1.5/24", because the binary representation of the sub-net mask is 11111111.11111111.11111111.00000000, and it has 24 ones.
    If you want to know more about sub-nets, I'd recommend you to read the Wikipedia article
    (A quick tip to help you remember: it's called the sub-net mask, because if you perform a bitwise AND operation on the IP address and the sub-net mask (i.e. use the sub-net mask as a mask for the IP address), you get the address of the sub-net.)
     

    MAC addresses and ARP (optional)

    It is actually impossible to send packets directly to another machine using only the IP address. To send a packet to a specific device on the LAN (Wi-Fi or Ethernet), you have to know its MAC-address. The MAC address is a unique number that is unique for every network device, and it never changes, it's hardwired in the network chip. This means that every ESP8266, every network card, every smartphone... ever made, has a different MAC address. 
    So before the ESP can send a packet to your smartphone for example, it has to know its MAC address. It doesn't know this yet, the ESP only knows the IP address of the smartphone, say 192.168.1.6. To do this, the ESP sends a broadcast message (i.e. a message addressed to all devices on the LAN) saying "I'm looking for the MAC address of the device with the IP address 192.168.1.6". The ESP also includes its own IP and MAC address with the message. When the smartphone receives this broadcast message, it recognizes its own IP address, and responds to the ESP by sending its own MAC address. Now the ESP and the phone both know each other's IP and MAC addresses, and they can communicate using IP addresses. This method is called the Addres Resolution Protocol, or ARP.
     

    What about the Internet?

    As you might have noticed, I only talked about the local area network, these are the computers in your own house. So how can the ESP8266 communicate with the Internet, you may ask? Well, there's a lot of network infrastructure involved in 'The Internet', and they all obey the IP rules, to make sure most of your packets arrive at there destination. It's not that simple of course, there's a lot of things going on, like routing and Network Address Translation (NAT), but that falls outside the scope of this article, and it's not really something most people have to worry about.
     

    TL;DR

    The Internet layer uses IP addresses in order to know where it should send the data. This means that two devices can now send packets of data to each other, even over the Internet.
     

    The Transport layer

    The different devices in the network do their best to deliver these IP packets to the addressee, however, it's not uncommon for a packet to get lost, so it will never arrive. Or the packet might get corrupted on the way: the data is no longer correct. IP also can't guarantee that the packets arrive in the same order they were sent in.
    This means that we can't reliably send messages yet by only using the link and the Internet layer, since we can never know when and whether a packet will arrive, or know for certain that a received packet is correct.
    We need a third layer on top of the Internet layer: the Transport layer.
     
    There are mainly two protocols that make up this third layer: the Transmission Control Protocol (TCP) and the User Datagram Protocol (UDP).
    • TCP makes sure that all packets are received, that the packets are in order, and that corrupted packets are re-sent. This means that it can be used for communication between multiple applications, without having to worry about data integrity or packet loss. This is why it's used for things like downloading webpages, sending email, uploading files etc.
    • UDP on the other hand, doesn't guarantee that every packet reaches its destination, it does check for errors however, but when it finds one, it just destroys the packet, without re-sending it. This means that it's not as reliable as TCP, but it's faster, and has a much lower latency, because it doesn't require an open connection to send messages, like TCP does. That's why it's used in voice and video chats, and for example in online games.
    If you want to know more about the differences between TCP and UDP, check out this video.

    TL;DR

    The IP protocol is not reliable, and has no error checking. TCP solves this by re-sending lost or corrupt packages, and orders packets that are received in the wrong order. UDP also checks for corrupt packages, but doesn't re-send them, so it has less latency than TCP.
     

    The Application layer

    We now have reliable communication using TCP, but there's still one problem. Think of it this way: you are sending a letter, and TCP guarantees that it will arrive at its destination, but if the receiver doesn't understand the language it's written in, he won't know what to do with it.
    In other words, we need a fourth layer of protocols, for two programs to be able to communicate with each other.
    There's lots of different protocols out there, but we'll mostly focus on the protocols for web servers and browsers.

    HyperText Transfer Protocol 

    The HyperText Transfer Protocol, or HTTP, is the protocol (cfr. language) that is used by both web servers and web clients in order to communicate. It uses text to perform send requests and responses from the client to the server and back again.
    For example, when you type http://www.google.com into the address bar of a web browser (client), it will send an HTTP GET request to the Google web server. The server understands this HTTP request, and will send the Google webpage as a response. Or when you upload an image to Instagram, your browser sends an HTTP POST request with your selfie attached to the Instagram server. The server understands the request, saves the image and adds it into the database, sends the URL of the new image back to your browser, and the browser will add the image on the webpage.
    As you can see, neither the client nor the server has to worry about the integrity of the messages they send, and they know that the recipient understands their language, and that it will know what to do with a certain HTTP request. 
    Most modern sites use a secure version of HTTP, called HTTPS. This secure connection encrypts the data, for security reasons. (You don't want anyone reading the packets from your mail server, or the packets you sent to your bank, for instance.)

    WebSocket

    HTTP is great for things like downloading webpages, uploading photos etc. but it's quite slow: every time you send an HTTP request, you have to start a new TCP connection to the server, then send your request, wait for the server to respond, and download the response. Wouldn't it be great if we didn't have to open a new connection every time we want to send some data, and if we could send and receive data at the same time at any moment we'd like? That's where WebSocket comes to the rescue: you can keep the TCP connection with the server open at all times, you get perfect TCP reliability, and it's pretty fast.

    Open Sound Control

    HTTP and WebSocket both use TCP connections. What if I want lower latency? Well, Open Sound Control, or OSC, uses UDP to send small pieces of data, like ints, floats, short text etc... with very low latency. It was originally designed for controlling low latency audio applications, but it's a very flexible protocol, so it's often used for low-latency tasks other than audio control.

    Domain Name System

    As mentioned before, you can only send a message to another computer if you know its IP address. But when you browse the Internet, you only know a website's domain name (e.g. www.google.com). Your computer uses the Domain Name System to translate this domain name to the right IP address. More on this later.
     

    Sources

  • HomeControl

  • How to use FreeRTOS with Arduino – Real time operating system

    In this article you will learn how to use FreeRTOS operating system with Arduino to perform specific tasks. A Real Time Operating System also known as RTOS is an operating system which is intended to fulfills the requirement of real time application. It is able to process data as comes in, typically without buffering delays. RTOS is the combination of calling predefined functions. The key factors in Real Time Operating System are minimum interrupt latency and minimum threads switching latency. The Real Time Operating System is valued more for how quickly and how predictably it responds to complete the tasks in given period of time.

    There are three types of RTOS:

    • Hard RTOS; bound to complete task in given deadline
    • Firm RTOS; bound of deadline but if they miss the deadline it is acceptable but not in the case of Hard RTOS.
    • Soft RTOS; not bound of any deadline.

    Examples of RTOS:

    • LynxOS
    • RTLinux
    • VxWorks
    • FreeRTOS
    • OSE
    • QNX
    • Windows CE

    Why RTOS are required:

     

    When we write good embedded software we do not need RTOS but when its complexity and size increases RTOS is always beneficial for the reasons listed below:

    • Abstract out timing information
    • Maintainability/Extensibility
    • Modularity
    • Cleaner interfaces
    • Easier testing (in some cases)
    • Code reuse
    • Improved efficiency
    • Idle time
    • Flexible interrupt handling
    • Mixed processing requirements
    • Easier control over peripherals

    These are the advantages of RTOS but there are also some disadvantages listed as below;

    • Low Priority Tasks
    • Precision of code
    • Limited Tasks
    • Complex Algorithms
    • Device driver and interrupt signals
    • Thread Priority
    • Expensive
    • Not easy to program

    The OS used in this project is FreeRTOS. FreeRTOS is developed by Real Time Engineers Ltd. FreeRTOS is a popular Real Time Operating System kernel. FreeRTOS is open source. It is used for embedded devices which are ported in described 35 microcontrollers. It is mostly written in C but some functions are written in assembly. There are also SafeRTOS and OpenRTOS available online which are similar to FreeRTOS.

    Task switching using Arduino:

    In this project 3 Led indicates three tasks and one Led indicate idle state. Three tasks are labels as Task1, Task2 and Task3 respectively.

    Code:

    We in includes the library file on FreeRTOS

    #include <Arduino_FreeRTOS.h>
    
    void setup()
    
    //Initialize the Serial Monitor with 9600 baud rate
    
    {Serial.begin(9600);
    
    Serial.println(F("In Setup function"));
    
    //Set the digital pins 8 to 11 as output
    
      pinMode(8,OUTPUT);
    
      pinMode(9,OUTPUT);
    
      pinMode(10,OUTPUT);
    
      pinMode(11,OUTPUT);
    
    //Create three tasks with labels Task1, Task2 and Task3 and assign the priority as 1, 2 and 3 //respectively. We also create fourth task labeled as IdelTask when the there is no task in //operation and it has the highest priority.
    
      xTaskCreate(MyTask1, "Task1", 100, NULL, 1, NULL);
    
      xTaskCreate(MyTask2, "Task2", 100, NULL, 2, NULL);
    
      xTaskCreate(MyTask3, "Task3", 100, NULL, 3, NULL);
    
      xTaskCreate(MyIdleTask, "IdleTask", 100, NULL, 0, NULL);}
    
    //We can change the priority of task according to our desire by changing the numeric’s //between NULL texts.
    
    void loop()
    
    {
    
    //There is no instruction in loop section of the code.
    
    }
    
    //The following function is Task1. We display the task label on Serial monitor.
    
    static void MyTask1(void* pvParameters)
    
    {while(1)
    
      { digitalWrite(8,HIGH);
    
        digitalWrite(9,LOW); 
    
        digitalWrite(10,LOW);
    
        digitalWrite(11,LOW); 
    
        Serial.println(F("Task1"));
    
        vTaskDelay(100/portTICK_PERIOD_MS);}}
    
    //Similarly this is task 2
    
    static void MyTask2(void* pvParameters)
    
    {  while(1)
    
      { digitalWrite(8,LOW);
    
        digitalWrite(9,HIGH); 
    
        digitalWrite(10,LOW);
    
        digitalWrite(11,LOW);   
    
        Serial.println(F("Task2"));
    
        vTaskDelay(110/portTICK_PERIOD_MS);}}
    
    //Similarly this is task 3
    
    static void MyTask3(void* pvParameters)
    
    { while(1)
    
      { digitalWrite(8,LOW);
    
        digitalWrite(9,LOW); 
    
        digitalWrite(10,HIGH);
    
        digitalWrite(11,LOW);
    
        Serial.println(F("Task3"));
    
        vTaskDelay(120/portTICK_PERIOD_MS);}}
    
    //This is the idle task which has the most higher priority and calls when no task is running.
    
    static void MyIdleTask(void* pvParameters)
    
    {while(1)
    
      { digitalWrite(8,LOW);
    
        digitalWrite(9,LOW); 
    
        digitalWrite(10,LOW);
    
        digitalWrite(11,HIGH);
    
        Serial.println(F("Idle state"));
    
        delay(50);}}

    Each task has its unique priority and different running duration.

  • multi function shield examples

    This shield got my attention as it looked like a nice beginners learning type shield with which you could get up and running with an Arduino

    Here is a picture of the board, a few code examples are available later on in the article.

    multi function shieldhttp://arduinolearning.com/wp-content/uploads/2014/12/multi-function-shield-300x290.jpg 300w, http://arduinolearning.com/wp-content/uploads/2014/12/multi-function-shield-150x145.jpg 150w, http://arduinolearning.com/wp-content/uploads/2014/12/multi-function-shield-400x387.jpg 400w" sizes="(max-width: 1024px) 100vw, 1024px" style="border: 0px none !important; margin: 5px 0px; padding: 0px !important; max-width: 96%; width: auto; height: auto;">

    multi function shield

    Features

    4 digit 7-segment LED display module driven by two serial 74HC595’s
    4 LED’s
    10K potentiometer
    3 x push buttons
    Piezo buzzer
    DS18B20 temperature sensor interface (not included)
    Infrared receiver interface
    Serial interface header for connection to serial modules

    Code Examples

    ********************************************************************

    Blinking LED

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int led = 13;
    void setup()
    {
    // initialize the digital pin as an output.
    pinMode(led, OUTPUT);
    }
    void loop()
    {
    digitalWrite(led, HIGH);
    delay(1000);
    digitalWrite(led, LOW);
    delay(1000);
    }

    ********************************************************************

    All LEDS blinking

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    int led1 = 13;
    int led2 = 12;
    int led3 = 11;
    int led4 = 10;
    void setup()
    {
    // initialize the digital pin as an output.
    pinMode(led1, OUTPUT);
    pinMode(led2, OUTPUT);
    pinMode(led3, OUTPUT);
    pinMode(led4, OUTPUT);
    }
    void loop()
    {
    digitalWrite(led1, HIGH);
    digitalWrite(led2, HIGH);
    digitalWrite(led3, HIGH);
    digitalWrite(led4, HIGH);
    delay(1000);
    digitalWrite(led1, LOW);
    digitalWrite(led2, LOW);
    digitalWrite(led3, LOW);
    digitalWrite(led4, LOW);
    delay(1000);
    }

    ********************************************************************

    Switches example

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    const byte LED[] = {13,12,11,10};
    #define BUTTON1 A1
    #define BUTTON2 A2
    void setup()
    {
    // initialize the digital pin as an output.
    /* Set each pin to outputs */
    pinMode(LED[0], OUTPUT);
    pinMode(LED[1], OUTPUT);
    pinMode(LED[2], OUTPUT);
    pinMode(LED[3], OUTPUT);
    }
    void loop()
    {
    if(!digitalRead(BUTTON1))
    {
    digitalWrite(LED[0], HIGH);
    digitalWrite(LED[1], HIGH);
    digitalWrite(LED[2], HIGH);
    digitalWrite(LED[3], HIGH);
    }
    if(!digitalRead(BUTTON2))
    {
    digitalWrite(LED[0], LOW);
    digitalWrite(LED[1], LOW);
    digitalWrite(LED[2], LOW);
    digitalWrite(LED[3], LOW);
    }
    }

    ********************************************************************

    Potentiometer 1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #define Pot1 0
    void setup()
    {
    Serial.begin(9600);
    }
    /* Main Program */
    void loop()
    {
    Serial.print(“Potentiometer reading: “);
    Serial.println(analogRead(Pot1));
    /* Wait 0.5 seconds before reading again */
    delay(500);
    }

    ********************************************************************

    Pot and led

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    const byte LED[] = {13,12,11,10};
    #define Pot1 0
    void setup()
    {
    Serial.begin(9600);
    // initialize the digital pin as an output.
    /* Set each pin to outputs */
    pinMode(LED[0], OUTPUT);
    pinMode(LED[1], OUTPUT);
    pinMode(LED[2], OUTPUT);
    pinMode(LED[3], OUTPUT);
    }
    /* Main Program */
    void loop()
    {
    int PotValue;
    //Serial.print(“Potentiometer reading: “);
    PotValue = analogRead(Pot1);
    /* Wait 0.5 seconds before reading again */
    if(PotValue < 400)
    {
    digitalWrite(LED[0], LOW);
    digitalWrite(LED[1], LOW);
    digitalWrite(LED[2], LOW);
    digitalWrite(LED[3], LOW);
    Serial.print(“Potentiometer: “);
    Serial.println(PotValue);
    }
    else
    {
    digitalWrite(LED[0], HIGH);
    digitalWrite(LED[1], HIGH);
    digitalWrite(LED[2], HIGH);
    digitalWrite(LED[3], HIGH);
    Serial.print(“Potentiometer: “);
    Serial.println(PotValue);
    }
    delay(500);
    }

    ********************************************************************

    segment display

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /* Define shift register pins used for seven segment display */
    #define LATCH_DIO 4
    #define CLK_DIO 7
    #define DATA_DIO 8
    /* Segment byte maps for numbers 0 to 9 */
    const byte SEGMENT_MAP[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90};
    /* Byte maps to select digit 1 to 4 */
    const byte SEGMENT_SELECT[] = {0xF1,0xF2,0xF4,0xF8};
    void setup ()
    {
    /* Set DIO pins to outputs */
    pinMode(LATCH_DIO,OUTPUT);
    pinMode(CLK_DIO,OUTPUT);
    pinMode(DATA_DIO,OUTPUT);
    }
    /* Main program */
    void loop()
    {
    /* Update the display with the current counter value */
    WriteNumberToSegment(0 , 0);
    WriteNumberToSegment(1 , 1);
    WriteNumberToSegment(2 , 2);
    WriteNumberToSegment(3 , 3);
    }
    /* Write a decimal number between 0 and 9 to one of the 4 digits of the display */
    void WriteNumberToSegment(byte Segment, byte Value)
    {
    digitalWrite(LATCH_DIO,LOW);
    shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, SEGMENT_MAP[Value]);
    shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, SEGMENT_SELECT[Segment] );
    digitalWrite(LATCH_DIO,HIGH);
    }

    ********************************************************************

    Read pot and display value on display

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    /* Define shift register pins used for seven segment display */
    #define LATCH_DIO 4
    #define CLK_DIO 7
    #define DATA_DIO 8
    #define Pot1 0
    /* Segment byte maps for numbers 0 to 9 */
    const byte SEGMENT_MAP[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90};
    /* Byte maps to select digit 1 to 4 */
    const byte SEGMENT_SELECT[] = {0xF1,0xF2,0xF4,0xF8};
    void setup ()
    {
    Serial.begin(9600);
    /* Set DIO pins to outputs */
    pinMode(LATCH_DIO,OUTPUT);
    pinMode(CLK_DIO,OUTPUT);
    pinMode(DATA_DIO,OUTPUT);
    }
    /* Main program */
    void loop()
    {
    int PotValue;
    PotValue = analogRead(Pot1);
    Serial.print(“Potentiometer: “);
    Serial.println(PotValue);
    /* Update the display with the current counter value */
    WriteNumberToSegment(0 , PotValue / 1000);
    WriteNumberToSegment(1 , (PotValue / 100) % 10);
    WriteNumberToSegment(2 , (PotValue / 10) % 10);
    WriteNumberToSegment(3 , PotValue % 10);
    }
    /* Write a decimal number between 0 and 9 to one of the 4 digits of the display */
    void WriteNumberToSegment(byte Segment, byte Value)
    {
    digitalWrite(LATCH_DIO,LOW);
    shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, SEGMENT_MAP[Value]);
    shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, SEGMENT_SELECT[Segment] );
    digitalWrite(LATCH_DIO,HIGH);
    }

    ********************************************************************

    Resources

    Multifunctional Expansion Board Shield Kit

  • Setup Home Control V01.02 from scratch

    In this article we will build Home Control System step by step:

     

     

  • Step 1, What HW do you need?

    Step 1, What HW do you need?

    shopping list:

    Setup Home Control V01.02 from scratch Step 1, What HW do you need? Step 2, What SW do you need?
  • Step 2, What SW do you need?

    Step 2, What SW do you need?

    Download, installation list:

    • BvSshClient
    • Advanced_IP_Scanner
    • npp.7.5.6.Installer
    • mqttfx-1.7.0-windows-x64

    Step 1, What HW do you need? Step 2, What SW do you need? Step 3, Puzzle all together
  • Step 3, Puzzle all together

    Step 3, Puzzle all together

    Step 2, What SW do you need? Step 3, Puzzle all together Step 4, Make it work
  • Step 4, Make it work

    Step 4, Make it work

    • Flash "Home Control V01.02" into Arduino mega 2650
    • Program Settings file. (incl. IP address for htttp server and event settings.
      C:\Taillieu.Info\Data\LabView\WebCom2015\SettingFiles\HomeControl HomeControl_354_Default.txt
    Step 3, Puzzle all together Step 4, Make it work Step 5, enjoy
  • Step 5, enjoy

    Step 5, enjoy

     

    Step 4, Make it work Step 5, enjoy Setup Home Control V01.02 from scratch
  • The ESP8266 as a microcontroller - Hardware

    While the ESP8266 is often used as a ‘dumb’ Serial-to-WiFi bridge, it’s a very powerful microcontroller on its own. In this chapter, we’ll look at the non-Wi-Fi specific functions of the ESP8266.

    Digital I/O

    Just like a normal Arduino, the ESP8266 has digital input/output pins (I/O or GPIO, General Purpose Input/Output pins). As the name implies, they can be used as digital inputs to read a digital voltage, or as digital outputs to output either 0V (sink current) or 3.3V (source current).

    Voltage and current restrictions

    The ESP8266 is a 3.3V microcontroller, so its I/O operates at 3.3V as well. The pins are not 5V tolerant, applying more than 3.6V on any pin will kill the chip.

    The maximum current that can be drawn from a single GPIO pin is 12mA.

    Usable pins

    The ESP8266 has 17 GPIO pins (0-16), however, you can only use 11 of them, because 6 pins (GPIO 6 - 11) are used to connect the flash memory chip. This is the small 8-legged chip right next to the ESP8266. If you try to use one of these pins, you might crash your program.

    GPIO 1 and 3 are used as TX and RX of the hardware Serial port (UART), so in most cases, you can’t use them as normal I/O while sending/receiving serial data.

    Boot modes

    As mentioned in the previous chapter, some I/O pins have a special function during boot: They select 1 of 3 boot modes:

    GPIO15GPIO0GPIO2Mode
    0V 0V 3.3V Uart Bootloader
    0V 3.3V 3.3V Boot sketch (SPI flash)
    3.3V x x SDIO mode (not used for Arduino)

    Note: you don’t have to add an external pull-up resistor to GPIO2, the internal one is enabled at boot.

    We made sure that these conditions are met by adding external resistors in the previous chapter, or the board manufacturer of your board added them for you. This has some implications, however:

    • GPIO15 is always pulled low, so you can’t use the internal pull-up resistor. You have to keep this in mind when using GPIO15 as an input to read a switch or connect it to a device with an open-collector (or open-drain) output, like I²C.
    • GPIO0 is pulled high during normal operation, so you can’t use it as a Hi-Z input.
    • GPIO2 can’t be low at boot, so you can’t connect a switch to it.

    Internal pull-up/-down resistors

    GPIO 0-15 all have a built-in pull-up resistor, just like in an Arduino. GPIO16 has a built-in pull-down resistor.

    PWM

    Unlike most Atmel chips (Arduino), the ESP8266 doesn’t support hardware PWM, however, software PWM is supported on all digital pins. The default PWM range is 10-bits @ 1kHz, but this can be changed (up to >14-bit@1kHz).

    Analog input

    The ESP8266 has a single analog input, with an input range of 0 - 1.0V. If you supply 3.3V, for example, you will damage the chip. Some boards like the NodeMCU have an on-board resistive voltage divider, to get an easier 0 - 3.3V range. You could also just use a trimpot as a voltage divider.

    The ADC (analog to digital converter) has a resolution of 10 bits.

    Communication

    Serial

    The ESP8266 has two hardware UARTS (Serial ports):
    UART0 on pins 1 and 3 (TX0 and RX0 resp.), and UART1 on pins 2 and 8 (TX1 and RX1 resp.), however, GPIO8 is used to connect the flash chip. This means that UART1 can only transmit data.

    UART0 also has hardware flow control on pins 15 and 13 (RTS0 and CTS0 resp.). These two pins can also be used as alternative TX0 and RX0 pins.

    I²C

    The ESP doesn’t have a hardware TWI (Two Wire Interface), but it is implemented in software. This means that you can use pretty much any two digital pins. By default, the I²C library uses pin 4 as SDA and pin 5 as SCL. (The data sheet specifies GPIO2 as SDA and GPIO14 as SCL.) The maximum speed is approximately 450kHz.

    SPI

    The ESP8266 has one SPI connection available to the user, referred to as HSPI. It uses GPIO14 as CLK, 12 as MISO, 13 as MOSI and 15 as Slave Select (SS). It can be used in both Slave and Master mode (in software).

    GPIO overview

    GPIOFunctionStateRestrictions
    0 Boot mode select 3.3V No Hi-Z
    1 TX0 - Not usable during Serial transmission
    2 Boot mode select
    TX1
    3.3V (boot only) Don’t connect to ground at boot time 
    Sends debug data at boot time
    3 RX0 - Not usable during Serial transmission
    4 SDA (I²C) - -
    5 SCL (I²C) - -
    6 - 11 Flash connection x Not usable, and not broken out
    12 MISO (SPI) - -
    13 MOSI (SPI) - -
    14 SCK (SPI) - -
    15 SS (SPI) 0V Pull-up resistor not usable
    16 Wake up from sleep - No pull-up resistor, but pull-down instead 
    Should be connected to RST to wake up

    The ESP8266 as a microcontroller - Software

    Most of the microcontroller functionality of the ESP uses exactly the same syntax as a normal Arduino, making it really easy to get started.

    Digital I/O

    Just like with a regular Arduino, you can set the function of a pin using pinMode(pin, mode); where pin is the GPIO number*, and mode can be either INPUT, which is the default, OUTPUT, or INPUT_PULLUP to enable the built-in pull-up resistors for GPIO 0-15. To enable the pull-down resistor for GPIO16, you have to use INPUT_PULLDOWN_16.

    (*) NodeMCU uses a different pin mapping, read more here. To address a NodeMCU pin, e.g. pin 5, use D5: for instance: pinMode(D5, OUTPUT);

    To set an output pin high (3.3V) or low (0V), use digitalWrite(pin, value); where pin is the digital pin, and value either 1 or 0 (or HIGH and LOW).

    To read an input, use digitalRead(pin);

    To enable PWM on a certain pin, use analogWrite(pin, value); where pin is the digital pin, and value a number between 0 and 1023.

    You can change the range (bit depth) of the PWM output by using analogWriteRange(new_range);

    The frequency can be changed by using analogWriteFreq(new_frequency);new_frequency should be between 100 and 1000Hz.

    Analog input

    Just like on an Arduino, you can use analogRead(A0) to get the analog voltage on the analog input. (0 = 0V, 1023 = 1.0V).

    The ESP can also use the ADC to measure the supply voltage (VCC). To do this, include ADC_MODE(ADC_VCC); at the top of your sketch, and use ESP.getVcc(); to actually get the voltage.
    If you use it to read the supply voltage, you can’t connect anything else to the analog pin.

    Communication

    Serial communication

    To use UART0 (TX = GPIO1, RX = GPIO3), you can use the Serial object, just like on an Arduino: Serial.begin(baud).

    To use the alternative pins (TX = GPIO15, RX = GPIO13), use Serial.swap() after Serial.begin.

    To use UART1 (TX = GPIO2), use the Serial1 object.

    All Arduino Stream functions, like read, write, print, println,... are supported as well.

    I²C and SPI

    You can just use the default Arduino library syntax, like you normally would.

    Sharing CPU time with the RF part

    One thing to keep in mind while writing programs for the ESP8266 is that your sketch has to share resources (CPU time and memory) with the Wi-Fi- and TCP-stacks (the software that runs in the background and handles all Wi-Fi and IP connections). 
    If your code takes too long to execute, and don’t let the TCP stacks do their thing, it might crash, or you could lose data. It’s best to keep the execution time of you loop under a couple of hundreds of milliseconds.

    Every time the main loop is repeated, your sketch yields to the Wi-Fi and TCP to handle all Wi-Fi and TCP requests.

    If your loop takes longer than this, you will have to explicitly give CPU time to the Wi-Fi/TCP stacks, by using including delay(0); or yield();. If you don’t, network communication won’t work as expected, and if it’s longer than 3 seconds, the soft WDT (Watch Dog Timer) will reset the ESP. If the soft WDT is disabled, after a little over 8 seconds, the hardware WDT will reset the chip.

    From a microcontroller’s perspective however, 3 seconds is a very long time (240 million clockcycles), so unless you do some extremely heavy number crunching, or sending extremely long strings over Serial, you won’t be affected by this. Just keep in mind that you add the yield(); inside your for or while loops that could take longer than, say 100ms.

    Sources

    This is where I got most of my information to writ this article, there’s some more details on the GitHub pages, if you’re into some more advanced stuff, like EEPROM or deep sleep etc.

  • TM32F103CBT6 Maple Mini ARM STM32 Cortex-M3 Controller Board Module

    STM32F103CBT6 Maple Mini ARM STM32 Cortex-M3 Controller Board Module 3.3V USB Digital IO PWM Pins Port for Arduino

    • Processor: STM32F103CBT6, 32 ARM Cortex-M3 processor
    • Frequency: 72 MHz
    • 128 KB Flash and 20 KB SRAM
    • 34 digital io ports (GPIOs)
    • 12 16-bit precision PWM port
    • 9 12 precision analog input (ADC) port
    • 2 SPI channel
    • 2 I2C channel
    • 7-channel direct memory access (DMA) (dma.h)
    • 3 USART (serial) channel
    • An enhanced and three general purpose timers
    • Can be downloaded directly via the USB port and communications program
    • An external JTAG
    • Nested Vectored Interrupt Controller (NVIC) (including GPIOs on the external interrupt)
    • Support for low-power, sleep and standard mode (<500 A)
    • Operating Voltage: 3.3 V
    • Input Voltage (recommended): 3 V - 12 V
    • Size: 2.02 "× 0.72"

    Maple Mini has a total of 34 input / output pins, numbered from D0 to D33. Maple Mini label on the screen corresponds to the port and its adjacent. However, they have some of the default has a special purpose.
    D23 is the USB port D +, D24 is the USB D-. Such as their use as GPIO, then your program must first disable the USB port. But you know,disable USB serial bootloader means that priority will not work, you will need to pass Perpetual Bootloader for the next update.


    Port D32 is the Mini button port. Therefore, it is mainly used as an input port. When the key is pressed, the magnetic port will readhigh.


    Port D33 is the Mini LED port. Therefore, its main role is as an output port. When it was written with high, LED lights will be lit

  • Uploading sketches to the ESP8266

    The upload procedure for ESP8266 boards is a little different from the normal Arduino procedure. Most Arduinos will automatically reset when a new program is being uploaded, and will automatically enter programming mode.
    On some ESP boards you have to manually enter programming mode, and on the bare-bones modules, you even have to reset them manually.
    However, there are some simple circuits you can use to get automatic uploads.

    Auto-reset

    This only applies to boards without an on-board USB-to-Serial converter.
     
    If the USB-to-Serial converter you're using has a DTR flow control line, you can automate the reset signal. When sending data to the ESP, the DTR line goes low, and stays low for some time. To reset the ESP, we need a low pulse on the RST pin. The problem is that the DTR pin stays low. To solve this, we're going to build a crude edge detector circuit, using a capacitor. Take a look at the following schematic:
     
    reset_without_diode.png
    You might recognize that this is basically a low-cut filter. In normal conditions, DTR is high (3.3V), and the reset line is also high, because of the pull-up resistor R2. This means that the voltage across the capacitor is 0V. When DTR suddenly drops (to 0V), the voltage across the capacitor is still 0V, meaning that the reset line will be at 0V + 0V = 0V, and a reset is triggered.
     
    However, C1 immediately starts charging through R2, and reaches 3.3V. At this point, DTR is still at 0V, meaning that there's now 3.3V across the capacitor. When DTR rises again, the reset line will be at 3.3V + 3.3V = 6.6V, and then immediately starts to discharge through R2, finally reaching 3.3V again, with 0V across C1.
    This is a problem: 6.6V can damage the ESP, so we have to find a way to get rid of the positive peak.
     
    One glance at this MATLAB simulation shows us the problem even better:
     

    without_diode.png

    The blue signal is the voltage on the DTR pin, and the yellow signal is the voltage on the reset pin.
     
    The solution is to add a diode: while charging the capacitor, it shouldn't change anything, so it should be reverse biased (just a fancy way of saying that it's not conducting any current because the polarity is the other way around), and while the capacitor is discharging, it should discharge the capacitor "immediately".
    Here's what that looks like:
     
    reset_with_diode.png
    Let's run the simulation again to check if our problem is solved:
     
    with_diode.png
     
    As you can see, the 6.6V peak is now very narrow, just like we wanted. It's impossible to discharge the capacitor instantly, that would require a capacitor and a diode with 0Ω of series resistance, and an infinite current, which is impossible, obviously. There's also a smaller but relatively wide peak of approximately 3.9V. This is because a diode only conducts when the voltage across it is higher than ~600mV. This means that the last 0.6V that's left in the capacitor (from 3.9 to 3.3V) will still be discharged through R2 only. 
    Nevertheless, the voltage peak is much lower and narrower than without the diode, and it's safe to connect to the ESP8266.
     
    This exact circuit is also used in the Arduino Uno, for example.
     
    Note: if you followed the instructions in the hardware step correctly, you should already have added R2 to your ESP.

    How to use Auto-reset

    To use this auto-reset circuit, connect it to the DTR line of your USB-to-Serial converter, and to the reset line of the ESP, as shown in the diagram. Then click compile (just because the first compilation can take quite some time). Go to Tools > Reset and select 'ck'. When it's done compiling, hold down the program button we added in the hardware step, and click upload. Wait for it to say "Uploading..." and then release the program button.

    Auto-reset and Auto-program

    This only applies to boards without an on-board USB-to-Serial converter.
     
    The method above still requires you to press a button to upload a new sketch. If your USB-to-Serial converter has a RTS line as well as a DTR line, you can automate the entire process.
    auto-reset_auto-program.png
    You may find out that the 4.7kΩ resistor doesn't work for you. In that case, try some other value, like 10kΩ, for example.
     
    This method was first used in the NodeMCU, so go to Tools > Reset Method, and select "nodemcu". This will drive the DTR&RTS pins high and low in the right sequence to get it in programming mode before uploading.
     
    This is by far the best method, but the problem is that you need access to both the RTS and DTR pins, while most USB-to-Serial adapters break out only one of the two.
     

    Manual reset and manual program

    This only applies to boards without an on-board USB-to-Serial converter.
     
    If you don't have a USB-to-Serial converter with DTR and RTS lines, you could also just use the reset and program buttons we added in the hardware chapter. To get the ESP in program mode, GPIO0 must be low while booting: 
    1. press and hold the reset button
    2. press and hold the program button
    3. release the reset button, the ESP will boot in program mode
    4. release the program button
    5. upload the sketch
    If you want to get out of program mode without uploading, just press reset (without pressing the program button).

    Board options

    If your specific board is in the Tools > Board list (e.g. NodeMCU, SparkFun and Adafruit boards), you can just select it, and you will get the right settings. When your board isn't in the list, you'll have to select a Generic ESP8266. In that case there's lots of new options in the Tools menu of the Arduino IDE, so let's go over them and pick the right settings.
     

    Flash Mode

    Like I said before, the ESP8266 uses an external flash chip for storage. You can communicate with this chip over 2 datalines (DIO), or over all 4 datalines (QIO). Using 4 lines is two times faster than 2 lines, so in most cases, you should choose QIO. (If you're doing some advanced stuff and you need 2 more GPIO pins, you could use 2 lines instead of 4, and use the 2 lines as I/O. Most modules don't give you access to these pins, though. )

    Flash Size

    Different boards/modules have different sizes of flash chips on board. There are boards with 512kB, 1MB, 2MB and 4MB of flash. To know how much flash your board has, you can try the Examples > ESP8266 > CheckFlashConfig to see if your flash setting is correct, or you can check the specifications of your specific board online.
    You can also select the SPIFFS (SPI Flash File System) size. The SPIFFS partition is a small file system to store files. If you're not using it, you can select the minimum. Later on in the article, we'll use SPIFFS, and I'll remind you to select a larger SPIFFS size, but for now, it doesn't really matter.

    Debug port

    There's a load of things going on when the ESP is running: Things like Wi-Fi connections, TCP connections, DNS lookups... you name it. All these small tasks produce a whole lot of debug output to help you troubleshoot. However, in a normal situation, where your program is behaving as expected, you don't need all those debug messages to flood the Serial Monitor, so you can just turn them off by selecting 'Disabled'. 
    If you do wish to receive debug messages, you can select the port to send them to. (Serial on pins 1 and 3, or Serial1 on pin 2)

    Debug level

    This allows you to choose what kind of debug messages you want to show.

    Reset Method

    As mentioned in the paragraphs above, there are different methods for auto-reset and auto-program. If you're using the first method (using the edge detector), you should use 'ck', if you use the two-transistor circuit, select 'nodemcu'.

    Flash Frequency

    If you need some extra memory speed, you could change the flash frequency from 40MHz to 80MHz. This is the clock frequency of the SPI/SDIO link.

    CPU Frequency

    If you need some extra CPU performance, you can double the clock speed from 80MHz to 160MHz. It's actually an overclock, but I've never had any issues or instability. 

    Upload Speed

    The baud rate for uploading to the ESP. The default is 115200 baud, but you can go higher (if you're changing your sketch a lot, it might be too slow). 921600 baud works most of the time, but you may get an error sometimes, if that's the case, switching back to 115200 will probably solve all problems.