// Includes the library for handling SPI
#include <SPI.h>
// Include libaries for handling WiFi and HTTP/HTTPS connections
#include<WiFi.h>
#include<HTTPClient.h>
// The CS (chip select) pin for SPI
#define CS 41
// Image resolution must be decreased in simulation.
#define BUFFER_LEN 120 * 240
// WiFi details
const char* ssid = "Wokwi-GUEST";
const char* pass = "";
// API Details
const char* secretKey = "8wPNBM5KFp9z7rLFJHL%yxkX$oDRsq2zQJQCS@4abRWuLj3hfr^ec^SVFxc%3uBS$g*Y!UfrgS%sb!xskkVjQcJq#uJ4vvdcigUyR32CzqmdV&j^Bxit#kXGhGjAMc@%";
// Just an example IP for a potential api
const char* arrivedApiUrl = "https://192.168.2.4/car-park/arrived";
const char* leftApiUrl = "https://192.168.2.4/car-park/left";
const char* initialApiUrl = "https://192.168.2.4/car-park/initial";
uint8_t buffer[BUFFER_LEN];
typedef struct BoundingBoxStruct {
int top;
int left;
int width;
int height;
} BoundingBox;
typedef struct BoundingBoxesStruct {
BoundingBox* boxes;
int length;
} BoundingBoxes;
typedef struct LicencePlateStruct {
char* licencePlate;
int parkingSpaceId;
} LicencePlate;
typedef struct LicencePlatesStruct {
LicencePlate* licencePlates;
int length;
} LicencePlates;
// Gets image data from camera chip using SPI and updates the buffer variable
// Gives random data in simulated camera chip
uint8_t* getImageData() {
digitalWrite(CS, LOW);
SPI.begin();
SPI.transfer(buffer, BUFFER_LEN);
SPI.end();
digitalWrite(CS, HIGH);
return buffer;
}
BoundingBoxes* getBoundingBoxes(uint8_t* imageData) {
// This would run the YOLOv8 model to detect the licence plate bounding boxes
// Returns set data in simulation
BoundingBox boundingBox = {
.top = 10,
.left = 20,
.width = 35,
.height = 17
};
BoundingBox* boundingBoxArr = (BoundingBox*) malloc(sizeof(BoundingBox));
*boundingBoxArr = boundingBox;
BoundingBoxes* boundingBoxesPtr = (BoundingBoxes*) malloc(sizeof(BoundingBoxes));
BoundingBoxes boundingBoxes = {
.boxes = boundingBoxArr,
.length = 1
};
*boundingBoxesPtr = boundingBoxes;
return boundingBoxesPtr;
}
void getLicencePlateFromBoundingBox(BoundingBox boundingBox, char* output) {
// This would run an OCR model to detect the licence plate text
// Returns set data in simulation
strcpy(output, "GF57XWD");
}
int getParkingSpaceIdFromBoundingBox(BoundingBox boundingBox) {
// This would probably check a map of the parking space
// Then apply a matrix transformation to the bounding box
// Then compare the transformed coords to the map and get the closest space
// In simulation just returns static data
return 1;
}
LicencePlates* getLicencePlatesFromImageData(uint8_t* imageData) {
BoundingBoxes* boundingBoxes = getBoundingBoxes(imageData);
LicencePlate* licencePlateArr = (LicencePlate*) malloc(sizeof(LicencePlate) * boundingBoxes->length);
for (int i = 0; i < boundingBoxes->length; i++) {
BoundingBox boundingBox = boundingBoxes->boxes[i];
char* licencePlateStrPtr = (char*) malloc(sizeof(char) * 8);
getLicencePlateFromBoundingBox(boundingBox, licencePlateStrPtr);
licencePlateArr[i].licencePlate = licencePlateStrPtr;
licencePlateArr[i].parkingSpaceId = getParkingSpaceIdFromBoundingBox(boundingBox);
}
LicencePlates licencePlatesStruct = {
.licencePlates = licencePlateArr,
.length = boundingBoxes->length
};
// freeBoundingBoxes(boundingBoxes);
LicencePlates* licencePlatesStructPtr = (LicencePlates*) malloc(sizeof(LicencePlates));
*licencePlatesStructPtr = licencePlatesStruct;
return licencePlatesStructPtr;
}
void freeBoundingBoxes(BoundingBoxes* boundingBoxes) {
free(boundingBoxes->boxes);
free(boundingBoxes);
}
void freeLicencePlates(LicencePlates* licencePlates) {
for (int i = 0; i < licencePlates->length; i++) {
free(licencePlates->licencePlates[i].licencePlate);
}
free(licencePlates->licencePlates);
free(licencePlates);
}
void sendHttpRequest(String url, String body) {
// Send the HTTPS request to the API
HTTPClient http;
http.begin(url);
http.addHeader("Content-Type", "application/json");
http.addHeader("API-Secret-Key", secretKey);
String requestBody = url;
http.POST(requestBody);
http.end();
}
void sentInitialLicencePlates(LicencePlates* licencePlates) {
String body = "[";
for (int i = 0; i < licencePlates->length; i++) {
if (i == 0) body += ",";
body += "{\"licencePlate\": \"" + String(licencePlates->licencePlates[i].licencePlate) + "\", \"parkingSpaceId\": " + String(licencePlates->licencePlates[i].parkingSpaceId) + "}";
}
body += "]";
sendHttpRequest(initialApiUrl, body);
}
LicencePlates* licencePlates = NULL;
void sendCarLeft(LicencePlate* licencePlate) {
String body = "\"" + String(licencePlate->licencePlate) + "\"";
sendHttpRequest(arrivedApiUrl, body);
}
void sendCarEnter(LicencePlate* licencePlate) {
String body = "{\"licencePlate\": \"" + String(licencePlate->licencePlate) + "\", \"parkingSpaceId\": \"" + String(licencePlate->parkingSpaceId) + "\"";
sendHttpRequest(leftApiUrl, body);
}
void setup() {
Serial.begin(115200);
// Set the CS (chip select) pin to output for SPI transfer
pinMode(CS, OUTPUT);
Serial.println("Connecting to WiFi");
WiFi.begin(ssid, pass);
while(WiFi.status() != WL_CONNECTED){
delay(100);
Serial.println(".");
}
Serial.println("WiFi Connected!");
Serial.println(WiFi.localIP());
}
void loop() {
// Gets image data through SPI to the camera chip
uint8_t* imageData = getImageData();
// Runs AI models to get licence plate text from images
LicencePlates* newLicencePlates = getLicencePlatesFromImageData(imageData);
if (licencePlates == NULL) {
sentInitialLicencePlates(newLicencePlates);
} else {
// Go through the new licence plates and check if there are any that aren't in the old licence plates
// This means that the car has entered and send the request
for (int i = 0; i < newLicencePlates->length; i++) {
bool entered = false;
LicencePlate licencePlate = licencePlates->licencePlates[i];
for (int j = 0; j < licencePlates->length; j++) {
if (i == j) continue;
LicencePlate licencePlate2 = licencePlates->licencePlates[j];
if (licencePlate.licencePlate == licencePlate2.licencePlate) entered = true;
}
if (entered) sendCarEnter(&licencePlate);
}
// Go through the old licence plates and check if there are any that aren't in the new licence plates
// This means that the car has left and send the request
for (int i = 0; i < licencePlates->length; i++) {
bool left = false;
LicencePlate licencePlate = newLicencePlates->licencePlates[i];
for (int j = 0; j < licencePlates->length; j++) {
if (i == j) continue;
LicencePlate licencePlate2 = licencePlates->licencePlates[j];
if (licencePlate.licencePlate == licencePlate2.licencePlate) left = true;
}
if (left) sendCarLeft(&licencePlate);
}
freeLicencePlates(licencePlates);
}
licencePlates = newLicencePlates;
delay(5000);
}