PHP2Shell alert: critical PHP flaws CVE-2026-109618 / 109619 — check your servers and get patched fast
HashTech Systems
EV Charging
Part 10 of 12

OCPP 1.6 Firmware Updates and Diagnostics: UpdateFirmware Guide

Remote firmware updates and log collection in OCPP 1.6: UpdateFirmware, FirmwareStatusNotification, GetDiagnostics and how to roll out safely.

HashTech Engineering · October 26, 2026 · 5 min read

Every charger fleet eventually needs a firmware update pushed to it, and every support team eventually needs logs from a charger that is misbehaving at a site three hours away. OCPP 1.6 covers both in the Firmware Management feature profile with four messages. This is part 10 of the series. The signed-firmware variant from the security whitepaper is covered in part 11.

UpdateFirmware

json
[
  2,
  "csms-fw-601",
  "UpdateFirmware",
  {
    "location": "https://firmware.example.com/vendorx/dcf60/3.5.0.bin",
    "retrieveDate": "2026-10-27T02:00:00Z",
    "retries": 3,
    "retryInterval": 600
  }
]
json
[3, "csms-fw-601", {}]
FieldRequiredNotes
locationYesURI the charger downloads the firmware from
retrieveDateYesWhen the charger should start downloading; a time in the past means now
retriesNoHow many download attempts; charger default if omitted
retryIntervalNoSeconds between attempts

The response is empty. The charger cannot refuse in the response, so everything you learn about progress comes from FirmwareStatusNotification messages afterwards. What the charger supports in location is up to the vendor: HTTP, HTTPS and FTP are all common, and some chargers only support one. Some do not follow HTTP redirects, which breaks pre-signed cloud storage URLs that redirect; test the exact URL form you plan to use.

FirmwareStatusNotification

json
[2, "fs-1", "FirmwareStatusNotification", {"status": "Downloading"}]
[3, "fs-1", {}]
[2, "fs-2", "FirmwareStatusNotification", {"status": "Downloaded"}]
[3, "fs-2", {}]
[2, "fs-3", "FirmwareStatusNotification", {"status": "Installing"}]
[3, "fs-3", {}]
statusMeaning
DownloadingDownload in progress
DownloadedDownload complete
DownloadFailedDownload failed after all retries
InstallingInstallation in progress
InstalledNew firmware installed
InstallationFailedInstallation failed
IdleNot busy with a firmware update; only sent in response to TriggerMessage

The message carries only status. There is no request identifier, so if you send two UpdateFirmware requests to the same charger you cannot tell which status belongs to which. Only run one update per charger at a time.

The full sequence

text
CSMS -> CP  UpdateFirmware {location, retrieveDate}
CP -> CSMS  UpdateFirmware.conf {}
(at retrieveDate)
CP -> CSMS  FirmwareStatusNotification {status: "Downloading"}
CP -> CSMS  FirmwareStatusNotification {status: "Downloaded"}
(charger waits for ongoing transactions to finish, depending on vendor)
CP -> CSMS  FirmwareStatusNotification {status: "Installing"}
(charger reboots)
CP -> CSMS  BootNotification {firmwareVersion: "3.5.0", ...}
CP -> CSMS  FirmwareStatusNotification {status: "Installed"}

The exact ordering of Installed and the reboot varies. Some chargers send Installed before rebooting, some after, some never. The most reliable confirmation is the firmwareVersion in the next BootNotification (part 2). Record the version you expect and compare when the charger boots.

The 1.6 specification expects a charger with ongoing transactions to wait for them to finish before installing, or to stop them, rather than interrupting charging silently. Firmware behaviour varies, so schedule updates for quiet hours, and if a vendor's charger is known to interrupt sessions, set the connectors to Inoperative with ChangeAvailability (part 7) once the current session ends.

Checking progress and detecting stalls

Firmware status messages are not guaranteed to arrive. A charger can lose connectivity during the download, or reboot into the new firmware without having sent Installing. If an update seems stuck, ask for the current state:

json
[2, "csms-tm-610", "TriggerMessage", {"requestedMessage": "FirmwareStatusNotification"}]
[3, "csms-tm-610", {"status": "Accepted"}]
[2, "fs-9", "FirmwareStatusNotification", {"status": "Idle"}]
[3, "fs-9", {}]

Idle means the charger is not currently processing a firmware update. Combined with the firmwareVersion from its last boot, that tells you whether the update finished or never started. Set a deadline per stage (for example, download complete within an hour of retrieveDate, boot with the new version within an hour of Installing) and flag chargers that miss it.

Rolling out firmware across a fleet

  1. Test the new firmware on bench hardware and one or two field chargers you can reach. Run a full regression: boot, authorize, start, meter values, stop, smart charging, offline queueing. A simulator helps for the CSMS side (testing with a simulator).
  2. Diff the full GetConfiguration output before and after the update (part 8). Firmware updates sometimes reset keys to defaults or rename vendor keys.
  3. Roll out in waves (for example, a small percentage of chargers per day), with a hold between waves and automatic pausing if failure rates rise.
  4. Track every charger's state: requested, downloading, downloaded, installing, booted with expected version, failed. A charger that stops reporting after Installing is your top priority.
  5. Keep the previous firmware image available. Whether a rollback is possible remotely is vendor-specific, so find out before you need it.

GetDiagnostics

GetDiagnostics asks the charger to upload a diagnostics file to a location you specify.

json
[
  2,
  "csms-gd-602",
  "GetDiagnostics",
  {
    "location": "ftp://diag-upload:[email protected]/uploads/",
    "retries": 2,
    "retryInterval": 120,
    "startTime": "2026-10-25T00:00:00Z",
    "stopTime": "2026-10-26T12:00:00Z"
  }
]
json
[3, "csms-gd-602", {"fileName": "CP-0001_diag_20261026.tar.gz"}]

location is required and is a directory URI; the charger chooses the file name and returns it in fileName. If fileName is absent, there is no diagnostics information available. startTime and stopTime optionally narrow the time window. The format and contents of the file are not defined by OCPP; expect anything from a plain text log to an encrypted vendor archive that only the manufacturer can read.

DiagnosticsStatusNotification

json
[2, "ds-1", "DiagnosticsStatusNotification", {"status": "Uploading"}]
[3, "ds-1", {}]
[2, "ds-2", "DiagnosticsStatusNotification", {"status": "Uploaded"}]
[3, "ds-2", {}]

Status values are Idle (only in response to TriggerMessage), Uploading, Uploaded and UploadFailed. Like firmware status, there is no request identifier, so run one diagnostics request at a time per charger.

Practical upload setup

  • FTP remains the most widely supported upload protocol on 1.6 chargers. If you must support it, run it on an isolated host with per-upload credentials and move files elsewhere promptly. Prefer HTTPS or FTPS where the charger supports them.
  • Some chargers use HTTP PUT, others POST with multipart form data. Find out per model and build the upload endpoint to accept what your fleet sends.
  • Diagnostics files can be large, and cellular data plans are not. Use startTime and stopTime to limit the window.
  • Index uploaded files against the charger identity and a support ticket, so the next engineer can find them.

Key takeaways

  • UpdateFirmware has an empty response; progress comes only through FirmwareStatusNotification.
  • Confirm success through the firmwareVersion in the next BootNotification, not just Installed.
  • Neither firmware nor diagnostics status messages carry a request ID: one operation per charger at a time.
  • Roll out in waves, diff configuration before and after, and know your rollback story.

Previous: Part 9, smart charging. Next: Part 11, security profiles. Remote recovery and fleet health are covered further in EV charger uptime and reliability.

Let's Talk

Ready to build something that lasts?

Whether it's a charging network, an AI rollout, or a core enterprise system — let's talk about what you're building.