Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Mithun Das
Published

Industry 4.0 Car Parking Marketplace (IOTA+iBeacon+ML)

A PoC for Industry 4.0 Car parking market place where machines negotiates and consumes service without human interaction.

IntermediateFull instructions provided14 hours3,101

Things used in this project

Hardware components

NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
One for Car iBeacon and one for Garage iBeacon scanner
×2
HM-10 BLE Module
One for iBeacon transmission in car and another for scanning iBeacons by garage
×2
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
Used for garage display board
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
Use for garage pole movement
×1
IR beam sensor
×1

Software apps and online services

IOTA Tangle
IOTA Tangle
AWS EC2
Amazon Web Services AWS EC2
Arduino IDE
Arduino IDE
Ionic Framework
TensorFlow
TensorFlow

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Premium Female/Female Jumper Wires, 40 x 3" (75mm)
Premium Female/Female Jumper Wires, 40 x 3" (75mm)
Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Multitool, Screwdriver
Multitool, Screwdriver

Story

Read more

Custom parts and enclosures

garage-1_5A5YaxEo21.stl

garage-2_jSREkb34DE.stl

garage-3_SIvlNCLgXt.stl

garage-4_6zazT0Up2n.stl

ibeacon-box_Ui9b3jtOVm.stl

garage-barrier_J5lKPL6CJr.stl

Code

IOTACar

Arduino
#include <Keypad.h>
#include <Servo.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <PubSubClient.h>



const char* ssid = "XXXX";//put your wifi network name here
const char* password = "XXXX";//put your wifi password here
const char* mqtt_server = "test.mosquitto.org";
String vin="WP0CB29858U731930";
bool isAdvertising=false;
long lastAdvertising=0;

WiFiClient espClient;
PubSubClient client(espClient);


void setup(){
  Serial.begin(9600);
  pinMode(D5,OUTPUT);
  digitalWrite(D5,LOW);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}


void setup_wifi() {
    delay(100);
  // We start by connecting to a WiFi network
    Serial.print("Connecting to ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) 
    {
      delay(500);
      Serial.print(".");
    }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
 
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
 
  Serial.print("Message:");
  String message;
  
  for (int i = 0; i < length; i++) {
    //Serial.print((char)payload[i]);
    message+=(char)payload[i];
  }
  Serial.println(message);
  
  if(message=="on"){
    startAdevertising();
  }else{
    stopAdevertising();
  }
  
 
}

void startAdevertising(){
  Serial.println("startAdevertising");
  digitalWrite(D5,HIGH);
  isAdvertising=true;
}

void stopAdevertising(){
  Serial.println("stopAdevertising");
  digitalWrite(D5,LOW);
  isAdvertising=false;
}

void publish(String msg){

    char message[100];
    msg.toCharArray(message,100);
    client.publish("/iotamp/car/WP0CB29858U731930/ibeacon/read", message);
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) 
  {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    //if you MQTT broker has clientID,username and password
    //please change following line to    if (client.connect(clientId,userName,passWord))
    if (client.connect(clientId.c_str()))
    {
      Serial.println("connected");
     //once connected to MQTT broker, subscribe command if any
      client.subscribe("/iotamp/car/WP0CB29858U731930/ibeacon/write");
      
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 6 seconds before retrying
      delay(6000);
    }
  }
} //end reconnect()

 
void loop(){

  if (!client.connected()) {
      reconnect();
    }
    client.loop();

    long diff = millis()-lastAdvertising;

    if(diff> 5000){
      lastAdvertising = millis();
       if(digitalRead(D5)==HIGH){
        publish("on");
      }else{
        publish("off");
      }
 
    }
   

}

service_requester_api.py

Python
import flask
from flask import request, jsonify
from flask_cors import CORS
from iotamp import IndustryMarketplace
import pprint
import sys
import threading
import sqlite3
import json


class ServiceRequesterApi(IndustryMarketplace):
    name = 'Robocops'
    service_provider = False
    fund_wallet = False
    gps_coords = '54.000, 4.000'

    endpoint = 'http://localhost:4000'
    con = sqlite3.connect('requestor-database.db',check_same_thread=False)

    def find_conversation(self, con,conversationId, messageId):
        cursorObj = con.cursor()
        cursorObj.execute('SELECT * FROM CONVERSATION_LOG WHERE id =? and messageId=?',[conversationId,messageId])
        rows = cursorObj.fetchall()
        return len(rows)
    
    def find_conversation_data(self,conversationId, messageId):
        cursorObj = self.con.cursor()
        cursorObj.execute('SELECT * FROM CONVERSATION_LOG WHERE id =? and messageId=?',[conversationId,messageId])
        rows = cursorObj.fetchall()
        return json.loads(rows[0][3])

    def log_conversation(self,con,entity):
        cursorObj = con.cursor()
        cursorObj.execute('INSERT INTO CONVERSATION_LOG(id,messageId,type,data ) VALUES(?, ?, ?,?)', entity)
        con.commit()
    
    def on_proposal(self, data, irdi, submodels):
        '''
        Accept only if the price is between 5 and 15
        '''
        self.log('on proposal called!')
        try:
            #self.log(data)
            price = self.get_price(irdi, submodels)
            beacon = self.get_beacon(irdi, submodels)
            vin = self.get_vin(irdi, submodels)
            location = self.get_location(irdi, submodels)
            userName = data['userName']
            conversationId = data['frame']['conversationId']
            messageId = data['frame']['messageId']
            messageType = data['frame']['type']

            count = self.find_conversation(self.con,conversationId,messageId)
            self.log('Count '+ str(count))

            if count == 0:
                self.log('Received proposal for %si for irdi %s' % (price, irdi))
                self.log_conversation(self.con,(conversationId,messageId,messageType,json.dumps(data)))
                if not price:
                    self.log('Price not found, submodels: %s' % submodels)
            
                self.publish('/iotamp/car/%s/tx' % (vin),json.dumps({'price':price,'type':'prop-recv','user':userName,'conversationId':conversationId,'messageId':messageId,'loc':location,'vin':vin}))
            

            

            # if price >= 5 and price <= 15:
            #     self.log('Accepting proposal')
            #     self.accept_proposal(data)
            # else:
            #     self.log('Rejecting proposal')
            #     self.reject_proposal(data)
        except Exception as e:
            self.log('Error on accepting: %s' % e)

    def on_inform_confirm(self, data, irdi, submodels):

        conversationId = data['frame']['conversationId']
        messageId = data['frame']['messageId']
        messageType = data['frame']['type']
        vin = self.get_vin(irdi, submodels)
        userName = data['userName']
        count = self.find_conversation(self.con,conversationId,messageId)
        self.log('Count '+ str(count))
        if count == 0:
            self.log('Offer confirmed, time to pay')
            self.log_conversation(self.con,(conversationId,messageId,messageType,json.dumps(data)))
            #self.inform_payment(data)
            self.publish('/iotamp/car/%s/pay' % (vin),json.dumps({'user':userName,'conversationId':conversationId,'messageId':messageId}))


app = flask.Flask(__name__)
app.config["DEBUG"] = True
CORS(app)
conn = sqlite3.connect('requestor-database.db',check_same_thread=False)

def find_conversation_data(con,conversationId, messageId):
        cursorObj = con.cursor()
        cursorObj.execute('SELECT * FROM CONVERSATION_LOG WHERE id =? and messageId=?',[conversationId,messageId])
        rows = cursorObj.fetchall()
        return json.loads(rows[0][3])


@app.route('/api/v1/user', methods=['GET'])
def api_user():
    return imp.user()


# 0173-1#02-AAF631#001 - used for VIN
# 0173-1#02-AAO742#002 - used for iBeacon advertisement 

@app.route('/api/v1/parking', methods=['POST'])
def request_for_parking():
    # values = {
    #         "0173-1#02-AAO742#002":"74278BDA-7427-8BDD-8F0C-720EAF059935",
    #         "0173-1#02-AAJ102#003":10,
    #         "0173-1#02-AAJ101#003":84,
    #         "0173-1#02-BAF464#008":True,
    #         "0173-1#02-AAF631#001":"WP0CB29858U731930",
    #         "0173-1#02-AAB733#007":3,
    #         "0173-1#02-AAP397#001":1,
    #         "0173-1#02-BAF163#002":"51.4121278, 13.0547193"

    #     }

    values = {
            "0173-1#02-AAO742#002":request.json['uuid'],
            "0173-1#02-AAJ102#003":10,
            "0173-1#02-AAJ101#003":84,
            "0173-1#02-BAF464#008":True,
            "0173-1#02-AAF631#001":request.json['vin'],
            "0173-1#02-AAB733#007":3,
            "0173-1#02-AAP397#001":1,
            "0173-1#02-BAF163#002":request.json['location']

        }
    

    ret = imp.cfp(irdi='0173-1#01-AAO742#002', values=values, location=request.json['location'])
    return jsonify(ret)

@app.route('/api/v1/proposal/accept', methods=['POST'])
def accept_proposal_manually():
    data = imp.find_conversation_data(request.json['conversationId'],request.json['messageId'])
    ret = imp.accept_proposal(data)

    imp.publish('/iotamp/car/%s/ibeacon/write' % (request.json['vin']),'on')
    return ret

@app.route('/api/v1/proposal/reject', methods=['POST'])
def reject_proposal_manually():
    data = imp.find_conversation_data(request.json['conversationId'],request.json['messageId'])
    return imp.reject_proposal(data)

@app.route('/api/v1/pay', methods=['POST'])
def pay_manually():
    data = imp.find_conversation_data(request.json['conversationId'],request.json['messageId'])
    return imp.inform_payment(data)

if __name__ == '__main__':
    imp = ServiceRequesterApi()
    threading.Thread(target=imp.listen,daemon=False).start()
    app.run(port=5000,threaded=True,debug=False)
    

create_tables.txt

SQL
CREATE TABLE "CONVERSATION_LOG" (
	"id"	TEXT,
	"messageId"	TEXT,
	"type"	TEXT,
	"data"	TEXT,
	"status"	INTEGER DEFAULT 0
);

IOTAGarage

Arduino
#include <SoftwareSerial.h>
#include <CDBLEProx.h>
#include <Servo.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <PubSubClient.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>

#define IRBEAMPIN D7
#define OLED_RESET LED_BUILTIN
Adafruit_SSD1306 display(OLED_RESET);

void ble_event(BLE_PROXIMITY_EVENT eventArgs);
SoftwareSerial sw(D5, D6); // RX, TX
CDBLEProximity ble(&sw, ble_event);

const char* ssid = "xxxx";//put your wifi network name here
const char* password = "xxxx";//put your wifi password here
const char* mqtt_server = "test.mosquitto.org";

String uuid = "xxxxxxxx", conversationId, messageId, topicToPublish;
bool isAdvertising = false;
bool found = false;
bool carParked = false;

WiFiClient espClient;
PubSubClient client(espClient);
Servo myservo;
int irSensorState = 0, irLastState=0;


void setup() {
  Serial.begin(9600);

  myservo.attach(15); //attach the servo on pin D8)
  
  myservo.write(130);

  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  ble.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  printToScreen("Available", 2);
  
  // initialize IR BEAM
  pinMode(IRBEAMPIN, INPUT_PULLUP);     
  digitalWrite(IRBEAMPIN, HIGH); // turn on the pullup
  
}

void loop() {

  

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  ble.update();


  irSensorState = digitalRead(IRBEAMPIN);
  Serial.println(irSensorState);

  if (irSensorState && !irLastState) {
    carParked = false;
    
  } 
  if (!irSensorState && irLastState) {
    carParked = true;
    printToScreen("Car at\ngate",2);
    
  }
  irLastState = irSensorState;

  if (carParked && found && isAdvertising) {
    Serial.print("publish - car found ");
    printToScreen("Requesting\npayment",2);
    isAdvertising = false;
    found = false;
    String payload = "{\"custom\":{\"type\":\"parked\",\"conversationId\":\"" + conversationId + "\",\"messageId\":\"" + messageId + "\"}}";
    Serial.println(payload);

    publish(payload);
  }
}

void setup_wifi() {
  delay(100);
  // We start by connecting to a WiFi network
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {

  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  Serial.print("Message:");
  String message;
  String topicAsString = String(topic);

  for (int i = 0; i < length; i++) {
    //Serial.print((char)payload[i]);
    message += (char)payload[i];
  }
  Serial.println(message);

  if (topicAsString == "/iotamp/garage/AXPD/awaiting") {

    conversationId = str_token(message, '|', 0);
    messageId = str_token(message, '|', 1);
    topicToPublish = str_token(message, '|', 2);

    Serial.println("Going to publish message :");
    Serial.println(conversationId);
    Serial.println(messageId);
    Serial.println(topicToPublish);



  }

  if (topicAsString == "/iotamp/garage/AXPD/beacon") {
    uuid = message;
    isAdvertising = true;
    Serial.print("Setting UUID:"); Serial.println(uuid);
    printToScreen("Reserved",2);
  }

  if (topicAsString == "/iotamp/garage/AXPD/payrcvd") {
    printToScreen("Payment\nreceived",2);
    myservo.write(15); //rotates the motor counterclockwise at slow speed
    delay(5000);
    myservo.write(130);
    printToScreen("Occupied",2);
  }
  
}

void publish(String msg) {

  char message[300];
  msg.toCharArray(message, 300);

  client.publish("/iotamp/garage/AXPD/payment", message);
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected())
  {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    //if you MQTT broker has clientID,username and password
    //please change following line to    if (client.connect(clientId,userName,passWord))
    if (client.connect(clientId.c_str()))
    {
      Serial.println("connected");
      //once connected to MQTT broker, subscribe command if any
      client.subscribe("/iotamp/garage/AXPD/awaiting");
      client.subscribe("/iotamp/garage/AXPD/beacon");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 6 seconds before retrying
      delay(6000);
    }
  }
} //end reconnect()






void ble_event(BLE_PROXIMITY_EVENT eventArgs) {
  Serial.print("Looking for UUID:"); Serial.println(uuid);

  if (eventArgs.eventID == BLE_EVENT_ON_DEVICE_LOST) {
    Serial.println("No device");
    Serial.println("");
  }
  if (eventArgs.eventID == BLE_EVENT_ON_DEVICE_APPROACH ) {
    //        Serial.println("New device");
    Serial.print("UUID: "); Serial.println(eventArgs.device.address);
    //        Serial.print("MAC: "); Serial.println(eventArgs.device.mac);
    //        Serial.print("HL : "); Serial.println(eventArgs.device.hilo);
    //        Serial.print("HI : "); Serial.println(eventArgs.device.hi);
    //        Serial.print("LO : "); Serial.println(eventArgs.device.lo);
    //        Serial.print("SIG: "); Serial.println(eventArgs.device.rssi);
    //        Serial.println("");
    if (!found && isAdvertising && eventArgs.device.address == uuid) {
      found = true;
      Serial.println("### Tracked. New device ");
    }


  }
  if (eventArgs.eventID == BLE_EVENT_ON_DEVICE_MOVED ) {

    //        Serial.println("Device moved");
    Serial.print("UUID: "); Serial.println(eventArgs.device.address);
    //        Serial.print("MAC: "); Serial.println(eventArgs.device.mac);
    //        Serial.print("HL : "); Serial.println(eventArgs.device.hilo);
    //        Serial.print("HI : "); Serial.println(eventArgs.device.hi);
    //        Serial.print("LO : "); Serial.println(eventArgs.device.lo);
    //        Serial.print("SIG: "); Serial.println(eventArgs.device.rssi);
    //        Serial.println("");

    if (!found && isAdvertising && eventArgs.device.address == uuid) {
      found = true;
      Serial.println("### Tracked. Device moved.");
    }
  }


}

String str_token(String data, char separator, int index)
{
  int found = 0;
  int strIndex[] = { 0, -1 };
  int maxIndex = data.length() - 1;

  for (int i = 0; i <= maxIndex && found <= index; i++) {
    if (data.charAt(i) == separator || i == maxIndex) {
      found++;
      strIndex[0] = strIndex[1] + 1;
      strIndex[1] = (i == maxIndex) ? i + 1 : i;
    }
  }
  return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

void printToScreen(String message, int txtSize) {
  display.clearDisplay();
  display.setTextSize(txtSize);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println(message);
  display.display();
}

service_requester.py

Python
from imp import IndustryMarketplace
import pprint
import sys

class ServiceRequester(IndustryMarketplace):
    name = 'ProSumer'
    service_provider = False
    fund_wallet = True
    gps_coords = '54.000, 4.000'

    endpoint = 'http://localhost:4000'
    
    def on_proposal(self, data, irdi, submodels):
        '''
        Accept only if the price is between 5 and 15
        '''
        self.log('on proposal called!')
        try:
            price = self.get_price(irdi, submodels)
            self.log('Received proposal for %si for irdi %s' % (price, irdi))
            if not price:
                self.log('Price not found, submodels: %s' % submodels)

            if price >= 5 and price <= 15:
                self.log('Accepting proposal')
                self.accept_proposal(data)
            else:
                self.log('Rejecting proposal')
                self.reject_proposal(data)
        except Exception as e:
            self.log('Error on accepting: %s' % e)

    def on_inform_confirm(self, data, irdi, submodels):
        self.log('Offer confirmed, time to pay')
        self.inform_payment(data)


if __name__ == '__main__':

    imp = ServiceRequester()
    
    # Either run it as a listeing service
    if len(sys.argv) == 1:
        imp.listen()
    
    # Or as a one time command requesting a drone!
    if len(sys.argv) == 2 and sys.argv[1] == 'request_drone':

        values = {
            '0173-1#02-AAJ336#002': 2,
            '0173-1#02-BAF163#002': '54.1234, 4.3210',
            '0173-1#02-AAO631#002': '54.4321, 4.5210',

        }

        ret = imp.cfp(irdi='0173-1#01-AAJ336#002', values=values, location='54.321, 4.123')
        #pprint.pprint(ret)
    
    if len(sys.argv) == 2 and sys.argv[1] == 'drone_inspection':

        values = {
            '0173-1#02-AAP788#001': 2,
            '0173-1#02-AAY979#001': 10,
            '0173-1#02-BAF163#002': '54.4321, 4.5210',
        }

        ret = imp.cfp(irdi='0173-1#01-AAP788#001', values=values, location='54.321, 4.123')
        pprint.pprint(ret)

    if len(sys.argv) == 2 and sys.argv[1] == 'ev_charging':

        values = {
            "0173-1#02-AAO742#002":"74278BDA-7427-8BDD-8F0C-720EAF059935",
            "0173-1#02-AAJ102#003":10,
            "0173-1#02-AAJ101#003":84,
            "0173-1#02-AAF631#001":"kthxhkjkxf",
            "0173-1#02-AAB733#007":3,
            "0173-1#02-AAP397#001":1,
            "0173-1#02-BAF163#002":"51.4121278, 13.0547193"

        }

        ret = imp.cfp(irdi='0173-1#01-AAO742#002', values=values, location='54.321, 4.123')

Credits

Mithun Das

Mithun Das

34 projects • 180 followers
Hacker and Maker driven by passion. Ambassador at Edge Impulse and Balena. Follow me on Twitter @_mithundas

Comments