Cet article date de 2021. Je l’avais écrit sous forme de notes, il est resté en brouillon pendant cinq ans. J’ai repris ces notes pour les mettre au propre et publier l’article. Le projet reste un POC et les versions utilisées sont celles de l’époque.


Je voulais savoir si je pouvais fabriquer un tableau de bord pour ma machine CNC : quelles informations je peux récupérer, quelles sont les possibilités et les limites. Je cherchais quelque chose de rapide à mettre en place pour voir si cela valait le coup d’y investir du temps.

1. Le besoin

Ce que je veux obtenir :

  • savoir où en est un usinage et quand il se termine
  • suivre les opérations en cours
  • suivre quelques indicateurs clés à intervalles réguliers
  • avoir les données les plus utiles en un coup d’œil

Pour ce POC, je récupère les données exposées par l’API de cncjs (cnc.js.org), le logiciel avec lequel je pilote la machine.

2. Le matériel et les outils

  • Machine : ma fraiseuse à commande numérique C-Beam Machine. Elle est pilotée par un Arduino Uno avec le firmware grbl.
  • Contrôleur : cncjs, installé en local sur mon PC.
  • Stockage : InfluxDB, une base de données orientée séries temporelles. Chaque valeur enregistrée reçoit automatiquement un timestamp, ce qui convient bien pour suivre l’évolution d’un usinage au fil du temps.
  • Affichage : Grafana. Je l’avais déjà utilisé, il est très modulable et je savais qu’il ferait l’affaire.
  • Collecte : un programme Python qui lit l’API de cncjs et écrit dans InfluxDB.

Pour tester sans être à côté de la machine, je n’ai pas branché la CNC à mon PC. J’ai utilisé un Arduino Nano avec le même firmware grbl.

arduino-nano.jpg

3. Accéder à l’API de cncjs

La première étape était de savoir comment contacter l’API et ce qu’elle renvoie.

Je lance cncjs et je crée un compte utilisateur, par exemple admin, dans les paramètres : http://127.0.1.1:8000/#/settings/user-accounts

cncjs-user-accounts.png

Une fois la session activée, je récupère le token dans http://127.0.1.1:8000/#/settings/workspace

cncjs-workspace

Avec le nom d’utilisateur et le token je peux appeler l’API :

http://127.0.1.1:8000/api/controllers?name=admin&token=<TOKEN>

Avant de me connecter au port série j’ai dû ajouter l’utilisateur cncjs au groupe uucp qui a les droits sur le port :

ls -l /dev/ttyUSB0
crw-rw---- 1 root uucp 188, 0 15 mai   21:00 /dev/ttyUSB0
usermod -a -G uucp cncjs
cat /etc/group
uucp:x:987:cncjs

Les droits sont bons et la carte est branchée, je peux me connecter au port série depuis cncjs.

cncjs-connection

Pour mes tests j’ai utilisé un fichier G-code qui dessine un cercle : circle.gcode

4. Installer InfluxDB et Grafana

Le plus rapide pour déployer les deux est un docker-compose :

---
services:
  influxdb:
    image: influxdb:1.8
    container_name: influxdb
    restart: always
    ports:
      - "8086:8086"
    environment:
      INFLUXDB_REPORTING_DISABLED: 'true'
      INFLUXDB_ADMIN_ENABLED: 'true'
      INFLUXDB_ADMIN_USER: admin
      INFLUXDB_ADMIN_PASSWORD: admin
      INFLUXDB_DB: cnc
    volumes:
      - ./influxdb:/var/lib/influxdb

  grafana:
    image: grafana/grafana:7.4.0
    container_name: grafana
    restart: always
    ports:
      - "3000:3000"
    user: "0"
    links:
      - influxdb
    volumes:
      - ./grafana:/var/lib/grafana

Le seul point d’attention pour connecter InfluxDB dans Grafana est l’URL. Grafana tourne dans un conteneur, il faut donc utiliser http://influxdb:8086 (avec « Access: Server »), et pas localhost.

5. Le script d’enregistrement

Le rôle du script Python est de faire le lien entre cncjs et InfluxDB.

Le script : cnc-recorder.py

Il tourne en boucle et répète le même cycle toutes les 3 secondes :

  1. Il interroge l’API de cncjs, avec l’URL, le nom d’utilisateur et le token vus plus haut.
  2. Il lit l’état de la machine.
  3. Si la machine est en train d’usiner, il récupère les informations du programme en cours.
  4. Il envoie ces informations à InfluxDB, qui tourne dans Docker et reste joignable sur localhost:8086.

cncjs indique aussi dans quel état se trouve la machine : au repos (Idle), en train d’exécuter un programme (Run) ou en pause (Hold). Le script ne s’intéresse qu’à un seul cas : Run. Tant que la machine usine, il enregistre les informations. Le reste du temps il n’enregistre rien pour ne pas remplir la base de valeurs sans intérêt.

Les informations envoyées à InfluxDB sont :

  • elapsed_time : le temps écoulé.
  • remaining_time : le temps restant.
  • received_ligne : le nombre de lignes reçues par cncjs.
  • total_ligne : le nombre total de lignes du programme.
  • tool_id : le numéro de l’outil.
  • file_name : le nom du fichier G-code.

Quand la machine quitte l’état Run le script écrit une dernière ligne avec des valeurs à zéro. Cela remet les indicateurs à zéro dans Grafana.

Chaque usinage est aussi associé à un identifiant (job), enregistré avec les données. J’en ai besoin pour calculer le temps de fonctionnement cumulé, plus bas.

Quelques commandes utiles pour manipuler la base :

# Se connecter à la base
docker exec -it influxdb /bin/bash
influx
use mydb
select * from mycnc
# Écrire une donnée à la main
curl -i -XPOST "http://localhost:8086/write?db=mydb" --data-binary 'mycnc,job=0 elapsed_time=0'

Cette ligne mycnc,job=0 elapsed_time=0 est le format utilisé par InfluxDB pour ranger une donnée. Elle se découpe en quatre parties :

mycnc,job=0 elapsed_time=0 1465839830100400200
  |     |         |                 |
  |     |         |                 +-- timestamp : le moment de la mesure
  |     |         +-- champ : la valeur mesurée
  |     +-- tag : l'étiquette qui identifie l'usinage
  +-- mesure : le nom sous lequel les données sont rangées
  • La mesure (mycnc) : le nom de la série de données, comparable à une table dans une base classique.
  • Le tag (job=0) : une étiquette qui permet de retrouver et de regrouper les données. Ici, elle identifie l’usinage ce qui sert pour calculer le temps de fonctionnement cumulé.
  • Le champ (elapsed_time=0) : la valeur mesurée. Le script en enregistre six à chaque fois, ceux listés plus haut.
  • Le timestamp : la date et l’heure de la mesure. InfluxDB l’ajoute automatiquement par défaut. Contenu de la base après un usinage :

influxdb-select-all

6. Le tableau de bord

Le dashboard Grafana est ici : grafana-cncjs-dashboard.json. Grafana rafraîchit les graphiques toutes les secondes.

grafana-cncjs-dashboard

Il contient cinq panneaux.

[Le nom du fichier]
Directement disponible dans l’API (file_name).

[Le numéro de la fraise]
Directement disponible dans l’API (tool_id). cncjs récupère la valeur depuis le G-code donc la commande T doit être définie dans le fichier. Dans mon exemple, la valeur est T12.

[Le temps restant]
Directement disponible dans l’API (remaining_time) en millisecondes. La requête :

SELECT remaining_time FROM mycnc

Pour n’afficher que la dernière valeur on peut utiliser SELECT last(remaining_time) FROM mycnc. L’autre solution est de garder la requête simple et de choisir « Last » dans les options de calcul du panneau Grafana.

[L’avancement]
Cette donnée n’est pas fournie par l’API. Je la calcule avec deux valeurs qu’elle fournit : le numéro de la ligne G-code en cours de lecture (received_ligne) et le nombre total de lignes du fichier (total_ligne). Je divise la ligne courante par le total puis je multiplie par 100 pour obtenir le pourcentage d’avancement :

SELECT mean("received_ligne")/mean("total_ligne")*100 FROM mycnc GROUP BY time($__interval) fill(null)

C’est simple et ça donne une idée générale. Mais cette méthode a des défauts :

    • si le fichier contient beaucoup de lignes de commentaires, le résultat est faussé
    • dans un cas réel, les lignes ne se valent pas : tout dépend de la vitesse d'avance et de la distance du déplacement

[Le temps de fonctionnement cumulé]
Cette donnée n’est pas non plus fournie par l’API. Elle permet de connaître le temps total de fonctionnement de la CNC, utile pour planifier une révision ou un entretien et pour calculer le TRS (Taux de Rendement Synthétique, OEE en anglais).

Pour la calculer je pars du temps écoulé fourni par l’API (elapsed_time). Pour chaque usinage (identifié par son job), je prends la valeur de temps écoulé la plus élevée c’est-à-dire la dernière enregistrée. Puis j’additionne ces valeurs pour tous les usinages :

SELECT Sum(temps) AS cumul FROM (SELECT Max(elapsed_time) AS temps FROM mycnc GROUP BY job)

Le résultat :

influxdb-select-working-time

Le même principe peut servir à connaître le temps de fonctionnement d’une fraise donnée.

7. Conclusion

Pour ce premier essai j’ai pu vérifier que l’API de cncjs fournissait suffisamment de données pour suivre un usinage. Avec un petit programme Python, InfluxDB et Grafana, j’ai pu les exploiter et les regrouper sur un seul écran. Le tout se déploie rapidement avec Docker.

Le concept est validé et il me convient. Il est prêt à être finalisé pour une intégration réelle sur ma machine.