<?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: EVChargingDevs</title>
    <description>The latest articles on DEV Community by EVChargingDevs (@evchargingdevs).</description>
    <link>https://dev.to/evchargingdevs</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%2F4189709%2F6003ed95-829d-42a8-bf51-acdf83f7da1d.png</url>
      <title>DEV Community: EVChargingDevs</title>
      <link>https://dev.to/evchargingdevs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/evchargingdevs"/>
    <language>en</language>
    <item>
      <title>Getting Started with OCPP 1.6 Using a Simulated Charger</title>
      <dc:creator>EVChargingDevs</dc:creator>
      <pubDate>Sat, 17 Oct 2026 02:20:56 +0000</pubDate>
      <link>https://dev.to/evchargingdevs/getting-started-with-ocpp-16-using-a-simulated-charger-23bc</link>
      <guid>https://dev.to/evchargingdevs/getting-started-with-ocpp-16-using-a-simulated-charger-23bc</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foj5fi2xenzprqgntd8j3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foj5fi2xenzprqgntd8j3.png" alt=" " width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have ever plugged in an electric car at a public charger, two computers had a conversation you never saw: the charger (the &lt;strong&gt;charge point&lt;/strong&gt;) and a server that manages it (the &lt;strong&gt;CSMS&lt;/strong&gt;, Charging Station Management System). The language they speak is usually &lt;strong&gt;OCPP&lt;/strong&gt;, the Open Charge Point Protocol.&lt;/p&gt;

&lt;p&gt;This tutorial walks through that conversation hands-on with OCPP 1.6, the version most chargers in the field still run. You will start a CSMS on your laptop, connect a simulated charger, run a charging session from the dashboard, and then drive the same charger from the command line with &lt;code&gt;curl&lt;/code&gt;. No hardware needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What OCPP is, in five minutes
&lt;/h2&gt;

&lt;p&gt;OCPP is an open standard from the Open Charge Alliance. Version 1.6 (2015) is the workhorse of the industry. Versions 2.0.1 and 2.1 add more features, but the core ideas are the same.&lt;/p&gt;

&lt;p&gt;Three ideas carry you a long way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The charger connects to the server, not the other way around.&lt;/strong&gt; A charge point opens a WebSocket to the CSMS and keeps it open. The URL ends with the charger's identity, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ws://csms.example.com/ocpp/CS-0001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During the WebSocket handshake the charger asks for the &lt;code&gt;ocpp1.6&lt;/code&gt; subprotocol, so the server knows which version it speaks. Because the connection stays open, the server can send commands to a charger that sits behind a mobile network or a firewall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Every message is a small JSON array.&lt;/strong&gt; OCPP 1.6 over WebSocket (often called OCPP-J) has three message types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;CALL, a request&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[2, "&amp;lt;messageId&amp;gt;", "&amp;lt;Action&amp;gt;", {payload}]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;CALLRESULT, a successful answer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[3, "&amp;lt;messageId&amp;gt;", {payload}]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;CALLERROR, a failed answer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[4, "&amp;lt;messageId&amp;gt;", "&amp;lt;errorCode&amp;gt;", "&amp;lt;description&amp;gt;", {details}]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;messageId&lt;/code&gt; ties an answer to its request. Either side can send a CALL: the charger reports things (it booted, a cable was plugged in, energy was delivered), and the server sends commands (start, stop, reset, change a setting).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A charger has connectors, and each connector has a status.&lt;/strong&gt; Connector 1 is the first plug. Connector 0 means the charger as a whole. Statuses move through a small state machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available -&amp;gt; Preparing -&amp;gt; Charging -&amp;gt; Finishing -&amp;gt; Available
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with side states such as &lt;code&gt;SuspendedEV&lt;/code&gt;, &lt;code&gt;Unavailable&lt;/code&gt; and &lt;code&gt;Faulted&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The messages you will see in this tutorial:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Message&lt;/th&gt;
&lt;th&gt;Sent by&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BootNotification&lt;/td&gt;
&lt;td&gt;Charger&lt;/td&gt;
&lt;td&gt;"I just started. Here is my vendor and model."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heartbeat&lt;/td&gt;
&lt;td&gt;Charger&lt;/td&gt;
&lt;td&gt;"I am still here." Also syncs the clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StatusNotification&lt;/td&gt;
&lt;td&gt;Charger&lt;/td&gt;
&lt;td&gt;A connector changed status.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorize&lt;/td&gt;
&lt;td&gt;Charger&lt;/td&gt;
&lt;td&gt;"May this RFID tag (idTag) charge?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StartTransaction&lt;/td&gt;
&lt;td&gt;Charger&lt;/td&gt;
&lt;td&gt;Charging started. The server answers with a transaction id.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MeterValues&lt;/td&gt;
&lt;td&gt;Charger&lt;/td&gt;
&lt;td&gt;Energy readings during the session.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StopTransaction&lt;/td&gt;
&lt;td&gt;Charger&lt;/td&gt;
&lt;td&gt;Charging stopped, with the final meter reading.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RemoteStartTransaction&lt;/td&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Start charging for an idTag, for example from an app.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RemoteStopTransaction&lt;/td&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Stop a transaction.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TriggerMessage, GetConfiguration&lt;/td&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Ask the charger to send a message now, or read its settings.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The lab
&lt;/h2&gt;

&lt;p&gt;You need a CSMS to talk to and a charger to talk to it. For this tutorial I use &lt;a href="https://github.com/evtivity/evtivity-csms" rel="noopener noreferrer"&gt;EVtivity&lt;/a&gt;, a self-hosted CSMS that ships with a built-in charging station simulator, so the whole lab runs in Docker on one machine. Any CSMS and simulator would teach the same protocol; this one just saves setup.&lt;/p&gt;

&lt;p&gt;You need Docker with Docker Compose, plus &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;jq&lt;/code&gt; for the last part.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/evtivity/evtivity-csms.git
&lt;span class="nb"&gt;cd &lt;/span&gt;evtivity-csms
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give it about a minute to start, then open the dashboard at &lt;a href="http://localhost:7100" rel="noopener noreferrer"&gt;http://localhost:7100&lt;/a&gt; and sign in as &lt;code&gt;admin@evtivity.local&lt;/code&gt; with the password &lt;code&gt;admin123&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The stack starts two simulated chargers. &lt;code&gt;CS-0001&lt;/code&gt; speaks OCPP 1.6 and &lt;code&gt;CS-0002&lt;/code&gt; speaks OCPP 2.1. We will use &lt;code&gt;CS-0001&lt;/code&gt;, which connects to &lt;code&gt;ws://localhost:7103/CS-0001&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: BootNotification, and why a new charger waits
&lt;/h2&gt;

&lt;p&gt;Open &lt;strong&gt;Stations&lt;/strong&gt;, then &lt;strong&gt;CS-0001&lt;/strong&gt;, and go to the &lt;strong&gt;OCPP Commands&lt;/strong&gt; tab. At the bottom is the OCPP message log: every message between the charger and the server, newest first. Click a row to see its JSON.&lt;/p&gt;

&lt;p&gt;The first thing a charger does after connecting is introduce itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a1b2c3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BootNotification"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chargePointVendor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chargePointModel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chargePointSerialNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"firmwareVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server answers with one of three statuses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Accepted&lt;/code&gt;: the charger is registered and may operate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Pending&lt;/code&gt;: the server knows about it but has not decided yet. The charger must not start normal operation and should retry after &lt;code&gt;interval&lt;/code&gt; seconds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Rejected&lt;/code&gt;: go away, retry later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fresh install answers &lt;strong&gt;Pending&lt;/strong&gt;, because new chargers need an operator's approval first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a1b2c3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"currentTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-17T09:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a real-world pattern. A CSMS on the internet should not trust every device that knows its URL.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Approve&lt;/strong&gt; on the station page. On its next BootNotification the charger receives &lt;code&gt;Accepted&lt;/code&gt;, and the log fills up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"d4e5f6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Accepted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"currentTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-17T09:05:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;StatusNotification&lt;/strong&gt;, once for connector 0 (the charger) and once per connector: &lt;code&gt;{"connectorId": 1, "errorCode": "NoError", "status": "Available"}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heartbeat&lt;/strong&gt; every &lt;code&gt;interval&lt;/code&gt; seconds (300 here). The empty request &lt;code&gt;{}&lt;/code&gt; gets back &lt;code&gt;{"currentTime": "..."}&lt;/code&gt;, which also keeps the charger's clock in sync.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The server chose the heartbeat interval, not the charger. The CSMS tells chargers how to behave, and OCPP gives it the messages to do so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create an RFID tag
&lt;/h2&gt;

&lt;p&gt;Drivers identify themselves with an &lt;strong&gt;idTag&lt;/strong&gt;, usually the id of an RFID card, and the charger asks the server whether that tag may charge. An unknown tag is refused, so create one first.&lt;/p&gt;

&lt;p&gt;Open &lt;strong&gt;Tokens&lt;/strong&gt; and add a token with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; &lt;code&gt;TUTORIAL-001&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; RFID (ISO14443)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Driver:&lt;/strong&gt; the demo driver, &lt;code&gt;driver@evtivity.local&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep idTags at 20 characters or fewer. OCPP 1.6 limits them to 20.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: A charging session, message by message
&lt;/h2&gt;

&lt;p&gt;Back on &lt;strong&gt;CS-0001&lt;/strong&gt;, open the &lt;strong&gt;Simulate&lt;/strong&gt; tab. It acts as the car and the driver. Enter &lt;code&gt;TUTORIAL-001&lt;/code&gt; as the ID token and click through the steps, checking the message log after each one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plug in.&lt;/strong&gt; The charger notices a cable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StatusNotification"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"connectorId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"errorCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NoError"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Preparing"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Start charging&lt;/strong&gt; (the driver taps the card). Three things happen in order.&lt;/p&gt;

&lt;p&gt;First the charger asks whether the tag is allowed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Authorize"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"idTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TUTORIAL-001"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"idTagInfo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Accepted"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it starts a transaction and reports its meter reading in Wh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StartTransaction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"connectorId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"idTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TUTORIAL-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"meterStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"transactionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"idTagInfo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Accepted"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server assigns the &lt;code&gt;transactionId&lt;/code&gt;. From here on, both sides use it to refer to this session.&lt;/p&gt;

&lt;p&gt;Finally the connector status changes to &lt;code&gt;Charging&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;While charging,&lt;/strong&gt; the charger sends &lt;strong&gt;MeterValues&lt;/strong&gt; every few seconds (the &lt;code&gt;MeterValueSampleInterval&lt;/code&gt; setting), each tagged with the transaction id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MeterValues"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connectorId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transactionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meterValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sampledValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1520"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"measurand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Energy.Active.Import.Register"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wh"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Sessions&lt;/strong&gt; page in the dashboard shows the energy growing. That number comes straight from these messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop charging.&lt;/strong&gt; The charger closes the transaction with its final meter reading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StopTransaction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"transactionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"meterStop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Local"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The connector then moves to &lt;code&gt;Finishing&lt;/code&gt;. &lt;strong&gt;Unplug&lt;/strong&gt; returns it to &lt;code&gt;Available&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The energy for the session is &lt;code&gt;meterStop - meterStart&lt;/code&gt;. Billing systems are built on this subtraction, which is why chargers report a cumulative meter register rather than the energy of each session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: The same thing from the command line
&lt;/h2&gt;

&lt;p&gt;Everything the dashboard does goes through a REST API, so you can drive the charger with &lt;code&gt;curl&lt;/code&gt;. The API runs on port 7102.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get an access token.&lt;/strong&gt; It is valid for an hour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; localhost:7102/v1/auth/login &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"email":"admin@evtivity.local","password":"admin123"}'&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;AUTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Look at the charger:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"localhost:7102/v1/stations?search=CS-0001"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AUTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'.data[] | {id, stationId, ocppProtocol, isOnline, status}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;id&lt;/code&gt; field (something like &lt;code&gt;sta_000000000001&lt;/code&gt;). The station URLs below use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Send a TriggerMessage.&lt;/strong&gt; This asks the charger to send a message now instead of waiting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; localhost:7102/v1/ocpp/commands/v16/TriggerMessage &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AUTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"stationId":"CS-0001","requestedMessage":"Heartbeat"}'&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;response&lt;/code&gt; field holds the charger's own answer (&lt;code&gt;Accepted&lt;/code&gt;), and a fresh Heartbeat appears in the log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read a configuration key.&lt;/strong&gt; In OCPP 1.6, chargers keep their settings as key-value pairs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; localhost:7102/v1/ocpp/commands/v16/GetConfiguration &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AUTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"stationId":"CS-0001","key":["HeartbeatInterval","MeterValueSampleInterval"]}'&lt;/span&gt; | jq .response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Start a session remotely,&lt;/strong&gt; the way a mobile app does. First plug the simulated car in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; localhost:7102/v1/css/actions/plugIn &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AUTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"stationId":"CS-0001","evseId":1}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then send &lt;strong&gt;RemoteStartTransaction&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; localhost:7102/v1/ocpp/commands/v16/RemoteStartTransaction &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AUTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"stationId":"CS-0001","connectorId":1,"idTag":"TUTORIAL-001"}'&lt;/span&gt; | jq .response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch the log. The server sends a CALL, the charger answers &lt;code&gt;{"status": "Accepted"}&lt;/code&gt;, and then the charger itself sends StartTransaction, just like in Step 3. An accepted RemoteStartTransaction only means "I will try". The session exists once StartTransaction arrives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find the transaction and stop it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"localhost:7102/v1/sessions?status=active"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AUTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.data[] | select(.stationName=="CS-0001") | .transactionId'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

curl &lt;span class="nt"&gt;-s&lt;/span&gt; localhost:7102/v1/ocpp/commands/v16/RemoteStopTransaction &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AUTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;stationId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;CS-0001&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;transactionId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$TX&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; | jq .response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The charger accepts, then sends StopTransaction with &lt;code&gt;"reason": "Remote"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read the message log from the API:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;STATION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sta_000000000001   &lt;span class="c"&gt;# the id from the stations call above&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"localhost:7102/v1/stations/&lt;/span&gt;&lt;span class="nv"&gt;$STATION_ID&lt;/span&gt;&lt;span class="s2"&gt;/ocpp-logs?limit=10"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AUTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'.data[] | {createdAt, direction, action, messageType}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;messageType&lt;/code&gt; is the same 2, 3 or 4 from the table at the top.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you have learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A charger opens a WebSocket to the CSMS and keeps it open. The URL carries its identity, and the subprotocol carries the OCPP version.&lt;/li&gt;
&lt;li&gt;Every message is a CALL, a CALLRESULT or a CALLERROR, matched by message id.&lt;/li&gt;
&lt;li&gt;BootNotification registers a charger, and the server can hold it at &lt;code&gt;Pending&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A session is Authorize, StartTransaction, MeterValues and StopTransaction. The server-issued transaction id ties them together.&lt;/li&gt;
&lt;li&gt;Remote commands only ask. The charger's own messages report what actually happened.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://openchargealliance.org/protocols/open-charge-point-protocol/" rel="noopener noreferrer"&gt;OCPP 1.6 specification&lt;/a&gt; is free to download from the Open Charge Alliance and reads well once these basics are in place.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was written with the help of AI and reviewed by the author.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>iot</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>abotwrotethis</category>
    </item>
  </channel>
</rss>
