Prototyping a Wireless Controllable Entrance/Exit Gate Controller Using an ESP32 and a Servo Motor

この記事には日本語版があります。

Introduction

There is an Amazon Go like cashless shopping area(we call it a "walk-through") in "Developers.IO CAFE" operated by Classmethod, Inc. We have installed no bars or flappers at the entrance and the exit of the "walk-through" so far. Though it gave good opened impressions to our customers that no existence of bars or flappers, sometimes the customers confused which is the entrance and which is the exit. In this blog post, I will explain how I prototyped an ultra-simple wireless controllable entrance/exit gate controller by a servo motor and an ESP32 for making sure whether it is effective for good customer experiences or not.

Concept

  • Attach a bar made of Styrofoam to a servo motor. The servo rotates the bar 90 degrees.
    • The horizontal state of the bar indicates the gate is opened, and the vertical state of the bar indicates the gate is closed.
  • A microcontroller
  • A servo motor
  • A styrofoam bar
  • Do not connect to the cafe's "walkthrough" system
    • This time, I want to make a working prototype at the fastest speed, so I don't link it with the cafe's walk-through system.
      • Manually open and close via BLE

Prototyping

Torque calculation

The styrofoam bar used to indicate the open and closed state of the gate weighs about 20g for 500mm. Assuming a load of 20g is applied to the end of a 500mm bar when the bar is horizontal, the required torque is 0.02kgf x 0.5m = 0.01kgfm. According to the specifications of the servo motor, its torque is 4.6 kgfm, which seems to be enough.

Controlling a servo motor by PWM

ESP32 has functions with the prefix ledc~~ for controlling LED by PWM. Since there is no difference between the LED and the servo motor in terms of PWM control, the functions can be used for controlling the servo motor. I have confirmed that PWM control of servo motor can be dynamically performed using simple CUI via serial port introduced in Sakaji's blog post.

Working code sample

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

#define MIN 1
#define MAX 30
int n;
int gate_open;

BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
#define SERVICE_UUID        "YOUR OWN SERVICE UUID"
#define CHARACTERISTIC_UUID "YOUR OWN CHARACTERISTIC UUID"
String bleSensorName = "YOUR OWN BLE DEVICE NAME";

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      Serial.println("onWrite");
      std::string value = pCharacteristic->getValue();
      if (value.length() > 0) {
        Serial.println("*********");
        Serial.print("New value: ");
        for (int i = 0; i < value.length(); i++)
          Serial.print(value[i]);
        Serial.println();
        Serial.println("*********");
      }
      if (gate_open == 0) {
        n = 15;  // 90°
        gate_open = 1;  // GATE CLOSED
      } else {
        n = 1;  // 0°
        gate_open = 0;  // GATE OPENED
      }
    }
};

void setup() {
  Serial.begin(115200);
  Serial.print("> ");

  ledcSetup(0, 50, 8);  // 0ch 50 Hz 8bit resolution
  ledcAttachPin(15, 0); // 15pin, 0ch
  n = MIN;
  gate_open = 0;

  BLEDevice::init((char *)bleSensorName.c_str());
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());
  BLEService *pService = pServer->createService(SERVICE_UUID);
  pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_WRITE
                    );
  pCharacteristic->setCallbacks(new MyCallbacks());
  // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
  pCharacteristic->addDescriptor(new BLE2902());
  pService->start();
  pServer->getAdvertising()->start(); // start advertising
  Serial.println("Setup completed. starting advertising...");
}

void loop() {
  if (Serial.available() > 0) {
    String s;
    s = Serial.readStringUntil('\n');
    Serial.println(s);
    int new_n = menu(s);
    if (new_n != -1) n = new_n;
  }

  if (n > MAX) n = MAX;
  if (n < MIN) n = MIN;
  ledcWrite(0, n);

  // disconnecting
  if (!deviceConnected && oldDeviceConnected) {
      delay(500); // give the bluetooth stack the chance to get things ready
      pServer->startAdvertising(); // restart advertising
      Serial.println("start advertising");
      oldDeviceConnected = deviceConnected;
  }
  // connecting
  if (deviceConnected && !oldDeviceConnected) {
      oldDeviceConnected = deviceConnected;
  }
}

int menu(String s) {
  if (s.equals("?")) {
    Serial.println("entrance-exit-gate-controller");
    Serial.print("> ");
    return -1;
  } else if (s.equals("ver")) {
    Serial.println("0.0.1");
    Serial.print("> ");
    return -1;
  } else {
    Serial.println(s);
    Serial.print("> ");
    return s.toInt();
  }
}

Hardware assemble

In this prototype, it is OK if the minimum operation is confirmed, so I assembled it extremely roughly as follows. The servo motor's power(5V) was supplied from the ESP32-DevKitC 5V output pin.

Function check

The following is a video at the time of the function check. Since it is not linked with the walkthrough system, opening and closing the gate is performed by writing the GATT characteristic of the ESP32 via a BLE client application at hand.

Conclusion

In this blog post, I have shown how I prototyped the ultra simple entrance/exit gate which is controlled wirelessly. I would like to complete the following tasks and would like to observe whether it is effective for good customer experiences or not.

  • Link to the walk-through system
  • Fix the servo motor and the post of the belt partition more firmly
  • Make the gate open/close bar look cooler

Reference