3D Model: Download here

Electronics parts (Buy link on my Youtube video):

  • 1x ESP32C3 Super Mini
  • 2x 6mm motor with 2-stage gearbox
  • 1x 6mm motor with 3-stage gearbox
  • 3x Driver RZ7888
  • 1x 50mah Lipo battery

 

Others:

  • 40x 1mm screw
  • 2x bearings 1x3x1

 

Android App: https://thehlab.org/products

Arduino code:

 


/*
* author: The H Lab
* Board Name: ESP32 C3 SuperMini
* Board selected in Arduino IDE: ESP32C3 Dev Module
* Board Manager: esp32 by Espressif Systems
*/

#include 
#include 
#include 
#include 

char command;
String string;
int svangle = 0;
int slideBarValue = 50;
int hindex = 0;
String aCmd;

int speeds = 0;//STOP_SPEED;
int gear = 0;
int turn_blink_state = LOW;
int turn_status_cmd = 0;
long previousMillis = 0;
int cDeepSleepButton = 0;

#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

#define PULSE_WIDTH 20
#define MOTOR1A 10
#define MOTOR1B 20
#define MOTOR2A 9
#define MOTOR2B 8
#define MOTOR3A 1
#define MOTOR3B 2
int pulse_count = 0;
int pulse_high1 = 0; // speed 0 to 10 (PULSE_WIDTH)
bool direction1 = false;
int pulse_high2 = 0; // speed 0 to 10 (PULSE_WIDTH)
bool direction2 = false;
int pulse_high3 = 0;
bool direction3 = false;

hw_timer_t *My_timer = NULL;
void IRAM_ATTR onTimer() {
  pulse_count++;
  if ( pulse_count >= PULSE_WIDTH ){
    pulse_count = 0;
  }
  // motor1
  if ( pulse_count < pulse_high1 ){
    if ( direction1 ){
      digitalWrite(MOTOR1A, LOW);
      digitalWrite(MOTOR1B, HIGH);
    } else {
      digitalWrite(MOTOR1A, HIGH);
      digitalWrite(MOTOR1B, LOW);
    }
  } else {
    digitalWrite(MOTOR1A, LOW);
    digitalWrite(MOTOR1B, LOW);
  }
  // motor2
  if ( pulse_count < pulse_high2 ){
    if ( direction2 ){
      digitalWrite(MOTOR2A, LOW);
      digitalWrite(MOTOR2B, HIGH);
    } else {
      digitalWrite(MOTOR2A, HIGH);
      digitalWrite(MOTOR2B, LOW);
    }
  } else {
    digitalWrite(MOTOR2A, LOW);
    digitalWrite(MOTOR2B, LOW);
  }
  // motor3
  if ( pulse_count < pulse_high3 ){
    if ( direction3 ){
      digitalWrite(MOTOR3A, LOW);
      digitalWrite(MOTOR3B, HIGH);
    } else {
      digitalWrite(MOTOR3A, HIGH);
      digitalWrite(MOTOR3B, LOW);
    }
  } else {
    digitalWrite(MOTOR3A, LOW);
    digitalWrite(MOTOR3B, LOW);
  }
}

BLECharacteristic *pCharacteristic;
void setupBLE() {
  BLEDevice::init("ESP32");
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  pCharacteristic = pService->createCharacteristic(
    CHARACTERISTIC_UUID,
    BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);

  //pCharacteristic->setValue("Hello World");
  pService->start();
  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
}

void setup() {
//  esp_sleep_wakeup_cause_t wakeup_reason;
//  wakeup_reason = esp_sleep_get_wakeup_cause();

  //esp_sleep_enable_ext0_wakeup(WAKE_UP_PIN, 0); //1 = High, 0 = Low
  //esp_deep_sleep_enable_gpio_wakeup(1 << 9, ESP_GPIO_WAKEUP_GPIO_HIGH);

  // Serial.begin( 115200 );

  setupBLE();
  //Serial.println("OK!");

  //Timer
  My_timer = timerBegin(0, 80, true);
  timerAttachInterrupt(My_timer, &onTimer, true);
  timerAlarmWrite(My_timer, 1000, true); // 1000 call per sec
  timerAlarmEnable(My_timer); //Just Enable

  pinMode(MOTOR1A, OUTPUT);
  pinMode(MOTOR1B, OUTPUT);
  pinMode(MOTOR2A, OUTPUT);
  pinMode(MOTOR2B, OUTPUT);
  pinMode(MOTOR3A, OUTPUT);
  pinMode(MOTOR3B, OUTPUT);

  //Serial.println("Setup done!");
  
}

void loop() {
  //delay(2000);
  
  string = "";
  if (pCharacteristic->getLength() > 0) {
    std::string s = pCharacteristic->getValue();
    string += s.c_str();
  }
  // Serial.println(string);
  while (string.length() >= 3) {
    aCmd = string.substring(0, 3);
    string = string.substring(3);

    if (aCmd.lastIndexOf("A") == 0) {
      speeds = aCmd.substring(1).toInt();
      if (speeds >= 0) {
        //sgo1.writeMicroseconds(map(speeds, 0, 100, MAX_SPEED_BACK, MAX_SPEED_GO));
        if ( speeds > 50 ) direction1 = true;
        else direction1 = false;
        pulse_high1 = abs(50 - speeds)/5;
        delay(5);
      }
    } else if (aCmd.lastIndexOf("B") == 0) {
      speeds = aCmd.substring(1).toInt();
      if (speeds >= 0) {
        //sgo2.writeMicroseconds(map(speeds, 0, 100, MAX_SPEED_BACK, MAX_SPEED_GO));
        if ( speeds > 50 ) direction2 = true;
        else direction2 = false;
        pulse_high2 = abs(50 - speeds)/5;
        delay(5);
      }
    } else if (aCmd.lastIndexOf("C") == 0) {
      speeds = aCmd.substring(1).toInt();
      if (speeds >= 0) {
        //sgo1.writeMicroseconds(map(speeds, 0, 100, MAX_SPEED_BACK, MAX_SPEED_GO));
        if ( speeds > 50 ) direction3 = true;
        else direction3 = false;
        pulse_high3 = abs(50 - speeds)/10;
        delay(5);
      }
    }
  }
}


Recommendations