indiantinker's blog

[IoT] Making server-less IoT things using Deta Micros and ESP8266/32

This is the second post describing possible ways to connect ESP8266/32 with the Deta ecosystem. In the last post we went through connecting ESP8266 over an HTTPS API to read (and possibly) write data to a NoSQL database hosted with Deta.

Today, we are going to do something crazier. Did you hear about those Amazon dash buttons? You 'press' a button and something gets ordered. We are going to realize a similar use case using ESP8266 and Deta's server-less microservices called Micro.

To be frank, the code presented here is more like a boilerplate to prototype ideas with Microservices on the web. The data flow is as follows :

  1. The ESP8266 connects to the Deta microservice and pushes the data.
  2. The microservice does something with that data. It could be storing the data in a database, calling webhooks, sending things to the metaverse, stopping a war, ending inflation in Spain, etc. In our case here, we will just loop back the data. I do this all the time to set things up and make sure things are connected and singing.
  3. The micro-service responds to our request and ESP8266 does something with that. Here, it just serial prints and goes to sleep.

Setup Deta Micros

To start, we need to set our personal computer (90's term) to work with deta. The instructions on the documentation are pretty clear. Just ONE important thing that I messed up :

Always do npm init -y before you write the whole code and realize why is it not working and where are the dependencies.

Deta conveniently shows the dependencies on the page of each micro.

Image title

Some side-notes and better practices (for my future self) are :

  1. Better to start writing directly inside the deta repo. Deta does not charge for calls like Netlify so I can iteratively prototype there instead of writing the code in a node repo and then trying to adapt it to run on deta. There may be CORS issues, and dependencies that do not work in later stages, and hence, better to find them earlier.
  2. Enable Visor for easy debugging.
  3. Reach out to support on Discord and ask for examples on how to do a particular thing.
  4. Use cron wisely, especially when dealing with external services that can be rate limited.
  5. The environment variables are created using the process in the documentation but do not use quotes or spaces there. For example : API_KEY=XXXXXXXXXXXX is correct and API_KEY = 'XXXXXXXXXXXX is painfully wrong. I know, they mention it on the page too.
  6. Last but not least, if you are running a fast cron (about a minute or two), it does take 30-90 secs for the code to update. Do not panic and change anything. Go take a tea break.

If you followed everything on the links above, you should be able to paste the following in your index.js file and hit the API with Postman or Insomnia to get a response. Please install the dependencies in your deta repo.

const express = require('express')
const bodyParser = require('body-parser');
const app = express()

app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());

app.get('/', (req, res) => res.send('Hello World!'))

app.post('/', (req, res) => {

    response= req.body;
    console.log(response);
    res.json(response);

  });

// export 'app'
module.exports = app

Another thing, you have to do is make your Micro public for this to work, otherwise, you will get an authorization error when calling from non-browser entities like cURL.

Getting ESP8266 talking

Frankly, we are not doing something strange here. Pretty much sending an HTTPS POST request to our endpoint and reading the data back. It is not a looping code, so you might have to press the RESET button to run again.

PS: In good faith, I have left the endpoint here in the code for you to try.

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>


/* Set these to your desired credentials. */
const char *ssid = "<SSID>"; // ENTER YOUR WIFI SETTINGS
const char *password = "<Pass>";


const char *host = "plifnj.deta.dev";
const int httpsPort = 443; // HTTPS= 443 and HTTP = 80

DynamicJsonDocument doc(1024);

//=======================================================================
//                     Power on setup
//=======================================================================

void setup()
{
  delay(1000);
  Serial.begin(115200);

  WiFi.mode(WIFI_OFF); // Prevents reconnection issue (taking too long to connect)
  delay(1000);
  WiFi.mode(WIFI_STA); // Only Station No AP, This line hides the viewing of ESP as wifi hotspot

  WiFi.begin(ssid, password); // Connect to your WiFi router
  Serial.println("");

  Serial.print("Connecting");
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }

  // If the connection successful show the IP address in the serial monitor
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP()); // IP address assigned to your ESP

  delay(50);

  WiFiClientSecure httpsClient; // Declare object of class WiFiClient

  Serial.println(host);

  httpsClient.setInsecure();
  httpsClient.setTimeout(15000); // 15 Seconds
  delay(1000);

  Serial.print("HTTPS Connecting");
  int r = 0; // retry counter
  while ((!httpsClient.connect(host, httpsPort)) && (r < 30))
  {
    delay(100);
    Serial.print(".");
    r++;
  }
  if (r == 30)
  {
    Serial.println("Connection failed");
  }
  else
  {
    Serial.println("Connected to web");
  }

  String Link;
  // POST Data
  Link = "/";
  String data="{\"answer\":"+String(millis())+"}";

  unsigned int length = data.length();

Serial.print("requesting URL: ");
Serial.println(host + Link);

  httpsClient.print(String("POST ") + Link + " HTTP/1.1\r\n" +
                    "Host: " + host + "\r\n" +
                    "Content-Type: application/json"+ "\r\n"+
                    "Content-Length: "+length+"\r\n\r\n" +
                    data + "\r\n" +
                    "Connection: close\r\n\r\n");

  Serial.println("request sent");

  while (httpsClient.connected())
  {
    String line = httpsClient.readStringUntil('\n');
    if (line == "\r")
    {
      Serial.println("headers received");
      break;
    }
  }

  Serial.println("reply was:");
  Serial.println("==========");
  String line="";
  while (httpsClient.available())
  {
    line = line+ httpsClient.readStringUntil('\n'); // Read Line by Line
                        // Print response
  }
  //Serial.println("==========");
  Serial.println(line); 
  Serial.println("closing connection");

  unsigned int startIndex = line.indexOf('{');
  unsigned int endIndex = line.lastIndexOf('}');
  String input = line.substring(startIndex, endIndex);
  Serial.println(input);
  deserializeJson(doc, input);
  JsonObject obj = doc.as<JsonObject>();
  unsigned int response =obj["answer"];
  Serial.println(response);

}

//=======================================================================
//                    Main Program Loop
//=======================================================================
void loop()
{

}
//=====================================================================

Conclusion

  1. There could be a problem/bug, but it is because probably, we are using things not designed to be used together. If you see the output of ESP8266 you might find out that we get a 400 : Bad Request error, but we do get the output pingback. Would be great if anyone can help fix that.
  2. The approach can be used with other IoT controllers too - Particle, ESP32, or even Raspberry Pi.

If more help is needed please reach out on Twitter. My name is Rohit and I hope you make cool things.

Cheers, Rohit

#electronics #iot #webdev