Projects
Unloading Circuit

Overview

The Unloading circuit will be placed at the Buyer location where the order items are unloaded from the delivery vehicle. It will have an ultrasonic sensor that will detect the arrival of the vehicle and capture the arrival time data. It will also trigger the camera to take a picture of the vehicle number plate for the ANPR service. It will also have a load sensor that will capture the weight of the unloaded items. All data will be sent to the Database and the Smart Contract.

Components

ComponentPurpose
ESP32 Cam ModuleProcessor and to capture the vehicle number plate image
HSR04 Ultrasonic SensorTo detect the arrival of a vehicle
HX711The ADC required by the Load sensor
1kg Load cellTo capture the load/weight
LM2596 Buck converterTo convert supply voltage to component voltage
LEDsTo indicate various actions and states

Circuit Diagram

web3_unloading_circuit

Connections

HSR04 Ultrasonic Sensor

ESP32 Cam PinHSR04 Pin
IO2Echo Pin
IO16Trig Pin
VCCVin
GNDGND

LEDs

ESP32 Cam module PinVehilcle Indicator LED Pin
12Anode (+)
GNDCathod (-)
ESP32 Cam module PinImage capatured and sent Indicator LED Pin
15Anode (+)
GNDCathod (-)
ESP32 Cam module PinLoad Indicator LED Pin
1Anode (+)
GNDCathod (-)

HX711

ESP32 Cam PinHX711 Pin
Vcc3V3
IO14SCK
IO13DT
GNDGND

HX711 - Load cell

HX711 PinLoad cell Pin
E+RED
E-BLACK
A-WHITE
A+GREEN

2X18650 Power Supply with LM2596 Buck converter

18650 PinLM2596 Pin
+(ve)IN+
-(ve)IN-

ESP32 Cam module with LM2596 Buck converter

ESP32 Cam module PinLM2596 Pin
VCCOUT+
GNDOUT-

Code

This code sends data to the AWS IoT platform which requires certificates and keys. These need to be generated for each account and cannot be reused. Instructions for getting these will be made available to subscribers.

/*
  Project: WEB3.0 distributed app Unloading location circuit
  Project Description:
  This sketch posts image clicked by camera to AWS upon detection of object by IR sensor
  This sketch send the arrival time to AWS via MQTT
  This sketch also sends the measured unloaded quantity to AWS via MQTT
 
  Author: STEMVentor Educonsulting
 
  This code is copyrighted. Please do not reuse or share for any purpose other than for learning with subscribed STEMVentor programs.
  This code is for educational purposes only and is not for production use.
*/
 
// LIBRARIES
// all the libraries (5) needed to connect and publish to AWS MQTT:-
// includes all the credentials like certificates , wifi passwords , IoT endpoint and thing name
#include "Secrets.h"
// enbales ESP32  to connect to wifi
#include <WiFiClientSecure.h> // enbales ESP32  to connect to wifi
#include <WiFi.h>
// MQTT library which helps publishing and subscribing to topics
#include <PubSubClient.h>
// JSON library to format the message being published
#include <ArduinoJson.h>
 
// to get real time in UNIX format
#include <NTPClient.h>
 
// libraries required to use the ESP32 cam
// to initialise the ESP32 cam
#include <esp_camera.h>
// TO DISABLE BROWNER PROBLEMS (when low volatge is supplied to the chip  , the chip shuts down  , to prevent this shut down we disbale browner probl)
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
// library required to do a HTTP post
#include <HTTPClient.h>
 
// Libraries to communicate and read values from Load sensor via HX711 ADC
// for I2C communication
#include <Wire.h>
// to read values from the HX711 load sensor
#include <HX711.h>
 
// #include <WiFiUdp.h> not sure if required , will remove after testing
 
// GLOBAL VARIABLES
 
char Time[500];              // variable to store logged time JSON message to be published via MQTT
char Unloaded_Quantity[500]; // variable to store unloaded quantity JSON message to be published via MQTT
 
unsigned long int loggedTime; // variable to store real time value
float load;                   // variable to store measured unloaded quantity
float distanceCm;             // to store vehicle distance
// define ultrasonic sensor speed in cm/uS
#define SOUND_SPEED 0.034
 
// device IDs
// string IDs are for the HTTP headers , it is not taking int for some reason
const String microcontroller_id = "3";
const String camera_id = "31";
const int microcontroller_id_int = 3;
const int Ultrasonic_sensor_id = 32;
const int Weight_sensor_id = 33;
 
// bool check variables
bool imageCapturedSent = false; // variable to store whether the image has been sent succesfully or no
bool loadSent = false;          // variable to store whether the load has been sent succesfully or no
bool timeSent = false;
bool imageRecognised = false;
 
// PIN DEFINITION
#define echoPin 2
#define trigPin 16
#define vehicleIndicatorLED 12
#define imageCapturedSentLED 15
#define loadIndicatorLED 1
#define onBoardLED 33
 
// defining HX711 pins
#define LOADCELL_DOUT 13
#define LOADCELL_SCK 14
// defining camera pins
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#define FLASH_GPIO_NUM 4
#define FLASH_GPIO_NUM 4 // ledPin refers to ESP32-CAM GPIO 4 (flashlight)
 
// Define the topic to which the data should be published
#define AWS_IOT_PUBLISH_TOPIC "com.stemventor/sensor_data"
 
/* INITIALIZE OBJECTS
 * Libraries usually follow an object-oriented approach that requires
 * an instance of the class to call its methods.
 */
WiFiClientSecure net;                            // wifi client
PubSubClient client(net);                        // MQTT client
WiFiUDP ntpUDP;                                  // UDP client
NTPClient timeClient(ntpUDP, "id.pool.ntp.org"); // NTPC client
HX711 scale;                                     // load cell object
 
// Setup functions
 
// function to setup wifi
void Wifi_setup()
{
 
  WiFi.mode(WIFI_STA);                  // station mode , where the device connects to an external wifi network
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // gets the ssid and password from the secrets.h file
 
  Serial.println("Connecting to Wi-Fi");
 
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
}
 
// function to setup AWS
void AWS_setup()
{
 
  // Configure WiFiClientSecure to use the AWS IoT device credentials
  net.setCACert(AWS_CERT_CA);
  net.setCertificate(AWS_CERT_CRT);
  net.setPrivateKey(AWS_CERT_PRIVATE);
 
  // Connect to the MQTT broker on the AWS endpoint we defined earlier
  client.setServer(AWS_IOT_ENDPOINT, 8883);
 
  // Create a message handler
  // client.setCallback(messageHandler);
 
  Serial.println("Connecting to AWS IoT");
 
  while (!client.connected())
  {
    Serial.print(".");
    if (client.connect(THINGNAME))
    {
      Serial.println("Connected to AWS IoT");
      // subscribe over here
    }
    else
    {
      Serial.println("AWS IoT Connection Failed! Retrying...");
      int errorCode = client.state();
      Serial.print("Error code: ");
      Serial.println(errorCode);
      delay(1000);
    }
  }
}
 
// function to setup NTPC to get real time value
void NTPC_setup()
{
  timeClient.begin();
  timeClient.setTimeOffset(0);
}
 
// function to setup camera (ESP32 Cam module OV2640)
void Camera_setup()
{
  pinMode(FLASH_GPIO_NUM, OUTPUT); // defining pinmode as output  for the flash on board
  pinMode(33, OUTPUT);             // defining pinmode as output for the LED on the esp32 cam board , this is being used an indication  that the camera has been initialised properly
  // Turn-off the 'brownout detector'
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
 
  // OV2640 camera module
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
 
  delay(500);
 
  config.frame_size = FRAMESIZE_SVGA; // this the resolution , you can change the value according to your need
  config.jpeg_quality = 12;           // quality parameter
  config.fb_count = 5;
 
  // Camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK)
  {
    Serial.printf("Camera init failed with error 0x%x", err);
    // ESP.restart();
  }
 
  else
  {
    Serial.println("camera inint succesfull");
    digitalWrite(33, LOW); // switch on the indication light on board , its a active low light.
  }
}
 
// function to setup ultrasonic sensor
void HCSR04_setup()
{
  // Define the pinmode
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin, INPUT);  // Sets the echoPin as an Input
}
 
// function to setup HX711 (ADC)
void HX711_setup()
{
  Wire.begin();
  scale.begin(LOADCELL_DOUT, LOADCELL_SCK);
  scale.set_scale();
  scale.tare(); // Tare the scale (set it to zero)
}
 
// function to setup the indicator LEDS
void LEDSetup()
{
  pinMode(vehicleIndicatorLED, OUTPUT);
  pinMode(imageCapturedSentLED, OUTPUT);
  pinMode(loadIndicatorLED, OUTPUT);
  pinMode(onBoardLED, OUTPUT);
  digitalWrite(vehicleIndicatorLED, LOW);
  digitalWrite(imageCapturedSentLED, LOW);
  digitalWrite(loadIndicatorLED, LOW);
  digitalWrite(onBoardLED, LOW);
}
// loop fucntions
 
// function to read value of ultrasonic sensor and store in global variable
void readHCSR04()
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
 
  // Reads the echoPin, returns the ultrasonic sensor wave travel time in microseconds
  long duration = pulseIn(echoPin, HIGH);
 
  // Calculate the distance
  distanceCm = duration * SOUND_SPEED / 2;
}
 
// function to note down value of actual time when IR sensor detects object
void logTime()
{
  timeClient.update();
  // Get current Unix timestamp
  loggedTime = timeClient.getEpochTime();
  Serial.print("logged time : ");
  Serial.println(loggedTime);
}
 
// function to read load value from the HX711 sensor
void readHX711()
{
  load = scale.get_units(5); // in kg
  load = (load / 420);       // 420 is the calibration factor (needs to be measured separately)
  load = load * 1000;        // convert to grams
  Serial.print("Loaded quantity: ");
  Serial.print(load);
  Serial.println(" g");
}
 
// function to format the JSON message in the required structure
void JSON_formatter_logTime()
{
  JsonDocument doc;
 
  doc["timestamp"] = timeClient.getEpochTime();
  delay(300);
  doc["microcontroller_id"] = microcontroller_id_int;
 
  JsonArray sensor_data = doc["sensor_data"].to<JsonArray>();
 
  JsonObject sensor_data_0 = sensor_data.add<JsonObject>();
  sensor_data_0["sensor_id"] = Ultrasonic_sensor_id;
 
  JsonObject sensor_data_0_parameters = sensor_data_0["parameters"].to<JsonObject>();
  sensor_data_0_parameters["time"] = loggedTime;
 
  doc.shrinkToFit();
  // Serial.println(doc.overflowed()); //to check if the JSON is not more than the initialised JSON variable
  serializeJson(doc, Time);
  Serial.println(Time);
}
 
// function to form JSON of Loaded quantity to publish
void JSON_formatter_UQ() // UQ stands for Loaded Quantity
{
  JsonDocument doc;
 
  doc["timestamp"] = timeClient.getEpochTime() + (7 * 3600);
  doc["microcontroller_id"] = microcontroller_id;
 
  JsonArray sensor_data = doc["sensor_data"].to<JsonArray>();
 
  JsonObject sensor_data_0 = sensor_data.add<JsonObject>();
  sensor_data_0["sensor_id"] = Weight_sensor_id;
 
  JsonObject sensor_data_0_parameters = sensor_data_0["parameters"].to<JsonObject>();
  sensor_data_0_parameters["weight"] = load;
 
  doc.shrinkToFit(); // compresses the size of the JSON
  serializeJson(doc, Unloaded_Quantity);
}
 
// function to publish the json to MQTT topic
void publishMessage(char message[500])
{
  bool publish_check = false; // variable to indicate if MQTT publsih has happened succesfully or no
  int publish_counter = 0;    // variable to store number of times MQTT publish has been tried
  while (publish_check == false)
  {
    if (!client.publish(AWS_IOT_PUBLISH_TOPIC, message))
    {
      Serial.println("Failed to publish message 1");
      Serial.println("Checking connection with AWS IoT...");
      checkAWS();
      delay(1000);
      if (publish_counter > 3) // if publish is not successfull for 4 times then restart the ESP.
      {
        Serial.println("Restarting the ESP");
        ESP.restart();
      }
    }
    else
    {
      Serial.println("Message published successfully");
      publish_check = true;
      loadSent = true;
    }
    publish_counter++;
  }
}
 
// function to click and send  photo
void Click_SendPhoto()
{
  bool post_check = false; // variable to indicate if HTTP post has happened succesfully or no
  int post_counter = 0;    // variable to store number of times HTTP post has been tried
  if (WiFi.status() == WL_CONNECTED)
  {
    digitalWrite(FLASH_GPIO_NUM, HIGH); // switch on flash
    delay(500);
 
    Serial.println("Taking picture...");
    camera_fb_t *fb = NULL; // empty the variable
    esp_err_t res = ESP_OK;
 
    fb = esp_camera_fb_get(); // click photo , the photo gets stored as a pixel data buffer
    if (!fb)
    {
      Serial.println("Camera capture failed");
      // return ESP_FAIL;
    }
    else
    {
      Serial.println("picture taken");
    }
    digitalWrite(FLASH_GPIO_NUM, LOW); // swich of the flash light . also an indication that the photo clicking part is over
 
    HTTPClient http; // define a http client
 
    http.begin("https://gewomgsnm0.execute-api.ap-south-1.amazonaws.com/beta/"); // aws api link where the image will be posted
    http.setTimeout(20000);                                                      // increase or decrease timeout depending how strong the wifi connectivity is , its the time to establish a connection with the link
    http.addHeader("Content-Type", "image/jpg");                                 // header files
    http.addHeader("microcontrollerID", microcontroller_id);
    http.addHeader("sensor_id", camera_id);
 
    while (post_check == false) // keep on posting the image to the api till it receives and responds properly , this is done because sometimes the server is busy.
    {
      int httpCode = http.POST(fb->buf, fb->len); // post the buffer and its length  ,"->" is the pointer
      post_counter++;
      if (httpCode == HTTP_CODE_OK) // once http code is 200 , which means post was successfull read and print the return which it got.
      {
        post_check = true; //successfully posted
 
        String payload = http.getString();
        Serial.println(payload);
        // Create a StaticJsonDocument
        StaticJsonDocument<200> doc3;
 
        // Deserialize the JSON
        DeserializationError error = deserializeJson(doc3, payload);
 
        // Extract the value
        const char *value = doc3["sensor_data"][0]["parameters"][0]["value"];
        Serial.print("Value: ");
        Serial.println(value);
 
        // Check if the value is "null"
        if (strcmp(value, "null") == 0)
        {
          Serial.println("The value is null");
          imageRecognised = false;
          delay(5000); //wait for 5 seconds for the camera to stop lagging
        }
        else
        {
          Serial.println("The value is not null");
          imageRecognised = true;
        }
 
        
      }
      else
      {
        Serial.print("HTTP post not succesful , code : ");
        Serial.println(httpCode);
        checkWIFI(); // check wifi
        if (post_counter > 3)
        {
          Serial.println("Restarting the ESP");
          ESP.restart();
        }
        else
        {
          Serial.println("retrying posting the photo. ");
        }
      }
    }
    http.end();               // end the connection
    esp_camera_fb_return(fb); // Release the frame buffer
  }
}
 
void checkWIFI()
{
  if (WiFi.status() != WL_CONNECTED)
  {
    Serial.println("Wi-Fi not connected, reconnecting...");
    Wifi_setup();
  }
}
 
void checkAWS()
{
  if (!client.connected())
  {
    Serial.println("AWS IoT not connected, reconnecting...");
    AWS_setup();
  }
}
// Main setup function
void setup()
{
  Serial.begin(115200);
  Wifi_setup();
  AWS_setup();
  Camera_setup();
  HCSR04_setup();
  HX711_setup();
  NTPC_setup();
  Serial.println("All setup done!");
  digitalWrite(onBoardLED, HIGH);
}
 
// Main loop function
void loop()
{
  // the conidtions have been applied such it first sends the time as soon it detects a vehicle and them clicks and sends the number plate image.
  // after thats done it checks if any load has been kept on the weighing scale and sends that once.
  // the code stops after all the 3 paramters have been sent once
 
  checkWIFI(); // check wifi connection at the start fo every loop
 
  readHCSR04();
 
  if (distanceCm < 25) // the value 25 can be changed after testing , this indicates a vehcile has been detected
  {
    digitalWrite(vehicleIndicatorLED, HIGH);
    if (timeSent == false)
    {
      logTime();
      delay(300);
      JSON_formatter_logTime();
      delay(1000);
      publishMessage(Time);
      timeSent = true;
      delay(5000);
    }
    if (imageCapturedSent == false && timeSent == true)
    {
      while ( imageRecognised == false) //will resend image if there is null value returned 
      {
      Click_SendPhoto();  
      }
      imageCapturedSent = true;
      digitalWrite(imageCapturedSentLED, HIGH);
    }
 
    // Monitor free heap memory
    Serial.print("Free heap: ");
    Serial.println(ESP.getFreeHeap());
    // delay(10000); //kept for testing / debugging
 
    readHX711();
 
    if (load >= 100)
    {
      digitalWrite(loadIndicatorLED, HIGH);
      if (imageCapturedSent == true && loadSent == false)
      {
        JSON_formatter_UQ();
        publishMessage(Unloaded_Quantity);
        loadSent = true;
      }
    }
 
    else
    {
      digitalWrite(loadIndicatorLED, LOW);
    }
  }
 
  else // this means no vehicle has been detected nearby or the vehicle has left
  {
    digitalWrite(vehicleIndicatorLED, LOW);
    digitalWrite(imageCapturedSentLED, LOW);
    digitalWrite(loadIndicatorLED, LOW);
    timeSent = false;
    imageCapturedSent = false;
    loadSent = false;
    imageRecognised = false;
  }
}