<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Serkan ÇAKMAK</title>
    <description>The latest articles on DEV Community by Serkan ÇAKMAK (@ureticy).</description>
    <link>https://dev.to/ureticy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4168090%2F35f64a2f-7989-4c04-9df3-311d4de75164.jpg</url>
      <title>DEV Community: Serkan ÇAKMAK</title>
      <link>https://dev.to/ureticy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ureticy"/>
    <language>en</language>
    <item>
      <title>Building an OCPP Backend: Lessons from Supporting OCPP 1.6J and OCPP 2.0.1 in Production</title>
      <dc:creator>Serkan ÇAKMAK</dc:creator>
      <pubDate>Wed, 07 Oct 2026 06:59:32 +0000</pubDate>
      <link>https://dev.to/ureticy/building-an-ocpp-backend-lessons-from-supporting-ocpp-16j-and-ocpp-201-in-production-47oa</link>
      <guid>https://dev.to/ureticy/building-an-ocpp-backend-lessons-from-supporting-ocpp-16j-and-ocpp-201-in-production-47oa</guid>
      <description>&lt;p&gt;Building an OCPP backend looks simple at first.&lt;/p&gt;

&lt;p&gt;A charger opens a WebSocket connection, sends a &lt;strong&gt;BootNotification&lt;/strong&gt;, the backend responds, and you're connected.&lt;/p&gt;

&lt;p&gt;Then you connect &lt;strong&gt;real chargers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's where things get interesting.&lt;/p&gt;

&lt;p&gt;While building and testing &lt;strong&gt;Ureticy&lt;/strong&gt;, our OCPP software platform, we've worked with real EV charging hardware using both &lt;strong&gt;OCPP 1.6J&lt;/strong&gt; and &lt;strong&gt;OCPP 2.0.1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The biggest lesson?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Supporting OCPP is not the same thing as supporting real OCPP chargers.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here are some of the lessons we've learned from production.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. BootNotification is only the beginning
&lt;/h2&gt;

&lt;p&gt;A successful &lt;strong&gt;BootNotification&lt;/strong&gt; feels great.&lt;/p&gt;

&lt;p&gt;The charger connects.&lt;br&gt;&lt;br&gt;
The backend accepts it.&lt;br&gt;&lt;br&gt;
Everything looks good.&lt;/p&gt;

&lt;p&gt;But that only proves the beginning of the communication works.&lt;/p&gt;

&lt;p&gt;The real problems usually appear later:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Connector status&lt;/li&gt;
&lt;li&gt;Transaction handling&lt;/li&gt;
&lt;li&gt;Meter values&lt;/li&gt;
&lt;li&gt;Remote start / stop&lt;/li&gt;
&lt;li&gt;Reconnection behavior&lt;/li&gt;
&lt;li&gt;EVSE / connector mapping&lt;/li&gt;
&lt;li&gt;Vendor-specific behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A successful boot should therefore be treated as the &lt;strong&gt;beginning of integration testing&lt;/strong&gt;, not the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Real chargers don't always behave like simulators
&lt;/h2&gt;

&lt;p&gt;Simulators are extremely useful during development.&lt;/p&gt;

&lt;p&gt;But production hardware introduces another level of complexity.&lt;/p&gt;

&lt;p&gt;We've seen chargers connect and boot successfully, then behave differently than expected during authorization, transaction handling or status updates.&lt;/p&gt;

&lt;p&gt;This is particularly important when integrating hardware from multiple manufacturers.&lt;/p&gt;

&lt;p&gt;That's why &lt;strong&gt;logging and observability become critical&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For every OCPP message, we want to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which charger sent it?&lt;/li&gt;
&lt;li&gt;Which OCPP version was used?&lt;/li&gt;
&lt;li&gt;What action was requested?&lt;/li&gt;
&lt;li&gt;What payload was received?&lt;/li&gt;
&lt;li&gt;What response was returned?&lt;/li&gt;
&lt;li&gt;How long did processing take?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this information, remotely debugging a charging station becomes painful very quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. OCPP 1.6J and OCPP 2.0.1 are fundamentally different
&lt;/h2&gt;

&lt;p&gt;One mistake is treating OCPP 2.0.1 as simply a newer version of OCPP 1.6J.&lt;/p&gt;

&lt;p&gt;The architecture changed significantly.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;OCPP 1.6J&lt;/strong&gt;, transactions commonly revolve around:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StartTransaction → MeterValues → StopTransaction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;OCPP 2.0.1&lt;/strong&gt;, transaction handling revolves around &lt;strong&gt;TransactionEvent&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Started → Updated → Ended&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That difference affects how a backend should model charging sessions.&lt;/p&gt;

&lt;p&gt;We've found it much cleaner to separate protocol-specific processing from the internal business logic.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OCPP 1.6J / OCPP 2.0.1 → Protocol Layer → Normalized Events → Business Logic → API / Applications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This allows the rest of the platform to work with a consistent model without needing to understand every protocol-specific detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. EVSE and connector mapping matters
&lt;/h2&gt;

&lt;p&gt;This becomes especially important with &lt;strong&gt;OCPP 2.0.1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should not simply assume:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EVSE 1 = Connector 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A charging station can expose multiple EVSEs and connectors.&lt;/p&gt;

&lt;p&gt;A safer internal structure is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Charging Station → EVSE → Connector&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It sounds obvious when written down.&lt;/p&gt;

&lt;p&gt;It's much less obvious when you're debugging a real charger at 2 AM because a transaction has been associated with the wrong connector. 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  5. MeterValues need defensive handling
&lt;/h2&gt;

&lt;p&gt;Energy data sounds simple.&lt;/p&gt;

&lt;p&gt;Receive a value and store it.&lt;/p&gt;

&lt;p&gt;Real chargers can report many different measurements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Energy&lt;/li&gt;
&lt;li&gt;Power&lt;/li&gt;
&lt;li&gt;Current&lt;/li&gt;
&lt;li&gt;Voltage&lt;/li&gt;
&lt;li&gt;State of Charge (SoC)&lt;/li&gt;
&lt;li&gt;Temperature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And not every charger provides every measurement.&lt;/p&gt;

&lt;p&gt;A production backend should therefore never assume that every value will always exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Energy available?&lt;/strong&gt; Display it.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Power available?&lt;/strong&gt; Display it.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;SoC available?&lt;/strong&gt; Display it.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Not available?&lt;/strong&gt; Continue normally.&lt;/p&gt;

&lt;p&gt;Your system shouldn't fall apart because one charger doesn't provide SoC.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. RemoteStart does not mean charging has started
&lt;/h2&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;A successful remote-start request does &lt;strong&gt;not necessarily mean energy is already flowing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The actual sequence may look more like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remote command accepted → Authorization → Transaction created → EV ready → Energy starts flowing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applications should distinguish between these states.&lt;/p&gt;

&lt;p&gt;Otherwise, a customer might see &lt;strong&gt;"Charging"&lt;/strong&gt; while the charger has merely accepted the remote command.&lt;/p&gt;

&lt;p&gt;The same principle applies when stopping a charging session.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. WebSocket reliability matters
&lt;/h2&gt;

&lt;p&gt;EV chargers maintain long-lived connections.&lt;/p&gt;

&lt;p&gt;Real networks, unfortunately, have other plans. :)&lt;/p&gt;

&lt;p&gt;Routers restart.&lt;/p&gt;

&lt;p&gt;Internet connections disappear.&lt;/p&gt;

&lt;p&gt;Mobile networks become unstable.&lt;/p&gt;

&lt;p&gt;Chargers sometimes reconnect without a perfectly clean disconnect.&lt;/p&gt;

&lt;p&gt;A production OCPP backend needs to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heartbeats&lt;/li&gt;
&lt;li&gt;Stale connections&lt;/li&gt;
&lt;li&gt;Reconnection&lt;/li&gt;
&lt;li&gt;Duplicate sessions&lt;/li&gt;
&lt;li&gt;Unexpected disconnects&lt;/li&gt;
&lt;li&gt;Transaction recovery&lt;/li&gt;
&lt;li&gt;Charger availability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A charger being online five seconds ago doesn't necessarily mean it's online now.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Observability is a product feature
&lt;/h2&gt;

&lt;p&gt;This was one of our biggest lessons.&lt;/p&gt;

&lt;p&gt;At first, logging feels like an engineering concern.&lt;/p&gt;

&lt;p&gt;Once you start integrating real chargers, it becomes a &lt;strong&gt;product feature&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When something fails, developers need to answer one question immediately:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What exactly did the charger send?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is also one of the reasons we built a small developer tool for working with OCPP messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  OCPP Software by Ureticy — Chrome Extension
&lt;/h3&gt;

&lt;p&gt;It supports &lt;strong&gt;OCPP 1.6J and OCPP 2.0.1&lt;/strong&gt; message analysis directly inside the browser.&lt;/p&gt;

&lt;p&gt;And importantly:&lt;/p&gt;

&lt;p&gt;🔒 &lt;strong&gt;OCPP payloads stay local.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Messages are parsed and analyzed inside the extension. They are &lt;strong&gt;not uploaded to Ureticy or any external server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://chromewebstore.google.com/detail/ocpp-software-by-ureticy/pleapkiakgbijilhbcnbhofpmbfhmlie" rel="noopener noreferrer"&gt;OCPP Software by Ureticy — Chrome Web Store&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Separate protocol logic from business logic
&lt;/h2&gt;

&lt;p&gt;This is probably one of the architectural decisions we're happiest with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your OCPP layer should understand OCPP.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your business layer should understand your product.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The architecture can look roughly like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OCPP Message → Protocol Handler → Normalized Event → Business Rules → Database / API / Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The business layer shouldn't need to care whether an event originated from &lt;strong&gt;StartTransaction in OCPP 1.6J&lt;/strong&gt; or &lt;strong&gt;TransactionEvent in OCPP 2.0.1&lt;/strong&gt; when that distinction isn't relevant.&lt;/p&gt;

&lt;p&gt;This separation also makes supporting additional protocol versions significantly easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Don't build only for the happy path
&lt;/h2&gt;

&lt;p&gt;A perfect charging session might look like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connect → Boot → Authorize → Start → MeterValues → Stop → Disconnect&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Production isn't always that clean.&lt;/p&gt;

&lt;p&gt;You also need to ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if authorization succeeds but the transaction never starts?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if the charger disconnects during an active transaction?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if MeterValues suddenly stop arriving?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if the charger reconnects during the session?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if a connector reports an unexpected status?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if your backend restarts while chargers are connected?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The interesting part of building an OCPP backend isn't processing the perfect transaction.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It's surviving the imperfect ones.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Building Ureticy
&lt;/h2&gt;

&lt;p&gt;These lessons are part of what led us to build &lt;strong&gt;Ureticy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ureticy is an &lt;strong&gt;OCPP software platform&lt;/strong&gt; for EV charger manufacturers, CPOs, developers, fleets and companies building EV charging infrastructure.&lt;/p&gt;

&lt;p&gt;We're developing the platform around real charging hardware and real OCPP communication, with support for &lt;strong&gt;OCPP 1.6J and OCPP 2.0.1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;⚡ &lt;strong&gt;OCPP Software:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://ureticy.com/en/" rel="noopener noreferrer"&gt;Ureticy — OCPP Software Platform&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🇹🇷 &lt;strong&gt;Türkiye:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://ureticy.com/tr/" rel="noopener noreferrer"&gt;Ureticy — OCPP Yazılımı&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🧩 &lt;strong&gt;Developer Tool:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://chromewebstore.google.com/detail/ocpp-software-by-ureticy/pleapkiakgbijilhbcnbhofpmbfhmlie" rel="noopener noreferrer"&gt;OCPP Software by Ureticy — Chrome Extension&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;If you're starting an OCPP backend today, my biggest recommendation would be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Design for imperfect chargers and imperfect networks from day one.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The OCPP specification gives you the language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production hardware teaches you the dialects.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We're still learning those dialects ourselves.&lt;/p&gt;

&lt;p&gt;If you're building chargers, a CSMS or anything around OCPP, I'd love to hear:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the strangest OCPP behavior you've encountered from real charging hardware?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ocpp</category>
      <category>evcharging</category>
      <category>iot</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
