mikuso / ocpp-rpc

A Node.js client & server implementation of the WAMP-like RPC-over-websocket system defined in the OCPP-J protocols.
MIT License
98 stars 29 forks source link

Help for sending commands (like pause and resume) via Charging profile #81

Open SKB-CGN opened 1 week ago

SKB-CGN commented 1 week ago

Hi, perhaps you can help me a little, in understanding, whats wrong here.

I am trying to build up a smart-charging system by my own.

I use your project to handle a Wallbox.

Currently i am struggling with the following scenario.

The car is connected to the Wallbox, the server receives the "Preparing" Status from the WB - so far so good. Now, i start the charging with:

await sendCommand('RemoteStartTransaction', { connectorId: DEFAULT_CONNECTOR_ID, idTag: DEFAULT_ID_TAG });
Response: "Accepted"

After that, i put the car into pause via Setting a ChargeProfile with the Limit of 0 (which means 0 Amp).

The WB stops charging immediately and the status is "SuspendedEVSE". After checking, if enough solar-energy is available, i want to start the charging again and send a new profile to the WB with a proper limit of 6A (e.g. 10A). But neither the WB changes to "charging" nor the amp is raised.

Might you be able, to have an idea here?

mikuso commented 1 week ago

Hi, It sounds like you are doing the correct thing.

Can you capture the OCPP logs to/from your charger - it might help identify where things are going wrong.

On the other hand, this might not be an OCPP issue at all. It could simply be that the charging station and/or the EV have an issue that prevents a charging session from resuming from 0 amps.

The logs should make things clearer, if you can provide them.

SKB-CGN commented 1 week ago

Hi, first of all - thanks for your reply :) This is much appreciated!

Perhaps you know the Software "EVCC", which also offers this type of charging with solar. I am trying to build that a bit smaller and they do the same thing (as far as i understand from their code).

Could you provide, how i could capture the logs?

Currently the things look like this:

Function for "Preparing" sourced from StatusNotification:

case 'Preparing': {
   console.log('Wallbox is preparing a new car!');

   // First put the car into pause mode and check requirements
   const response = await sendCommand('SetChargingProfile', generateChargeProfile(0));
   console.log("ChargeProfile", response);

   if (response && response.status == 'Accepted') {
      // Start the Remote
      const response = await sendCommand('RemoteStartTransaction', { connectorId: DEFAULT_CONNECTOR_ID, idTag: DEFAULT_ID_TAG });
      console.log("RemoteTrans", response);
   }

   // Check, if charging is possible
}

Function generateChargeProfile:

/**
 * @param {number} amps
 */
function generateChargeProfile(amps) {
   const now = new Date();
   const timestamp = new Date(now.getTime() - 60 * 1000).toISOString(); // Reduce one minute
   console.log(timestamp);

   return {
      "connectorId": DEFAULT_CONNECTOR_ID,
      "csChargingProfiles": {
         "chargingProfileId": 1,
         "stackLevel": 0,
         "chargingProfilePurpose": "TxDefaultProfile",
         "chargingProfileKind": "Absolute",
         "chargingSchedule": {
            "startSchedule": timestamp,
            "chargingRateUnit": "A",
            "chargingSchedulePeriod": [
               {
                  "startPeriod": 0,
                  "limit": amps
               }]
         }
      }
   }
}
mikuso commented 1 week ago

You can log OCPP messages using this module by listening for the 'message' event, like so:

client.on('message', ({message, outbound}) => {
    console.log(outbound ? '>>>' : '<<<', message);
});
SKB-CGN commented 1 week ago

So, i have enabled the messages. Very good thing ... but i can only see:

>>> [2,"bede3b77-653a-4098-bf92-6c9aeeaf8c08","SetChargingProfile",{"connectorId":1,"csChargingProfiles":{"chargingProfileId":1,"stackLevel":0,"chargingProfilePurpose":"TxDefaultProfile","chargingProfileKind":"Absolute","chargingSchedule":{"startSchedule":"2024-11-22T14:31:17.394Z","chargingRateUnit":"A","chargingSchedulePeriod":[{"startPeriod":0,"limit":0}]}}}]
<<< [3,"bede3b77-653a-4098-bf92-6c9aeeaf8c08",{"status": "Accepted"}]

Which normally should now deactivate the charging - but it does not. I am totally confused now!

mikuso commented 1 week ago

Hi, perhaps there is some kind of time synchronisation issue?

Have you tried omitting the startSchedule property? This property is optional, and the charging station should apply the charging schedule immediately if it is omitted.

It would be good to see all of your message logs up until this point (everything for this session up to this point, BootNotification onwards). Including timestamps with each message would also help.

SKB-CGN commented 1 week ago

Sure, will set that up again and come back to you shortly.

Thanks for your great help so far!

SKB-CGN commented 1 week ago

Here is a log with timestamps and some Logs from my end. The Log shows the procedure from a fresh start (including reset through the fusebox) till plugging in the car. But, as seen in the log, the Wallbox does not stop charging immediately after plugging the car in - which was working today morning. Also, i ommited the "startSchedule" you mentioned.

Log:

<<< 22.11.2024, 20:15:47 [2,"1294040071","BootNotification",{"chargePointSerialNumber": "330571","chargePointVendor": "Wall Box Chargers","meterType": "Internal NON compliant","meterSerialNumber": "","chargePointModel": "PLP1-M-2-4","iccid": "","chargeBoxSerialNumber": "330571","firmwareVersion": "6.4.14","imsi                                               ": ""}]
>>> 22.11.2024, 20:15:47 [3,"1294040071",{"status":"Accepted","interval":60,"currentTime":"2024-11-22T19:15:47.446Z"}]
<<< 22.11.2024, 20:15:47 [2,"921689141","StatusNotification",{"info": "","vendorId": "com.wallbox","vendorErrorCode": "","connectorId": 0,"errorCode": "NoError","status": "Available","timestamp": "2024-11-22T19:15:47Z"}]
>>> 22.11.2024, 20:15:47 [3,"921689141",{}]
<<< 22.11.2024, 20:15:47 [2,"365210983","StatusNotification",{"info": "","vendorId": "com.wallbox","vendorErrorCode": "","connectorId": 1,"errorCode": "NoError","status": "Available","timestamp": "2024-11-22T19:15:47Z"}]
Wallbox is ready!
>>> 22.11.2024, 20:15:47 [3,"365210983",{}]
<<< 22.11.2024, 20:16:47 [2,"1062513924","Heartbeat",{}]
>>> 22.11.2024, 20:16:47 [3,"1062513924",{"currentTime":"2024-11-22T19:16:47.615Z"}]
<<< 22.11.2024, 20:17:22 [2,"1182132260","StatusNotification",{"info": "","vendorId": "com.wallbox","vendorErrorCode": "","connectorId": 1,"errorCode": "NoError","status": "Preparing","timestamp": "2024-11-22T19:17:21Z"}]
Wallbox is preparing a new car!
>>> 22.11.2024, 20:17:22 [2,"0e8433e6-7565-498b-a038-8ac37a7c462e","SetChargingProfile",{"connectorId":1,"csChargingProfiles":{"chargingProfileId":1,"stackLevel":0,"chargingProfilePurpose":"TxDefaultProfile","chargingProfileKind":"Absolute","chargingSchedule":{"chargingRateUnit":"A","chargingSchedulePeriod":[{"startPeriod":0,"limit":0}]}}}]
>>> 22.11.2024, 20:17:22 [3,"1182132260",{}]
<<< 22.11.2024, 20:17:23 [3,"0e8433e6-7565-498b-a038-8ac37a7c462e",{"status": "Accepted"}]
>>> 22.11.2024, 20:17:23 [2,"459ca698-7ff4-4082-8d3c-e882c4c70b4e","RemoteStartTransaction",{"connectorId":1,"idTag":"iobcc"}]
<<< 22.11.2024, 20:17:23 [3,"459ca698-7ff4-4082-8d3c-e882c4c70b4e",{"status": "Accepted"}]
<<< 22.11.2024, 20:17:25 [2,"1870190811","StatusNotification",{"info": "","vendorId": "com.wallbox","vendorErrorCode": "","connectorId": 1,"errorCode": "NoError","status": "SuspendedEV","timestamp": "2024-11-22T19:17:24Z"}]
>>> 22.11.2024, 20:17:25 [2,"841e5572-aa2b-4a02-8b32-0e4a6d8b2a67","SetChargingProfile",{"connectorId":1,"csChargingProfiles":{"chargingProfileId":1,"stackLevel":0,"chargingProfilePurpose":"TxDefaultProfile","chargingProfileKind":"Absolute","chargingSchedule":{"chargingRateUnit":"A","chargingSchedulePeriod":[{"startPeriod":0,"limit":0}]}}}]
>>> 22.11.2024, 20:17:25 [3,"1870190811",{}]
<<< 22.11.2024, 20:17:26 [3,"841e5572-aa2b-4a02-8b32-0e4a6d8b2a67",{"status": "Accepted"}]
<<< 22.11.2024, 20:17:26 [2,"1190630297","StartTransaction",{"connectorId": 1,"meterStart": 4848547,"idTag": "iobcc","timestamp": "2024-11-22T19:17:25Z"}]
>>> 22.11.2024, 20:17:26 [3,"1190630297",{"idTagInfo":{"status":"Accepted"},"transactionId":1}]
>>> 22.11.2024, 20:17:28 [3,"367625119",{}]
<<< 22.11.2024, 20:17:30 [2,"357808272","StatusNotification",{"info": "","vendorId": "com.wallbox","vendorErrorCode": "","connectorId": 1,"errorCode": "NoError","status": "Charging","timestamp": "2024-11-22T19:17:29Z"}]
The car is charging! Trying to stop it!
>>> 22.11.2024, 20:17:30 [3,"357808272",{}]
>>> 22.11.2024, 20:17:38 [3,"1523082678",{}]
>>> 22.11.2024, 20:17:40 [2,"0a4dc438-22ba-49cc-b7a1-586326f96caa","SetChargingProfile",{"connectorId":1,"csChargingProfiles":{"chargingProfileId":1,"stackLevel":0,"chargingProfilePurpose":"TxDefaultProfile","chargingProfileKind":"Absolute","chargingSchedule":{"chargingRateUnit":"A","chargingSchedulePeriod":[{"startPeriod":0,"limit":0}]}}}]
<<< 22.11.2024, 20:17:41 [3,"0a4dc438-22ba-49cc-b7a1-586326f96caa",{"status": "Accepted"}]
{ status: 'Accepted' }
Wallbox stopped! // But it did not stop the car!
<<< 22.11.2024, 20:17:47 [2,"217345665","Heartbeat",{}]
>>> 22.11.2024, 20:17:47 [3,"217345665",{"currentTime":"2024-11-22T19:17:47.752Z"}]
>>> 22.11.2024, 20:17:48 [3,"1632855875",{}]
>>> 22.11.2024, 20:17:58 [3,"781024730",{}]
>>> 22.11.2024, 20:18:08 [3,"940676182",{}]
>>> 22.11.2024, 20:18:18 [3,"1462635937",{}]
>>> 22.11.2024, 20:18:28 [3,"1858120622",{}]
>>> 22.11.2024, 20:18:38 [3,"77666387",{}]
<<< 22.11.2024, 20:18:47 [2,"1627781132","Heartbeat",{}]
>>> 22.11.2024, 20:18:47 [3,"1627781132",{"currentTime":"2024-11-22T19:18:47.865Z"}]
>>> 22.11.2024, 20:18:48 [3,"1120714984",{}]
>>> 22.11.2024, 20:18:58 [3,"1992761353",{}]

My handler for the the detection of Preparing is:

case 'Preparing': {
   console.log('Wallbox is preparing a new car!');

   // First put the car into pause mode and check requirements
   const response = await sendCommand('SetChargingProfile', generateChargeProfile(0));
   console.log("ChargeProfile", response);

   if (response && response.status == 'Accepted') {
      // Start the Remote
      const response = await sendCommand('RemoteStartTransaction', { connectorId: DEFAULT_CONNECTOR_ID, idTag: DEFAULT_ID_TAG });
      console.log("RemoteTrans", response);
   }
} F

The function for generateChargeProfile() is the same like above, but currently without the startSchedule.

Hopefully, you have some idea for me ;)

Thanks again!

mikuso commented 6 days ago

I would have expected the status of the connector to change to SuspendedEVSE once you set the 0-amp charging profile, but it remained as Charging. Perhaps this is a mistake in the Wallbox reporting that it is charging when it is not?

Try enabling periodic meter values sampling so we can see for sure whether or not the EV is charging.

To do this:

Let's see what logs we get back then.

SKB-CGN commented 6 days ago

Thanks for coming back on this topic 👍

I think, I have had a coding error while putting the Wallbox to pause with 0 Amp.

Will verify that tomorrow again and come back.

In the meantime - what do you think, is the overall best way, to apply the first charge profile, to set the wb into pause?

I want to archive, that the wallbox will be set to pause first, every time a car connects.

After that, I will check the conditions and start charging or leave the wallbox in pause.

mikuso commented 3 days ago

The best way to do this is by setting a TxDefaultProfile charging profile which specifies 0 amps. This will cause all new charging sessions to pause as soon as they start, and should keep the status of the connector as SuspendedEVSE. You should only need to set this up one time only, but in some rare cases you might need to re-issue this default profile when the charger reboots in case it is prone to losing its memory.

Then, whenever you are ready to ramp up your amps, set a TxProfile for that specific connector with the amps that you desire. At the end, once the session has completed and the transaction stops, the TxProfile should automatically delete itself (as per the spec), so you don't need to worry about removing the TxProfile yourself.

The next session on that connector will go back to using the 0 amps TxDefaultProfile that you previously set up.

Also, here's a couple of things I probably should have mentioned earlier on...

SKB-CGN commented 3 days ago

Hi, thanks for sharing all informations and thoughts about my topic. This is much appreciated ;)

I went further with all the things you provided and i found out in the meanwhile. During this period, i got confused a lot cause of the reactions of my wallbox. I think, they implemented OCPP in a "special" way, but dont support the necessary specifications.

Your library is used, to build up an adapter for the automation system iobroker - so dont wonder, why the logging or commands look a bit different ;)

Please have a look at the following things i did so far.

I am starting the server and waiting for the client to connect - this works. After that, i set some configurations, which also works. Where i start to get confused is:

await this.sendCommand('SetChargingProfile', this.generateChargeProfile(0));

The command JSON is:

>>> 30.11.2024, 11:23:18[2,"ea8bf886-4bbc-4b7f-be3a-f320bca00e95","SetChargingProfile",{"connectorId":1,"csChargingProfiles":{"chargingProfileId":1,"stackLevel":0,"chargingProfilePurpose":"TxDefaultProfile","chargingProfileKind":"Absolute","chargingSchedule":{"chargingRateUnit":"A","chargingSchedulePeriod":[{"startPeriod":0,"limit":0}]}}}]

Answer:

<<< 30.11.2024, 11:23:19[3,"ea8bf886-4bbc-4b7f-be3a-f320bca00e95",{"status": "Accepted"}]

This indicates - from my understanding, that the profile is installed successfully and set to 0A. When i have a look into the Webinterface of my Wallbox, it still says: 2024-11-30 11_40_24-myWallbox – Mozilla Firefox

After that, i request the MeterValues, which returns, if no car is connected:

OCPP-Charge: {"chargePower":0,"chargeCurrent":32,"meterStart":0,"meterEnd":0,"session":0,"lastValue":4865260.2}

So, the wallbox still is in 32A mode, which is obviously not "correct". Therefore, i need to send the below command, after the wallbox says it is in "Preparing". chargeAmps here is some variable - mostly 0 for putting the car into pause first.

// First start the RemoteTransAction
const response = await this.sendCommand('RemoteStartTransaction', { connectorId: DEFAULT_CONNECTOR_ID, idTag: DEFAULT_ID_TAG });
if (response.status === 'Accepted') {
    // Set the Wallbox into Pause-Mode or charge directly; depends on status
    const response = await this.sendCommand('SetChargingProfile', this.generateChargeProfile(chargeAmps));
    this.log.info(`[OCPP]: Wallbox has accepted the remote start and is set to ${chargeAmps}A!`);
    this.log.info("[OCPP]: ChargeProfile" + JSON.stringify(response));
}

This puts the car into pause - but using a TxProfile here, is not working - nor sending it directly with the StarteRemoteTransaction. The wallbox returns with Accepted but does not use the profile according to my testing.

So, from my point of view, the OCPP works totally different on "every" wallbox.

Perhaps i misunderstood something? Do you perhaps have any other ideas?

mikuso commented 2 days ago

This indicates - from my understanding, that the profile is installed successfully and set to 0A. When i have a look into the Webinterface of my Wallbox, it still says: 2024-11-30 11_40_24-myWallbox – Mozilla Firefox

After that, i request the MeterValues, which returns, if no car is connected:

OCPP-Charge: {"chargePower":0,"chargeCurrent":32,"meterStart":0,"meterEnd":0,"session":0,"lastValue":4865260.2}

So, the wallbox still is in 32A mode, which is obviously not "correct". Therefore, i need to send the below command, after the wallbox says it is in "Preparing". chargeAmps here is some variable - mostly 0 for putting the car into pause first.

It's possible that the GUI is misreporting (or perhaps reporting something different, like a manufacturer's limit, rather than a configuration limit). Don't take this number as gospel.

// First start the RemoteTransAction
const response = await this.sendCommand('RemoteStartTransaction', { connectorId: DEFAULT_CONNECTOR_ID, idTag: DEFAULT_ID_TAG });
if (response.status === 'Accepted') {
  // Set the Wallbox into Pause-Mode or charge directly; depends on status
  const response = await this.sendCommand('SetChargingProfile', this.generateChargeProfile(chargeAmps));
  this.log.info(`[OCPP]: Wallbox has accepted the remote start and is set to ${chargeAmps}A!`);
  this.log.info("[OCPP]: ChargeProfile" + JSON.stringify(response));
}

This puts the car into pause - but using a TxProfile here, is not working - nor sending it directly with the StarteRemoteTransaction. The wallbox returns with Accepted but does not use the profile according to my testing.

Have you got the logs for this one? Are you sure that you've set the chargingProfilePurpose to TxProfile and specified which connector ID the transaction is taking place on? How did the charger respond to this command? Setting a TxProfile with connectorId 0 results in undefined behaviour (in fact, such a profile should be rejected by the charging station).

So, from my point of view, the OCPP works totally different on "every" wallbox.

Perhaps i misunderstood something? Do you perhaps have any other ideas?

Have you checked for firmware updates? In other brands, I have seen major changes in behaviour from version to version. It's not unheard of.

SKB-CGN commented 2 days ago

Thanks a lot for your comments!

I will have a look into this again - if I remember it correctly, i set the TXProfile when the wallbox mentions, it is preparing.

Then it is accepting the profile, but not applying it.

What I don't get is, if I send the 0A DefaultTxProfile even a MeterValues request shows the 32A in return.

Will provide you with the logs after preparing the application for this scenario again!