Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCPP support #82

Closed
wants to merge 28 commits into from
Closed

OCPP support #82

wants to merge 28 commits into from

Conversation

matth-x
Copy link

@matth-x matth-x commented May 31, 2024

Integrate OCPP 1.6 into the SmartEVSE firmware.

Current TODO list:

  • Add MicroOcpp dependencies
  • Run MicroOcpp on the same task as Mongoose (--> moved to the main task)
  • Monitor control pilot status
  • Create list of possible error states / monitor error states in MicroOcpp
  • Monitor energy meter and further available sensors
  • Set Access_bit via OCPP
  • RFID-based authorization
  • Add OCPP settings to HTTP API
  • Add menu item for OCPP settings
  • Smart Charging
  • Firmware download and hardware diagnostics upload
  • Modify connector lock behavior via OCPP (advanced)

Copy link
Owner

@dingo35 dingo35 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good Matthias, like your coding style; mg_mgr_poll in loop causes crashes so thats why I put it in a separate task. If you see another solution, of course I'm open for that.
"I wish I could pay my developers for every line of code they make. I would pay them double for every line of code they delete."

void Mongoose_Poll(void * parameter) {
while (true) {
if (WiFi.isConnected())
mg_mgr_poll(&mgr, 1000);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a good reason mg_mgr_poll isn't in our loop(): it causes a crash.

Coredump:
image

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each call into the mongoose module needs to happen on the same RTOS thread, as the documentation says: https://mongoose.ws/documentation/tutorials/core/multi-threaded/#overview

I believe that the root cause of the crash you mention is somewhere else and unfortunately, such callback-driven frameworks are notoriously hard to debug. If I find possible causes, I will look for alternative solutions.

In general, preemptive multitasking like FreeRTOS threading is dangerous if the software components are not explicitly protected against it (and most high-level libraries on microcontrollers aren't). I would suggest moving all networking-related stuff on the main task as well as non-time-critical code, i.e. mongoose (including MQTT and the web server), OCPP and Wi-Fi management. Otherwise you always need to bear in mind which task is executing what which makes the code more complex to follow + adds the need for extra synchronization mechanisms.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my email...

@@ -4811,6 +4822,67 @@ void handleWIFImode() {
}
}

void ocppInit() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love it if you would put your code in #if OCPP ...... #endif blocks, so we can decide compile time: -DOCPP=1 or -DOCPP=0 . I prefer this over #ifndef....

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can also imagine introducing a new PlatformIO environment for that adding MicroOcpp in the lib_ignore section and not compiling it into the firmware at all.

if (!LocalTimeSet && WIFImode == 1) {
_LOG_A("Time not synced with NTP yet.\n");

static unsigned long lastNtpCheck = 0;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this an improvement to the vTaskDelay 1000 statement? Its one more static var ...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relates to the suggested change above of putting the networking related functionality on the main task. Then we need the loop function to execute as frequent as possible and can't put delays on it anymore.

@dingo35
Copy link
Owner

dingo35 commented Jun 2, 2024 via email

@matth-x
Copy link
Author

matth-x commented Jun 4, 2024

Hi @dingo35,

No problem to move the tasks change to another PR. In the end, I could imagine to take all OCPP-related code changes of this PR and apply them in a clean way on a fresh new branch from master. Then it makes no difference if we take out the non-OCPP changes from here.

Anyway, the most recent updates contain:

  • RFID authorization: as a first step, I think OCPP should monopolize the RFID reader. If OCPP is enabled and RFID is in EnableOne or EnableAll mode, then the UUID is forwarded to the OCPP lib
  • The HTTP API allows to set the server URL, enable / disable OCPP and view the connection status
  • Two MQTT topics
  • LittleFS enabled

For the next steps I have two questions:

  • What is "announce" in the MQTT area? Do you think it's necessary for OCPP?
  • How to develop the web dashboard? Do you use a WYSIWYG editor or do you develop the code in a text editor?

SmartEVSE-3/include/evse.h Outdated Show resolved Hide resolved
}

if (shouldReboot) {
delay(1000);
ESP.restart();
}

mg_mgr_poll(&mgr, 1000);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about I move the mgr_init, mgr_poll and mgr_free to a separate commit in my master, and insulate it from OCPP ?
mgr_init should be in the wifi event "connected" since we need it to stop/start when the captive portal is started....

@dingo35
Copy link
Owner

dingo35 commented Jun 5, 2024 via email

@matth-x
Copy link
Author

matth-x commented Jun 5, 2024

Hi Hans,

Thanks for the code review! I changed the menu position as suggested and added the missing ENABLE_OCPP statements (it compiles now when ENABLE_OCPP=0).

The Mongoose lifecycle is actually a critical topic, because my WebSocket wrapper depends on it and so does MicroOcpp, transitively. In the Mongoose docs and examples I never found that initializing the manager in the very beginning of the boot procedure would be a somehow discouraged practice. In fact, I haven't seen any problems if Mongoose is initialized before the Wi-Fi connection succeeds and remains alive throughout network reconfigurations.

OCPP is also active without network connection. If the charger boots into an offline state, most operators want that it still authorizes RFID cards according to local whitelists and captures charging sessions for a later transmission to the server. So it would be important to be able to have OCPP always on.

How hard would it be extend the lifecycle of Mongoose with the current captive portal implementation?

Okay, I will implement the MQTT announce routine by duplicating the RFID code then.

For the web dashboard, it's no problem to work with a text editor to add the few OCPP settings.

@dingo35
Copy link
Owner

dingo35 commented Jun 5, 2024

In the current master branch mgr_init is called at WifiSetup, and mgr_free is never caĺled in the normal lifecycle.

Only when the portal is called, mgr_free is called, and at the end, mgr_init again.
So usually the portal is only called on first time installation, and perhaps a few other times during the lifetime of the SmartEVSE, when wifi credentials change.

Would that be a problem for OCPP? I can look for a way to only shut the listeners down?
In what way is mongoose critical to OCPP? Is there a tcp client, http server, or what? What ports are you using?

Also: do you need certificates on the device? Are they specific to the device or can they be generic?

Keep up the great work!

Hans.

@matth-x
Copy link
Author

matth-x commented Jun 6, 2024

From the user perspective it's no problem to disable OCPP while the Wi-Fi portal is up and running. The only thing necessary is to shut down OCPP before MG is deinitialized and then to start it up again. I looked for possible locations on the recent master branch, but doing so I still found that the MG lifecycle model seems quite complex. The reason to shut down MG is to free the ports 80 and 443 again for WiFiManager? I'm wondering why not unbind the listeners on these ports and keep MG initialized. The other background tasks of MG (i.e. MQTT and OCPP trying to reconnect) shouldn't be a problem.

This would also avoid MG functions being called from another thread here:

mg_mgr_free(&mgr);

mg_mgr_init(&mgr); // Initialise event manager

What do you think? I can also make a quick write up in another draft PR so we can discuss about it there.

Also: do you need certificates on the device? Are they specific to the device or can they be generic?

The device needs only one root CA cert for the OCPP server. But between OCPP servers, the root certs can differ of course. How do you plan to do certificate management for MQTT? I think the easiest is to just use the same approach for both protocols.

In OCPP, there is also the possibility to have the certificates managed by the server. I will add that as an extra feature in future, but I'm not sure how widely it is used in practice at the moment.

Keep up the great work!

Thanks, I also appreciate how well this project is made!

@matth-x
Copy link
Author

matth-x commented Jun 12, 2024

The initial integration is ready now!

To comment on the latest changes: in 3ee0fb1 I implemented the Mongoose HTTP server lifecycle as described in #82 (comment). In the next commit 71e8412 I recreated the MQTT timer lifecycle that it stops while the WiFi connection is lost. I think that it wouldn't be harmful if that timer continued running (maybe at a lower frequency), but it's no hassle to stop it.

For Smart Charging, the connector lock (and RFID authorization) I implemented "OCPP-only" modes, i.e. OCPP takes over full control over the connector lock and RFID (if in mode EnableOne or EnableAll) and the load balancer if the load balancer is disabled.

For firmware updates and diagnostics uploads I finally made the built-in solution of MicroOcpp ready (which will also be tested in other installations). We could, however, customize the OTA process and add more hardware diagnostics, if we have use cases which require that.

@fluppie
Copy link

fluppie commented Jun 16, 2024

Nice, tried both the fork of @matth-x and the merged one here. With the fork of Matthias I can connect to E-Flux
OCPP_1

With the merged one here I doesn't connect. Pretty weird?

First test with remotestart and remotestop seems to work. Only to be shut off after 1-2s since the station and tag is not authorized. But I hear the contactor clicking.

afbeelding

afbeelding

Tagging @dingo35 @mstegen so they know I'm already fiddling around.

They only thing that I cannot get to work is the RFID reader. Looks like the local rfid.txt list is disabled/ignored (what I've also read here in the conv.) but the RFIDLastRead is not getting updated when holding tags in front of the reader. So looks like it's really shut off?

@matth-x
Copy link
Author

matth-x commented Jun 16, 2024

Hi @fluppie, thanks for taking a look at the OCPP integration!

The existing RFID whitelisting is fully bypassed in this first integration. OCPP catches the RFID events and doesn't forward them to SmartEVSE again. So RFIDLastRead remains blank. But you should see Authorize messages arriving at the server.

To use RFID-based authorization with OCPP, the RFID settings must be "EnableOne" or "EnableAll". To use OCPP Smart Charging, the SmartEVSE load balancer must be turned off so that it's in mode "0". Not too user friendly, but should do the job in the beginning.

@fluppie
Copy link

fluppie commented Jun 16, 2024

I can't get the RFID stuff to work. Tried it on a real one connected to a vehicle and then on a EVSE next to my computer with only an RFID reader connected. is there a way to see if the EVSE is actually reading a tag? Maybe we should flash the red and green LED's on the reader at the same time when a tag is read? (that's more towards @dingo35 and @mstegen ) So we get visual feedback that something is happening. For example on the Alfen Eve charging points there's also a buzzer that beeps when you hold a tag in front of the reader. Some "succes" beep and a "rejected" beepbeep.

BTW did you also try to build the merged version here? when I compile from Dingo35's repo it will not connect to the backoffice. Compiling from your repo works.

@mstegen
Copy link

mstegen commented Jun 18, 2024

Just noticed the RFID readers we use send 'only' the 6 LSB of the UID, while Mifare cards can have a 4, 7 or 10 byte UID.
With the SteVe simulator this works fine, but it might not with a real backend.
Will ask the manufacturer of the RFID readers is there is a way to extend the number of bytes that can be read.

Yes good idea. The LED should flash if the card is accepted or not.

@fluppie
Copy link

fluppie commented Jul 9, 2024

@mstegen I installed Steven on a VM and have it running, but I don't see any RFID information being communicated to the platform. At least I don't see anything, but could be due to insufficient knowledge of Steve.
I did not fill in the auth key field, don't know if this is needed?

EDIT: I kind of have it working. Only the reaction times for the starting and stopping of a session are bad. Or maybe it sometimes misses/skips a RFID read and send of the tag id. But in general it's working :).

afbeelding

@dingo35
Copy link
Owner

dingo35 commented Jul 9, 2024

@fluppie, I am following the same path and I can't see any RFID info either.....

@dingo35
Copy link
Owner

dingo35 commented Jul 9, 2024

@fluppie, how many bytes is the RFID tag you had to define in OCCP? Does it correspond to the 6 bytes in the SmartEVSE logs or did you have to find out the 7th byte?

@mstegen
Copy link

mstegen commented Jul 9, 2024

Testing with the Steve implementation Matthias provided. If i try to authenticate with a RFID card, the ID is visible on the serial output of the SmartEVSE:

[MO] Send: [2,"1000003","Authorize",{"idTag":"04656B82EB2CDD"}]
[MO] Recv: [3,"1000003",{"idTagInfo":{"status":"Invalid"}}]

When i go to the SteVe webserver, Data Management -> OCPP tags, then click on 'Unknown Tags (i)' The ID is visible here.
Then click on Add card (or something like that)

Now the card should be accepted by the SmartEVSE.
But just like fluppie, i noticed you'll have to tap the card very quickly, otherwise it will go to Enable and then immediately back to Disable.

[MO] Send: [2,"1000016","Authorize",{"idTag":"04656B82EB2CDD"}]
[MO] Recv: [3,"1000016",{"idTagInfo":{"status":"Accepted","expiryDate":"2024-07-09T11:15:05.268Z"}}]
[MO] Send: [2,"1000017","StartTransaction",{"connectorId":1,"idTag":"04656B82EB2CDD","meterStart":25,"timestamp":"2024-07-09T10:15:03.305Z"}]
[MO] Recv: [3,"1000017",{"transactionId":173,"idTagInfo":{"status":"Accepted","expiryDate":"2024-07-09T11:15:05.591Z"}}]
[MO] Send: [2,"1000018","StatusNotification",{"connectorId":1,"errorCode":"NoError","status":"SuspendedEV","timestamp":"2024-07-09T10:15:03.538Z"}]

@fluppie
Copy link

fluppie commented Jul 9, 2024

Yes it only started working when I made a user and assigned a tag to a user.

16:08:51.529 -> [MO] Send: [2,"1000005","Authorize",{"idTag":"5CED2D290000AF"}]
948A17C300002F
5CED2D290000AF

RFIDLastRead
948A17C30000
5CED2D290000

afbeelding

Is rfid.txt already ready to support the 7th byte? Or maybe we shouldn't be using the 7th at all, since an Alfen doesn't read it either.
Would be nice if we can support a local list via de txt file for when the (internet) connection between EVSE and Steve/Backend cuts and charging can still be started/stopped.

Maybe we should think of some debounce for sending the tag id? 1000ms? 2000ms?

@dingo35
Copy link
Owner

dingo35 commented Jul 9, 2024

Can you document that Alphen only uses 6 bytes for OCPP?
It might be needed when talking to backend providers...

@dingo35
Copy link
Owner

dingo35 commented Jul 9, 2024

I just pushed a 1,5s debounce 3e963e9

@fluppie
Copy link

fluppie commented Jul 10, 2024

Nope, with a Robocharge tag it's longer.

Can we flash the led green or red when the tag is valid or invalid? Based on that idTagInfo message?

20:18:50.730 -> [MO] Send: [2,"1000009","Authorize",{"idTag":"948A17C300002F"}]
20:18:50.776 -> [MO] Recv: [3,"1000009",{"idTagInfo":{"status":"Invalid"}}]

@matth-x
Copy link
Author

matth-x commented Jul 11, 2024

@fluppie Yes that's already on the road map for the next revision.

@dingo35
Copy link
Owner

dingo35 commented Jul 11, 2024

@matth-x would you be so kind to apply this patch to your mongoose adapter, so we can upgrade to mongoose 7.14:

mongoose714.zip

@matth-x
Copy link
Author

matth-x commented Jul 11, 2024

@dingo35 Thank you for the contribution! The new commit hash is dae388fe04beff6174b7fc30bc67dd4ad8343df3

@matth-x matth-x mentioned this pull request Jul 12, 2024
@matth-x
Copy link
Author

matth-x commented Jul 12, 2024

Moving the integration + discussion to a new PR (this branch and dingo35/master have diverged quite a bit): #97

@matth-x matth-x closed this Jul 12, 2024
@matth-x matth-x mentioned this pull request Jul 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants