Today I made a BLE RC car

Part 4 — Adding ultrasonic sensors (Just for fun) and burning the Car before its first run

gal brandwine
Machines talk, we tech.
5 min readJul 24, 2023

--

In the previous post

  • I shared the BLE remote-controlled car’s design, implementation, and testing.
  • Eventually, I built a client running on my laptop, controlling the Car using BLE GATT characteristics I’ve implemented. Enabling sending basic commands like forward, backward, left, and right)

The extra mile

Basically, the project is completed, a working BLE RC car, juuuuuust as promised.

But I feel like a grand finale is missing, so here’ it is — I’ve added Ultrasonic sensors so the Car could have some basic environmental awareness!

These are the notorious hc-sr04 ultra-sonic distance sensors, and I won’t get into the deets. But it’s pretty simple — one “eye” transmits a sound wave, and the other “eye” receives it:

  1. Roundtrip time is being measured.
  2. The speed of sound is known.
  3. Viola! we can calculate the distance (reminder: x = v/t)
  4. :)

NOTE:
This sensor works with 5v, but the ESP32 GPIOs can handle 3.3v [debatable], so I needed to implement a simple voltage divider on the echo pin (The one that sends back data to the ESP32 data)

Using a 470 Ohm resistor and 1k Ohm resistor, I could reduce 5V to ~3.3V.

I also needed to cut the original line to the echo pin, so my voltage divider could do the job:

Here’s what the whole schematics look like:

This Car needs three different voltages to operate! (12v in, 3.3v for the ESP32, 5v for the US sensors)

Expanding the <<IController>> to expose distance reading (Server side)

Remember, this interface ensures a BLE client can access all the BLE server (i.e., the Car) provides, without

Since now the Car can measure distances, wouldn’t it be great to expose those readings to the user? I thought so!

Luckily, it took minor changes to apply. See images descriptions

Adding to <<IController>> the new GetDistanceMeasurements so all users could get this data.
Modified Car to have two UltraSonicDistanceSensors objects

Modifying BLEManager component:

UML changes description:

  • New BLE characteristics “Distance_Measurements
  • A new task that runs at 10hz, samples Cars Controller for new distance readings (first line in purple)
  • Write those samples to the new Characteristic (2nd line in purple):

The diagrams above have this loop (purple) This is how it is done:

/*
looping in 10hz,
trying to read sensor measurements and notifying the BLE characteristics.
In ESP32, we can run multiple "tasks" that can run in "parallel".
*/
void ConnectivityLEDStuff_t(void *pvParameters)
{
int TICKS = 100;
auto pBLEManager = (BLEManager *)(pvParameters);
auto led = pBLEManager->GetContext().LED_PIN;
for (;;)
{
if ((pBLEManager->GetContext()).IsDeviceConnected)
{
// Client connected, turn the LED on
digitalWrite(led, HIGH);
DriveMode current_drive_mode{DriveMode::Unsupported};
/**
* @brief Send distance measurements in 10hz
*/
const TickType_t xDelay = 100 / portTICK_PERIOD_MS;
while ((pBLEManager->GetContext()).IsDeviceConnected)
{
auto tmp_drive_mode = pBLEManager->GetContext().Controller->CurrentDriveMode();
if (current_drive_mode != tmp_drive_mode)
{
current_drive_mode = tmp_drive_mode;
BLEDrivePacket p{current_drive_mode, 0};
// Notify to connected BLE clients that there's new driveMode
pBLEManager->NotifyNewDriveMode(p);
}
// Get current distance from all sensors
auto distanceMeasurements = pBLEManager->GetContext().Controller->GetDistanceMeasurements();
// Notify connected client about new distance readings
pBLEManager->NotifyNewDistanceRead(distanceMeasurements);
vTaskDelay(xDelay);
}
Serial.println("Controller disconnected");
pBLEManager->GetContext().Controller->SetDriveMode(DriveMode::Stop);
}
/**
* @brief Blink while waiting for connection
*/
else
{
while (!(pBLEManager->GetContext()).IsDeviceConnected)
{
blinkLed();
}
}
vTaskDelay(pdMS_TO_TICKS(TICKS * 10));
}
vTaskDelete(NULL);
}

(Client-side)

This was easy — just add another characteristic callback:

void BLERCCar_client::attachDistanceMeasurementsCallback()
{
auto front_left_distance_reading_characteristic = m_CharMap[CHARACTERISTIC_UUID_FRONT_LEFT_DISTANCE_CM].second;
front_left_distance_reading_characteristic->set_on_value_changed([&](SimpleBluez::ByteArray leftSensorReadings)
{
memcpy(&m_DistanceMeasurements.FrontLeft, leftSensorReadings.c_str(), sizeof(double));
// spdlog::debug("leftSensorReadings: {}", m_DistanceMeasurements.FrontLeft);
});
front_left_distance_reading_characteristic->start_notify();
}

That’s it.
Every time the Server notifies there’s a new distance reading. The client will get a callback and get those readings ASAP — amazing, right?

So now I have a BLE RC Car that also sends back environmental sensing in 10hz. From what I saw in my tests, there’s almost no latency — it depends on distance and sensor reading success rate.

For the experienced amongst us — I can already hear the wheels of new autonomous car projects turning. I hear ya.

Here’s the outcome of this 6-month project:

Meet the Beast!

I Think This Just Might Be My Masterpiece
“I Think This Just Might Be My Masterpiece!”

The Bad News

The Car I found in the trash had two cheap DC motors that were supposed to run on 3V tops:

But the HW-095 motor driver needs at least 7v to operate the DC motor outputs, so eventually, I burned the motor :(

I had two options here:

A. Buy a new motor — it is cheap and available in Ali-Express. But they still won’t run as expected because they operate over low voltage and can’t take much load.

B. Put this project aside until I get a better DIY robotic chassis.

I chose option B.
But that will wait for next time.

Sum-up

Another one bites to dust :)

In this project, I’ve experienced firsthand the difficulties of transitioning from R&D to production:

  • The Car functioned like a charm while all the electronics spread on my workbench — cable was ordered- and everything was clean and tidy.
  • Once, I stuffed everything inside the car chassis — surprisingly, nothing worked.
  • Also, not knowing the full spec of our components will eventually destroy the whole product, in my case, burn one of the DC motors.

I did all that [writing a 4-posts-miniseries, giving up sleep hours — soldering, coding, testing, reading, learning] only because I was curious about the technology my workplace uses, which I knew nothing about (the BLE used for communicating with our sensors).

I challenge you!
Go out there, leave your comfort zone, and find some dark corners in your day-to-day lives, areas you know nothing about but use relentlessly, investigate them, learn how they work, and shed some light on the dark!

It is rewardful. I promise.

See you next time.
Cheers, Gal.

--

--