Home > OS >  Flask REST API Server with ESP8266 Client
Flask REST API Server with ESP8266 Client

Time:09-30

I have developed a password verification system using Arduino Nano ESP8266 (FrontEnd) and Backend with Flask REST API server.

Following is my code of ESP8266, Which is passing the HTTP POST request to REST API server,

#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <ESP8266HTTPClient.h>
#include<SoftwareSerial.h>

SoftwareSerial link(4, 0);
byte greenLED = 13;
byte statusLED = 14;
String hexstring = "";

// Replace with your network credentials
const char *ssid     = "***********";
const char *password = "***********";

const long utcOffsetInSeconds = 240;

// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "asia.pool.ntp.org", utcOffsetInSeconds);

//Week Days
String weekDays[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

//Month names
String months[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  Serial.print("Initializing System");
  for (int i = 10; i > 0; i--) {
    delay(500);
    Serial.print(i); Serial.println(' ');
  }
  pinMode(statusLED, OUTPUT);

  // Connect to Wi-Fi
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    link.begin(9600);
    link.setTimeout(100);
    // clear any pending stuff
    link.readString();
    pinMode(greenLED, OUTPUT);
  }

  // Initialize a NTPClient to get time
  timeClient.begin();

  timeClient.setTimeOffset(19770);
}

void loop() {
  timeClient.update();

  unsigned long epochTime = timeClient.getEpochTime();
  String formattedTime = timeClient.getFormattedTime();
  int currentHour = timeClient.getHours();
  int currentMinute = timeClient.getMinutes();
  int currentSecond = timeClient.getSeconds();
  String weekDay = weekDays[timeClient.getDay()];

  //Get a time structure
  struct tm *ptm = gmtime ((time_t *)&epochTime);
  int monthDay = ptm->tm_mday;
  int currentMonth = ptm->tm_mon   1;
  String currentMonthName = months[currentMonth - 1];
  int currentYear = ptm->tm_year   1900;

  //Print complete date:
  String currentDate = String(currentYear)   String(currentMonth)   String(monthDay)   String(currentHour)   String(currentMinute)    String(currentSecond);


  delay(1000);

  if (link.available()) {
    String rec = link.readString();
    Serial.print(F("rec:")); Serial.println(rec);
    if (rec.length() > 0) {    // print it out
      Serial.println(F("Received data"));
      for (size_t i = 0; i < rec.length(); i  ) {
        digitalWrite(greenLED, HIGH); //flash led to show data is arriving
        delay(20);
        digitalWrite(greenLED, LOW);
        if (i % 8 == 0) {
          Serial.println();
        }
        Serial.print(" 0x");
        if (rec[i] < 10) {
          Serial.print("0");
          hexstring  = '0';
        }
        Serial.print(rec[i], HEX);
        hexstring  = String(rec[i], HEX);

      }
      Serial.println();
      Serial.print("Current Date: ");
      Serial.println(currentDate);
      Serial.println("Your String Sir:");
      Serial.print(hexstring);
      Serial.println();
      //confirmation();
      
      String sub1 = hexstring.substring(0, 2);
      String sub2 = hexstring.substring(16, 32);
      String sub3 = hexstring.substring(32, 34);

      if (sub3 == "31") { //New User Registartion
        String data =  "{\"user_id\" :\""   sub1   "\",\"encrypted_password\" :\""   sub2   "\",\"option\" :\""   sub3   "\" ,\"request_datetime\" :\""   currentDate   "\"}";
        HTTPClient http; //Declare object of class HTTPClient
        http.begin("http://192.168.8.228:5000/users/");      //Specify request destination
        http.addHeader("Content-Type", "application/json");
        int httpCode = http.POST(data.c_str());

        if (httpCode == 200) {
          String payload = http.getString();   //Get the response payload
          Serial.println(httpCode);   //Print HTTP return code
          Serial.println(payload);    //Print request response payload
          if (payload == "Code1") {
            link.read();
            Serial.println(F("Prompt other side Confirmation"));
            link.print("Success_Code1");
            digitalWrite(statusLED, HIGH); //flash led to show data is arriving
            delay(20);
            digitalWrite(statusLED, LOW);
          }
          else {
            link.read();
            Serial.println(F("Prompt other side Error1"));
            link.print("Error 1");
          }
        }
        http.end();
        rec = ""; // clear for next receive
        hexstring = ""; //Clear hexstring
      }

      else if (sub3 == "32") { //Confirm Password
        String data ="{\"user_id\":\""   sub1   "\",\"encrypted_password\":\""   sub2   "\",\"option\":\""   sub3   "\" ,\"request_datetime\":\""   currentDate   "\"}";
        
        HTTPClient http; //Declare object of class HTTPClient
        String link2 = "http://192.168.8.228:5000/users/"   sub1;
        http.begin(link2);      //Specify request destination
        http.addHeader("Content - Type", "application / json");
        int httpCode = http.POST(data.c_str());
        delay(1000);
        Serial.println(data.c_str());
        Serial.println(httpCode);
        
        if (httpCode == 200) {
          String payload = http.getString();   //Get the response payload
          Serial.println(httpCode);   //Print HTTP return code
          Serial.println(payload);    //Print request response payload
          if (payload == "Code2") {
            link.read();
            Serial.println(F("Prompt other side Confirmation"));
            link.print("Success_Code1");
            digitalWrite(statusLED, HIGH); //flash led to show data is arriving
            delay(20);
            digitalWrite(statusLED, LOW);
          }
        }
        else {
          link.read();
          Serial.println(F("Prompt other side Error1"));
          link.print("Error 1");
        }
        http.end();
        rec = ""; // clear for next receive
        hexstring = ""; //Clear hexstring
      }

      else if (sub3 == "33") { //Change Password
        String data =  "{\"user_id\" :\""   sub1   "\",\"encrypted_password\" :\""   sub2   "\",\"option\" :\""   sub3   "\" ,\"request_datetime\" :\""   currentDate   "\"}";
        HTTPClient http; //Declare object of class HTTPClient
        http.begin("http://192.168.8.228:5000/users/");      //Specify request destination
        http.addHeader("Content-Type", "application/json");
        int httpCode = http.POST(data.c_str());

        if (httpCode == 200) {
          String payload = http.getString();   //Get the response payload
          Serial.println(httpCode);   //Print HTTP return code
          Serial.println(payload);    //Print request response payload
          if (payload == "Code3") {
            link.read();
            Serial.println(F("Prompt other side Confirmation"));
            link.print("Success_Code1");
            digitalWrite(statusLED, HIGH); //flash led to show data is arriving
            delay(20);
            digitalWrite(statusLED, LOW);
          }
        }
        else {
          link.read();
          Serial.println(F("Prompt other side Error1"));
          link.print("Error 1");
        }
        http.end();
        rec = ""; // clear for next receive
        hexstring = ""; //Clear hexstring
      }

    }
  }
}

As you can see, There are 3 methods,

New user registration code part:

if (sub3 == "31") { //New User Registartion
        String data =  "{\"user_id\" :\""   sub1   "\",\"encrypted_password\" :\""   sub2   "\",\"option\" :\""   sub3   "\" ,\"request_datetime\" :\""   currentDate   "\"}";
        HTTPClient http; //Declare object of class HTTPClient
        http.begin("http://192.168.8.228:5000/users/");      //Specify request destination
        http.addHeader("Content-Type", "application/json");
        int httpCode = http.POST(data.c_str());

        if (httpCode == 200) {
          String payload = http.getString();   //Get the response payload
          Serial.println(httpCode);   //Print HTTP return code
          Serial.println(payload);    //Print request response payload
          if (payload == "Code1") {
            link.read();
            Serial.println(F("Prompt other side Confirmation"));
            link.print("Success_Code1");
            digitalWrite(statusLED, HIGH); //flash led to show data is arriving
            delay(20);
            digitalWrite(statusLED, LOW);
          }
          else {
            link.read();
            Serial.println(F("Prompt other side Error1"));
            link.print("Error 1");
          }
        }
        http.end();
        rec = ""; // clear for next receive
        hexstring = ""; //Clear hexstring
      } 

Verify password code part:

else if (sub3 == "32") { //Confirm Password
        String data ="{\"user_id\":\""   sub1   "\",\"encrypted_password\":\""   sub2   "\",\"option\":\""   sub3   "\" ,\"request_datetime\":\""   currentDate   "\"}";
        
        HTTPClient http; //Declare object of class HTTPClient
        String link2 = "http://192.168.8.228:5000/users/"   sub1;
        http.begin(link2);      //Specify request destination
        http.addHeader("Content - Type", "application / json");
        int httpCode = http.POST(data.c_str());
        delay(1000);
        Serial.println(data.c_str());
        Serial.println(httpCode);
        
        if (httpCode == 200) {
          String payload = http.getString();   //Get the response payload
          Serial.println(httpCode);   //Print HTTP return code
          Serial.println(payload);    //Print request response payload
          if (payload == "Code2") {
            link.read();
            Serial.println(F("Prompt other side Confirmation"));
            link.print("Success_Code1");
            digitalWrite(statusLED, HIGH); //flash led to show data is arriving
            delay(20);
            digitalWrite(statusLED, LOW);
          }
        }
        else {
          link.read();
          Serial.println(F("Prompt other side Error1"));
          link.print("Error 1");
        }
        http.end();
        rec = ""; // clear for next receive
        hexstring = ""; //Clear hexstring
      }

Below is my Flask/Python API server backend code:

from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
import uuid


app = Flask(__name__)
db = SQLAlchemy(app)
DEBUG = True

# app.config['SECRET_KEY'] = 'secret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql pymysql://root@localhost:3306/app'
print("App2 Change2")


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True, index=True)
    user_id = db.Column(db.VARCHAR(10), nullable=False, unique=True)
    encrypted_password = db.Column(db.VARCHAR(20), nullable=False)
    option = db.Column(db.VARCHAR(3), nullable=False)
    request_datetime = db.Column(db.VARCHAR(20), nullable=False)
    public_id = db.Column(db.VARCHAR(100), nullable=False)

    def __repr__(self):
        return f'User <{self.public_id}>'


db.create_all()


@app.route('/')
def home():
    return {
        'message': 'Welcome to Vinod Test App'
    }


@app.route('/users/')
def get_users():
    return jsonify([
        {
            'user_id': user.user_id, 'encrypted_password': user.encrypted_password, 'option': user.option,
            'request_datetime': user.request_datetime
        } for user in User.query.all()
    ])


@app.route('/users/<user_id>/')
def get_user(user_id):
    print(user_id)
    user = User.query.filter_by(user_id=user_id).first_or_404()
    return {
        'user_id': user.user_id, 'encrypted_password': user.encrypted_password,
        'public_id': user.public_id, 'original_time': user.request_datetime
    }


# Add New User

@app.route('/users/', methods=['POST'])
def create_user():
    data = request.get_json()
    print(request.get_json())
    if not 'user_id' in data or not 'encrypted_password' in data or not 'option' in data:
        return jsonify({
            'error': 'Bad Request',
            'message': 'User Id and Encrypted Data is Missing'
        }), 400
    if len(data['encrypted_password']) < 16:
        return jsonify({
            'error': 'Bad Request',
            'message': 'Length Error in Encrypted Data'
        }), 400
    u = User(
        user_id=data['user_id'],
        encrypted_password=data['encrypted_password'],
        option=data.get('option'),
        request_datetime=data['request_datetime'],
        public_id=str(uuid.uuid4())
    )
    db.session.add(u)
    db.session.commit()
    return {
               'id': u.user_id, 'encrypted_data': u.encrypted_password,
               'public_id': u.public_id, 'original_time': u.request_datetime,
           }, 200


# Verify Password

@app.route('/users/<user_id>', methods=['POST'])
def check_user(user_id):
    data = request.get_json()
    if 'user_id' not in data:
        return {
                   'error': 'Bad Request',
                   'message': 'User_id field needs to be present'
               }, 400
    user = User.query.filter_by(user_id=user_id).first_or_404()
    user.user_id = data['user_id']
    if 'user_id' in data:
        if user.encrypted_password == data['encrypted_password']:

            return jsonify({
                'user_id': user.public_id,
                'encrypted_password': user.encrypted_password,
                'original_time': user.request_datetime
            })
        else:
            return {
                       'error': 'Bad Request',
                       'message': 'Password Do Not Match'
                   }, 400


@app.route('/users/<user_id>/', methods=['DELETE'])
def delete_user(user_id):
    user = User.query.filter_by(user_id=user_id).first_or_404()
    db.session.delete(user)
    db.session.commit()
    return {
        'success': 'Data deleted successfully'
    }


if __name__ == '__main__':
    app.run(debug=True)

My issue is: It is not working when verify password passed through the ESP8266 to the Backend for verify password. But new user registration works when passed through ESP8266.

Below is the error I am getting in the PyCharm:

 * Running on http://192.168.8.100:5000/ (Press CTRL C to quit)
[2021-09-26 14:57:55,237] ERROR in app: Exception on /users/32 [POST]
Traceback (most recent call last):
  File "C:\Users\Vinod Amarathunga\PycharmProject\Test1\server1\venv\lib\site-packages\flask\app.py", line 2070, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\Vinod Amarathunga\PycharmProject\Test1\server1\venv\lib\site-packages\flask\app.py", line 1515, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\Vinod Amarathunga\PycharmProject\Test1\server1\venv\lib\site-packages\flask\app.py", line 1513, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\Vinod Amarathunga\PycharmProject\Test1\server1\venv\lib\site-packages\flask\app.py", line 1499, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  File "C:\Users\Vinod Amarathunga\PycharmProject\Test1\server1\app2_Change2.py", line 108, in check_user
    if 'user_id' not in data:
TypeError: argument of type 'NoneType' is not iterable

192.168.8.174 - - [26/Sep/2021 15:52:20] "POST /users/32 HTTP/1.1" 500 -

But when I send the same request (Verify Password) from Postman it works, Below is the Postman response on verification:

enter image description here

This request was the same sent from ESP8266 which was captured from Mockon test server:

enter image description here

Backend expected response [Working with Postman]

192.168.8.103 - - [26/Sep/2021 15:52:59] "POST /users/32 HTTP/1.1" 400 -

Note: Encrypted data along with user inputs are passed to ESP by an Arduino Nano as below:

enter image description here

CodePudding user response:

The error TypeError: argument of type 'NoneType' is not iterable means that your data = request.get_json() return a None. The reason why it return a None can be found in falsk.Request.get_json API documentation. It said:

By default this function will return None if the mimetype is not application/json

Take a look at your Arduino code, you add extra spaces on the http header:

       http.addHeader("Content - Type", "application / json");

The HTTP header should be:

Content-Type: application/json
  • Related