Architecture OCPP 1.6 - Comment une borne parle au cloud
Un regard pratique sur la connexion entre bornes de recharge et systèmes centraux via OCPP 1.6 : WebSockets, heartbeats et notifications de statut.
Architecture OCPP 1.6. Comment une borne parle au cloud
Chaque borne de recharge que vous avez utilisée fait la même chose au démarrage : elle ouvre un WebSocket vers un serveur et dit "Je suis là." C'est OCPP résumé en une phrase. Le reste, ce sont des détails. mais c'est dans les détails que ça casse.
Les deux acteurs
OCPP 1.6 est un protocole client-serveur. La borne (Charge Point) est toujours le client. Le système central (votre backend / CSMS) est le serveur. La borne se connecte en sortant. jamais l'inverse. C'est important pour les pare-feux : une borne derrière un NAT ou un modem 4G fonctionne sans problème parce qu'elle initie la connexion.
Rendering diagram...
Le cycle de vie WebSocket
OCPP 1.6J (la variante JSON. la seule que les gens déploient aujourd'hui) tourne sur WebSocket. Voici la séquence de démarrage :
- La borne ouvre un WebSocket vers
wss://votre-backend.com/ocpp/CP001 - La borne envoie
BootNotification. fabricant, modèle, numéro de série, firmware - Le backend répond
Accepted(ouPending/Rejected) plus un intervalle de heartbeat - La borne commence à envoyer des heartbeats à cet intervalle
- Les deux côtés peuvent maintenant échanger librement
Rendering diagram...
WebSocket est persistant et bidirectionnel. Contrairement au HTTP classique, le backend peut envoyer des commandes à la borne quand il veut. "lance une charge", "déverrouille ce connecteur", "mets à jour ton firmware". Sans cette capacité de push, la gestion de flotte ne fonctionne pas.
Structure des messages
Chaque message OCPP 1.6J est un tableau JSON. Trois types :
Call (requête) :
[2, "uuid-123", "BootNotification", {"chargePointVendor": "Acme", "chargePointModel": "FC-50"}]
CallResult (réponse) :
[3, "uuid-123", {"status": "Accepted", "currentTime": "2026-01-07T10:00:00Z", "interval": 300}]
CallError :
[4, "uuid-123", "InternalError", "Quelque chose a mal tourné", {}]
Premier élément : 2 = requête, 3 = réponse, 4 = erreur. L'uuid lie une réponse à sa requête. C'est toute l'enveloppe. Pas de namespaces XML, pas de headers SOAP. juste un tableau JSON. Cette simplicité explique largement pourquoi 1.6J a gagné face à 1.6S.
Heartbeats. plus utiles qu'ils n'en ont l'air
La borne envoie un Heartbeat à intervalles réguliers. Le backend répond avec l'heure actuelle. Deux choses se passent :
- Le backend sait que la borne est vivante. Si les heartbeats s'arrêtent, quelque chose cloche.
- La borne synchronise son horloge. Beaucoup de bornes n'ont pas de NTP fiable, donc l'heure du backend devient la référence pour les timestamps des transactions et des relevés de compteur.
J'ai déjà débogué des écarts de facturation qui remontaient à des bornes dont l'horloge avait dérivé parce qu'elles avaient perdu leur serveur NTP et que l'intervalle de heartbeat était trop élevé. En production, 300 secondes ou moins.
Notifications de statut et la machine d'états
Chaque fois que quelque chose change sur un connecteur. câble branché, charge démarrée, panne détectée. la borne envoie un StatusNotification.
Rendering diagram...
Cette machine d'états pilote l'interface des applis de recharge. Quand un utilisateur ouvre son appli et voit "Connecteur 1 : Disponible". ça vient d'un StatusNotification.
Séquence complète boot-to-charge
Rendering diagram...
C'est tout le cycle de vie. Boot, heartbeat, statut, autorisation, démarrage, compteur, arrêt. Chaque déploiement OCPP 1.6 dans le monde fait tourner cette boucle, des milliers de fois par jour.