Hydraulic moving floor (BLE app)ESP32 PLC 38RBLEInfrastructure
Syncing the PLC real-time clock from a mobile app over BLE
A machine with no internet cannot call NTP, yet its event log still needs real dates. The trick from this real mobile hydraulic machine deployment: the operator's smartphone always knows the time, so the mobile app sends it. This ESP32 RTC sync example receives a
[D]timestamp_ms* frame over BLE UART and fixes the PLC clock with settimeofday() — from that moment every counter, alarm and record carries a true timestamp.The phone as time source
On every connection, the mobile app writes the current Unix epoch in milliseconds to the BLE RX characteristic as a
[D]timestamp_ms* frame. Since operators and technicians connect at least once per working session, the clock gets refreshed far more often than it drifts, and the ESP32's internal RTC keeps ticking between visits. No GPS module, no NTP server, no cellular modem — zero extra hardware on the bill of materials.Parsing the timestamp safely
Epoch milliseconds exceed 32 bits, so the frame is parsed with
strtoull into a 64-bit integer — using toInt() here is a classic silent-overflow bug. A sanity check rejects any value earlier than 2020, protecting the clock from a malformed frame or a buggy app build. Once validated, settimeofday() updates the system clock and every standard time() and strftime() call in the firmware just works.Test it before the app exists
Because the protocol is plain ASCII over the Nordic UART service, you can sync the clock from nRF Connect by writing the frame manually. The example notifies the current PLC time every five seconds, so you immediately see the jump from "RTC SIN SYNC" to a correct date — the same pattern used by the full machine-control firmware in this series.
A snippet from the implementation
Straight from the example as deployed on the ESP32 PLC 38R — copy it freely:
void setup() {
Serial.begin(115200);
BLEDevice::init("PLC-HIDRAULICO");
BLEServer *server = BLEDevice::createServer();
server->setCallbacks(new ServerCB());
BLEService *svc = server->createService(SERVICE_UUID);
txChar = svc->createCharacteristic(CHAR_TX_UUID, BLECharacteristic::PROPERTY_NOTIFY);
txChar->addDescriptor(new BLE2902());
BLECharacteristic *rxChar =
svc->createCharacteristic(CHAR_RX_UUID, BLECharacteristic::PROPERTY_WRITE);
rxChar->setCallbacks(new RxCB());
svc->start();
server->getAdvertising()->start();
Serial.println("BLE advertising. Waiting for [D]timestamp_ms* frame ...");
}The full example is a complete program — wiring header, setup and main loop — ready to adapt to your application.
Frequently asked questions
Does the ESP32 keep the time after a power cycle?
No, the internal clock resets when power is removed unless you add a battery-backed external RTC chip. In practice the app re-syncs on every connection, which covers most mobile machine scenarios.
Why send milliseconds instead of seconds?
Phone APIs natively produce epoch milliseconds, and the extra resolution lets the PLC order events that happen within the same second. The firmware divides by 1000 for the seconds field anyway.
How do I handle time zones?
Send UTC from the app and store UTC in the PLC. Convert to local time only for display, either in the app or with the TZ environment variable on the ESP32.