Overview
The Loading circuit will be placed at the Seller location where the order items are loaded on to the delivery vehicle. It will have an ultrasonic sensor that will detect the arrival of the vehicle and trigger the camera to take a picture of the vehicle number plate for the ANPR service. The ultrasonic sensor will also detect when the vehicle leaves after loading is complete and capture the departure time data. All data will be sent to the Database and the Smart Contract.
Components
Component Name | Purpose |
---|---|
ESP32 Cam Module | Processor and to capture the vehicle number plate image |
HSR04 Ultrasonic Sensor | To detect the arrival and departure of a vehicle |
LM2596 Buck converter | To convert voltages as required for the components |
LEDs | To indicate various actions and states |
Circuit Diagram
Assembly
HSR04 Ultrasonic Sensor
ESP32 Cam Module Pin | HSR04 Pin |
---|---|
IO2 | Echo Pin |
IO16 | Trig Pin |
VCC | Vin |
GND | GND |
LEDs
ESP32 Cam Module Pin | Vehicle Detector LED Pin |
---|---|
14 | Anode (+) |
GND | Cathod (-) |
ESP32 Cam Module Pin | Image Capture Indicator LED Pin |
---|---|
15 | Anode (+) |
GND | Cathod (-) |
2X18650 Power Supply with LM2596 Buck converter
18650 Pin | LM2596 Pin |
---|---|
+(ve) | IN+ |
-(ve) | IN- |
ESP32 Cam module with LM2596 Buck converter
ESP32 Cam module Pin | LM2596 Pin |
---|---|
VCC | OUT+ |
GND | OUT- |
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 Loading location circuit
Project Description:
This sketch posts image clicked by camera to AWS upon detection of object by IR sensor
This sketch sendS the arrival time 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>
// #include <WiFiUdp.h> not sure if required , will remove after testing
// GLOBAL VARIABLES
char Time[500]; // variable to store JSON message to be published via MQTT
unsigned long int loggedTime; // variable to store real time value
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 = "1";
const String camera_id = "11";
const int microcontroller_id_int = 1;
const int ultrasonic_sensor_id = 12;
// boolean check variables
bool timeSent = false;
bool imageCapturedSent = false;
bool imageRecognised = false;
// PIN DEFINITION
#define echoPin 2
#define trigPin 16
#define vehicleIndicatorLED 14
#define imageCapturedSentLED 15
#define onBoardLED 33
// 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
// 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 = 15; // quality parameter
config.fb_count = 1;
// 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 indicator LEDS
void LEDSetup()
{
pinMode(vehicleIndicatorLED, OUTPUT);
pinMode(onBoardLED, OUTPUT);
pinMode(imageCapturedSentLED, OUTPUT);
digitalWrite(vehicleIndicatorLED, LOW);
digitalWrite(imageCapturedSentLED, 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 format the JSON message in the required structure
void JSON_formatter()
{
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 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 3 times then restart the ESP.
{
Serial.println("Restarting the ESP");
ESP.restart();
}
}
else
{
Serial.println("Message published successfully");
publish_check = 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);
LEDSetup();
Wifi_setup();
AWS_setup();
HCSR04_setup();
Camera_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 at the start of 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();
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
}
else // this means no vehicle has been detected nearby or the vehicle has left
{
digitalWrite(vehicleIndicatorLED, LOW);
digitalWrite(imageCapturedSentLED, LOW);
timeSent = false;
imageCapturedSent = false;
imageRecognised = false;
}
}