Really Smart Box : Solution de Stockage Intelligente Connectée IoT
Composants et fournitures
| Plaque acrylique (ex: 3 mm Perspex) | ×1 |
| Arduino MKR FOX 1200 | ×1 |
| Amplificateur Cellule de Charge HX711 (SparkFun) | ×1 |
| Cellule de charge 5 kg | ×2 |
| Capteur BME280 (Adafruit) | ×1 |
Outils et machines nécessaires
| Découpeuse laser |
| Imprimante 3D |
Applications et services en ligne
| Tinamous (plateforme IoT) |
| Sigfox (réseau bas débit) |
À propos de ce projet
La plateforme Really Smart Box transforme une boîte « Really Useful Box » (marque déposée) en une boîte de stockage intelligente, connectée à Internet pour la surveillance de stock. Basée sur l'Arduino MKR FOX 1200, elle mesure le poids du contenu, la température et l'humidité, et transmet ces données via le réseau Sigfox basse consommation.
Cas d'usage – Stockage filament imprimante 3D
Si vous possédez une imprimante 3D, vous savez l'importance du stockage du filament (et d'autres matériaux sensibles à l'humidité/température, comme les mastics qui gèlent). En tant que responsable des imprimantes 3D dans mon makerspace local, je dois m'assurer qu'on a assez de filament et qu'il reste sec.
Avec la Really Smart Box, je surveille le poids du filament (pour savoir s'il faut en recommander) et l'humidité dans la boîte (pour savoir quand remplacer le gel de silice).
Cas d'usage – Gestion de stock de consommables
Une entreprise de nettoyage peut vouloir stocker des consommables (désodorisants, savon) chez un client. Le client n'autorisera pas l'accès Wi-Fi ni l'alimentation d'un appareil en son absence. L'entreprise doit savoir quand réapprovisionner, ce qui ajoute de la paperasse.
La Really Smart Box se glisse simplement dans la boîte de stockage. Grâce à Sigfox, elle n'a pas besoin du réseau du client et fonctionne sur piles. Comme le contenu change peu, l'Arduino reste en veille basse consommation la plupart du temps, prolongeant l'autonomie.
On programme le poids unitaire de l'article (ex: un désodorisant) pour calculer le nombre d'unités restantes. L'alerte est envoyée à l'entreprise quand le stock est bas.
Fabrication de la plateforme
Construction simple : deux plaques acryliques découpées au laser (3 mm, plus épais recommandé pour charges lourdes) avec deux cellules de charge entre elles.
J'ai fraisé manuellement les trous de vis des cellules de charge pour une finition propre. La plaque supérieure est légèrement plus petite pour ne pas accrocher les parois. La plaque inférieure a une découpe pour loger l'électronique sans entretoises. J'ai taraudé les coins (M3) pour visser le PCB directement. Des pieds imprimés 3D aux coins évitent que les vis des cellules ne déséquilibrent l'ensemble.
Le poids est mesuré par deux cellules de charge 5 kg (généralement 4 dans les pèse-personnes, mais 2 suffisent ici). Elles nécessitent un rembourrage haut/bas pour permettre la flexion. Les jauges de contrainte (partie blanche) sont plus épaisses que le bloc de fixation. C'est géré par :
- Sous la cellule : deux plaques d'extrémité « Really Smart Box » imprimées 3D avec un petit bloc de surélévation.
- Sur la cellule : blocs de rembourrage en acrylique découpés au laser.
Les cellules sont connectées à un amplificateur HX711 (double canal A et B, parfait pour deux cellules).
Chaque cellule est un pont de Wheatstone. Sous charge, la résistance des jauges change, créant une différence de potentiel amplifiée et numérisée par le HX711. J'utilise deux cellules 5 kg (d'autres plages existent : 1 kg, 10 kg, même principe, sensibilité différente).
Montage important : La flèche sur la cellule doit pointer vers le bas (direction de la charge). Trous M5 (côté fixe) et M4 (côté charge). Fils rouge/noir = alimentation (commun aux deux cellules). Vert/blanc = signaux vers canaux A et B du HX711.
Le HX711 offre 3 gains : 128 et 64 sur canal A, 32 sur canal B (le canal B est donc moins sensible, acceptable ici). Connecté sur broches numériques D0 (Data) et D1 (Clock), alimenté en 3,3 V de l'Arduino.
Voir le tutoriel excellent de SparkFun sur les cellules de charge et HX711.
Un BME280 sur bus I2C mesure température et humidité dans la boîte (la pression est aussi possible mais non transmise, limite de 12 octets Sigfox).
L'électronique est sur carte prototype ThingySticks, avec porte-pile (collé à chaud sur la plaque inférieure) et antenne plate intégrée.
Étalonnage des cellules de charge
Chaque cellule est unique (jauges collées sur métal percé pour fléchir sans casser). Il faut l'étalonner pour sa réponse au poids.
On applique la formule y = m×x + c (x = valeur ADC, y = poids réel). Il faut trouver c (offset) et m (pente).
J'ai retiré la plaque supérieure, fixé un petit carré acrylique sur chaque cellule tour à tour, et lu les valeurs (routine dans le firmware, déclenchée par « c » sur le port série).
- Mesure à vide → offset (c).
- Pose un poids connu → différence de lecture → pente.
Pente = (valeur_mesurée - offset) / poids (g)
Testé avec une bombe aérosol (~230 g) et une bobine de filament (~1,5 kg) : même pente, rassurant.
L'offset avec le petit carré diffère de celui avec la plaque complète, de même pour la pente avec deux cellules. Un second étalonnage est nécessaire. Pour l'instant, une mise à zéro (tare) à un point est utilisée, définie dans le firmware mais modifiable via USB série ou downlink Sigfox une fois déployé.
Connexion Sigfox
Au début, j'utilisais le port série USB pour déboguer. Mais en déploiement, la boîte doit être 100 % sans fil.
Sigfox permet d'envoyer 12 octets jusqu'à 140 fois/jour, amplement suffisant. Structure de données utilisée (12 octets) :
typedef struct __attribute__ ((packed)) sigfox_message {
uint8_t status; // drapeaux de statut
int8_t humidity; // humidité (int8, certains capteurs donnent des valeurs négatives)
int8_t temperature; // température (int8, pas de décimales)
int16_t zeroWeight; // poids zéro (little-endian)
int16_t weight; // poids actuel (little-endian)
int16_t itemCount; // nombre d'articles ×100 (little-endian)
int8_t driftCorrection;// correction dérive
int8_t filler; // réservée
int8_t lastStatus; // dernier statut Sigfox
} SigfoxMessage;
Le premier octet (status) est découpé en 8 drapeaux :
B7: Premier démarrage
B6: Défaut HX711
B5: Défaut BME280
B4: Alarme température
B3: Alarme humidité
B2: Alarme poids
B1: Stock bas
B0: Réservé
Configuration « Custom Payload » Sigfox pour dépaqueter :
firstRun::bool:7 hx711Fault::bool:6 bmeFault::bool:5 temperatureAlarm::bool:4
humidityAlarm::bool:3 weightAlarm::bool:2 lowStock::bool:1 b0::bool:0
status::int:8 humidity::int:8 temperature::int:8
zeroWeight::int:16:little-endian weight::int:16:little-endian itemCount::int:16:little-endian
Note : Il faut spécifier little-endian pour les valeurs >1 octet (Sigfox utilise big-endian par défaut, Arduino est little-endian). L'extraction des booléens du premier octet n'avance pas le curseur, donc on relit l'octet status pour le sauter.
Les alarmes température/humidité/poids sont détectées sur l'appareil et envoyées (puis réinitialisées après envoi réussi), car les messages peu fréquents (1-2/jour) pourraient rater une excursion courte.
Le itemCount vaut 100× le nombre réel d'articles pour permettre des valeurs comme 2,2 (erreur de poids ou objets parasites) sans arrondi forcé. Un float prendrait plus de place. La valeur est signée pour gérer les erreurs de zéro (stock -1, -2...).
Pour activer Sigfox : ajoutez la bibliothèque Sigfox dans l'Arduino, appelez les fonctions d'envoi comme dans les exemples. Enregistrez le dispositif sur le backend Sigfox.
Envoyez « s » sur le port série pour afficher l'ID et le PAC Sigfox, puis activez sur backend.sigfox.com/activate.
Dispositif activé :
Sigfox groupe les dispositifs par « Device Type ». Configurez un « Custom Callback » pour pousser les données vers Tinamous (mon choix, voir mon profil).
Utilisation de la Really Smart Box
Une fois câblée, assemblée, firmware flashé, piles en place : glissez la plateforme dans une Really Useful Box, c'est prêt.
Alimentez le plus tard possible : dès la mise sous tension, le premier message Sigfox part après 2 min avec demande de downlink (peut inclure commande « Zero » pour tarer). Sinon, attendez le prochain downlink (toutes les 12 h) ou connectez en USB.
En fonctionnement normal : envoi toutes les 15 min (poids, nombre d'articles, température, humidité, alarmes). Mesures toutes les minutes pour détecter les dépassements de seuils (alarmes envoyées au message suivant).
Supervision avec Tinamous
Tinamous gère les callbacks Sigfox via un « Sigfox Bot » (voir mon tutoriel Hackster.io « Get Your Sigfox On »).
Ajoutez le Bot, incluez les réglages API pour importer vos dispositifs. Vous accédez à l'écran de configuration du callback.
Créez un callback custom sur Sigfox (type DATA → BIDIR pour uplink + downlink). Collez le « Custom Payload » ci-dessus et mettez à jour les champs.
Lat/Lng dans le callback donnent une position approximative. Pour une géolocalisation précise, utilisez un second callback (ne mettez pas Lat/Lng ici sinon la boîte semblera bouger).
Activez le Downlink (désactivé par défaut même avec BIDIR).
Ajoutez aussi un callback SERVICE → ACKNOWLEDGE pour confirmer la réception du downlink. Dans Tinamous, cliquez sur le Sigfox Bot pour voir les autres configs. Copiez l'en-tête d'autorisation du premier callback (mot de passe chiffré dans Tinamous).
Une fois configuré, les données arrivent dans Tinamous. Des callbacks email sur Sigfox peuvent aider au débogage (mais deviennent vite bruyants).
Configuration du dispositif dans Tinamous
Une fois le dispositif vu par Tinamous (via API ou callback), il apparaît sur la page Devices. Modifiez ses propriétés. Les champs sont ajoutés automatiquement à l'arrivée des données.
J'ai mis « Not Reporting After » à 1 heure (envoi toutes les 15 min) pour être alerté si la box ne répond plus.
Je n'affiche que le poids et le nombre d'articles sur le graphique (beaucoup de champs sinon). Labels et unités conviviaux appliqués.
Le champ Item Count vaut 100× la valeur réelle → calibration divisant par 100.
Des données downlink sont prêtes : commande « Zero » et seuils température/humidité, appliquées au prochain downlink (2 min après mise sous tension, puis toutes les 12 h).
Visualisation des informations
Page détails du dispositif (la plateforme n'était pas tarée ici, d'où 0,5 unité fantôme ; j'ai aussi remplacé le couvercle acrylique 3 mm par 5 mm pour supporter le filament).
Onglet Sigfox : interactions callbacks. Le downlink est envoyé et acquitté mais l'Arduino signale une erreur (voir « Problèmes à résoudre »).
Onglet Localisation : position approximative de la box (utile si oubli chez quel client ou dans quel véhicule).
Tableau de bord : poids du contenu, unités estimées, compteur de dispositifs ne rapportant plus.
Notifications avec Tinamous
Configuration d'alertes email/SMS quand le stock est bas. Plage de travail définie à 3–300 pour Item Count. Hors plage = événement « measurement out of range ».
Ajout d'une notification :
Champ laissé vide = notification pour tout champ hors plage.
Dispositifs : laissé vide = tous les dispositifs.
Répétition : une seule fois jusqu'à réinitialisation (quotidienne), sinon alerte toutes les 15 min !
Canaux : email + SMS.
Conclusion
Je peux maintenant déployer la Really Smart Box et l'oublier. Je serai notifié quand le stock sera bas, et pourrai vérifier le tableau de bord. Sigfox élimine les soucis d'alimentation (changement piles occasionnel) et de configuration Wi-Fi sur site, rendant le déploiement trivial.
Je prévois de déployer cette unité dans une boîte de stockage de filament au Cambridge Makespace.
Problèmes à résoudre
Ce n'est pas un produit fini, quelques points restent :
- Sigfox Downlink : 4 messages/jour possibles en réponse à un uplink. Utilisés pour re-tarer, définir seuils T°/H% et poids unitaire. Mais l'Arduino renvoie erreur 62 (non documentée pour la puce ATA8520). La commande utilise une requête downlink non listée dans la datasheet. Investigation nécessaire.
- Mode Debug seulement : Sans debug, la mise en veille basse consommation coupe le port série USB.
- Mode Basse Conso : La bibliothèque Low Power d'Arduino coupe l'USB série, donc désactivée pour l'instant.
- Dérive : Pas de compensation de dérive (les cellules dérivent sous charge constante).
- Bruit / Mesures non verticales : La box peut être dans un véhicule (artisan mobile). Il faudrait un accéléromètre pour ignorer les cycles de mesure quand la box n'est pas stable ou pas verticale (poids mal réparti sur les cellules).
Code
- Code Arduino Really Smart Box
Really Smart Box Arduino Code Arduino
Ajoutez les bibliothèques : Arduino MKR FOX 1200, HX711, Adafruit BME280, Arduino Low Power. Programmez via l'IDE Arduino normalement.
// Really Smart Box
// Mesure le poids du contenu d'une Really Smart Box
// Deux plaques acryliques avec 2 cellules de charge entre elles
// Dans une Really Useful Box.
// Inclut un BME280 pour température et pression dans la boîte.
// Auteur: Stephen Harrison
// Licence: MIT
#include <Adafruit_BME280.h>
#include <HX711.h>
#include <SigFox.h>
#include <ArduinoLowPower.h>
// --------------------------------------
// BME280 sur port I2C
Adafruit_BME280 bme;
// --------------------------------------
// HX711 amplificateur cellule de charge
// 0: D0 - DOUT
// 1: D1 - CLK
// gain initial 128
HX711 scales(0, 1, 128);
// Tableaux pour les 2 canaux. Index 0 = Canal A, Index 1 = Canal B
float gain[] = {128, 32};
// Facteurs d'étalonnage: y = m*x + c (c = offset, m = scaleFactor)
// Offset mesuré sans charge sur les cellules
float offset[] = {0, 54940};
// Facteur d'échelle: placer un poids connu, scaleFactor = valeur_mesuree / poids
float scaleFactor[] = {378.f, 260.9f};
// --------------------------------------
// Sigfox
// Structure de données publiée (12 octets)
// Les bits du premier octet sont extraits en booléens mais l'octet doit être inclus
// firstRun::bool:7 hx711Fault::bool:6 bmeFault::bool:5 temperatureAlarm::bool:4
// humidityAlarm::bool:3 weightAlarm::bool:2 lowStock::bool:1 b0::bool:0
// status::int:8 humidity::int:8 temperature::int:8
// zeroWeight::int:16:little-endian weight::int:16:little-endian itemCount::int:16:little-endian
typedef struct __attribute__ ((packed)) sigfox_message {
uint8_t status; // B7:First run, B6:HX711 fault, B5:BME280 fault, B4:Temp alarm, B3:Hum alarm, B2:Weight alarm, B1:Low stock, B0:spare
int8_t humidity;
int8_t temperature;
int16_t zeroWeight;
int16_t weight;
int16_t itemCount; // 100x nombre réel pour permettre 2.01 sans arrondi forcé
int8_t driftCorrection;
int8_t filler;
int8_t lastStatus;
} SigfoxMessage;
long lastPublish = 0;
long lastDownlink = 0;
uint8_t lastSigfoxStatus = 0;
bool isFirstCycle = true;
int mode = 0; // 0: Normal, 1: Calibration
int calibrate_channel = 0;
float lastAverage[] = {0, 0};
float currentWeight = 0;
float unitWeight = 238; // Modifiable via downlink
float zeroWeight = 0; // Modifiable via downlink
bool bmeOk = true;
bool hx711Ok = true;
float minTemperature = 5.f, maxTemperature = 60.f;
float minHumidity = 0.f, maxHumidity = 60.f;
float maxWeight = 10000; // 10 kg
bool temperatureAlarm = false, humidityAlarm = false, weightAlarm = false;
float currentTemperature = 0, currentHumidity = 0, stockLevel = 0;
bool lowStock = false;
float minStock = 5;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
bmeOk = bme.begin(0x76); // Mon capteur eBay n'utilise pas l'adresse par défaut
if (!bmeOk) Serial.println("Capteur BME280 introuvable !");
delay(5000);
Serial.println("Really Smart Box...");
printHeader();
}
int delayCounter = 0;
void loop() {
switch (mode) {
case 0: measureAndPublish(); delay(60 * 1000); break; // LowPower.sleep cause problèmes USB
case 1: calibrate(); delay(1000); break;
}
checkSerial();
delay(100);
}
// ... (fonctions measureAndPublish, measureWeight, updateStockLevels, checkWeightLimits,
// readDelta, readChannel, printHeader, calibrate, checkSerial, measureTemperatureAndHumidity,
// printBmeValues, shouldPublish, publishMeasurements, printSigfoxStatus, buildMessage,
// getStatusFlags, shouldUseDownlink, parseDownlinkData, printSigfoxModelDetails,
// resetAlarms, zeroScales, updateTemperatureAlarm, updateHumidityAlarm, updateUnitWeight)
// Code complet disponible sur le dépôt GitHub.
Dépôt GitHub Really Smart Box
https://github.com/Tinamous/ReallySmartBox
Pièces personnalisées et boîtiers
Fichier de découpe pour les plaques acryliques supérieure/inférieure : cuttingguide_e7GNHf980M.svg
Pièce intercalée entre plaque inférieure et cellule de charge pour la surélever et donner un bord à la plateforme. Imprimer 4 exemplaires (un par coin de la plaque inférieure) si nécessaire.
Schémas
Rien de très complexe.
Processus de fabrication
- La révolution de la brique de jus : histoire, conception et impact environnemental
- Enregistreurs de Vol « Boîte Noire » : Des Premiers Prototypes aux Conceptions Intégrées Futures
- Cellules solaires : des premières expériences au photovoltaïque moderne – Technologie, production et perspectives
- Barman intelligent : Construisez votre propre station de cocktails alimentée par Raspberry Pi
- Feu de circulation intelligent : Éclairage public adaptatif alimenté par l'IoT
- Poubelle intelligente : Optimiser la collecte des déchets avec capteurs IoT
- Robot DIY inspiré de Wall‑E : Raspberry Pi dans une boîte de CD recyclée
- Kit Stérilisateur UVC DIY – Boîte UV Faible Coût, Facile à Construire pour Objets Médicaux et Personnels
- Serre connectée IoT : Plante intelligente avec Arduino et Azure