Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Vraiment Smart Box

Composants et fournitures

Feuille acrylique (par exemple, plexiglas 3 mm)
× 1
Arduino MKR FOX 1200
× 1
Amplificateur de cellule de charge SparkFun HX711
× 1
Capteur de 5 kg
× 2
Adafruit BME280
× 1

Outils et machines nécessaires

Coupe laser (générique)
Imprimante 3D (générique)

Applications et services en ligne

Tinamous
Sigfox

À propos de ce projet

La plate-forme Really Smart Box transforme une Really Useful Box (tm) en une boîte de stockage intelligente connectée à Internet pour la surveillance des stocks. Basé sur le Sigfox Arduino MKR FOX 1200, il détecte le poids des objets stockés dans la boîte, ainsi que la température et l'humidité et utilise la radio Sigfox à faible puissance pour relayer ces informations.

Cas d'utilisation - Stockage de filament d'imprimante 3D :

Si vous possédez une imprimante 3D, vous vous soucierez probablement de la façon dont votre filament est stocké, cela est vrai non seulement du filament de l'imprimante, mais de nombreux autres éléments doivent être stockés dans des plages de température et d'humidité acceptables (par exemple, le calfeutrage des peintres peut devenir inutilisable s'il est exposé à des températures inférieures au point de congélation).

En tant que l'une des personnes chargées de la maintenance des imprimantes 3D dans mon espace de fabrication local, je dois m'assurer que nous avons un stock suffisant de filament et qu'il est maintenu au sec.

Avec la Really Smart Box, je suis capable de surveiller le poids du filament et donc de savoir si nous devenions faibles, ainsi que de surveiller le niveau d'humidité dans la boîte pour savoir si le gel de silice doit être remplacé.

Cas d'utilisation - Contrôle des stocks de consommables :

Un entrepreneur de nettoyage peut souhaiter conserver un stock de désodorisant, de savon pour les mains ou d'autres consommables sur le site d'un client, le client ne permettrait probablement pas à l'entrepreneur d'accéder au WiFi ou d'alimenter un appareil comme celui-ci lorsqu'il n'est pas présent, mais l'entreprise de l'entrepreneur doit savoir quand envoyer un nouveau stock, ce qui ajoute des frais généraux au temps des nettoyeurs et de la paperasse supplémentaire que personne n'aime.

La plate-forme Really Smart Box se dépose simplement dans une boîte de stockage, car elle utilise Sigfox, elle n'a pas besoin de se connecter au réseau des clients et sa consommation est faible, elle fonctionne donc à partir d'un ensemble de batteries. Comme le contenu de la boîte change très rarement, l'Arduino peut être maintenu dans un état de faible consommation la plupart du temps, ce qui contribue à prolonger la durée de vie de la batterie.

La plate-forme peut connaître le poids du type d'article stocké dans la boîte (c'est-à-dire les assainisseurs d'air) et donc calculer combien il y a dans la boîte. Cela peut ensuite être envoyé à l'entreprise de nettoyage pour les alerter lorsqu'ils ont besoin de plus de carburant pour être livrés sur le site du client.

Créer la plate-forme :

La plate-forme est de construction simple, le corps principal est composé de deux morceaux d'acrylique découpés au laser (j'ai utilisé 3 mm, plus épais serait mieux pour les articles lourds comme le filament d'imprimante) avec une paire de cellules de charge entre eux.

J'ai fraisé manuellement les trous de vis pour la cellule de charge pour donner une plus belle finition, je n'ai pas encore trouvé de découpeuse laser qui effectue le fraisage !

L'acrylique peut être coupé à n'importe quelle taille pour correspondre à la boîte que vous aimez, mais faites attention au positionnement des cellules de charge et à la longueur de fil sur celles-ci car elle est assez courte. La feuille acrylique supérieure est légèrement plus petite que la inférieure pour éviter qu'elle ne s'accroche sur les côtés de la boîte.

La feuille inférieure a une découpe pour permettre à l'électronique d'être montée sur l'acrylique sans entretoises et d'avoir les pattes de l'appareil soudées à travers. J'ai utilisé un taraud M3 sur les sections d'angle de la découpe pour y boulonner directement le PCB. Des pieds imprimés en 3D ont également été installés dans les coins pour s'assurer que les vis de la cellule de charge qui n'étaient pas au ras de l'acrylique n'affectaient pas l'équilibre.

Le poids est détecté à l'aide de deux cellules de charge de 5 kg. Un ensemble de 4 est normalement utilisé dans les balances de salle de bain et cela aurait pu être mieux, mais je n'ai pas pu trouver un bon moyen de les fixer à l'acrylique ainsi que de fournir l'espacement requis pour l'électronique.

Les cellules de charge ont besoin d'un rembourrage en haut et en bas pour permettre une petite flexion et les capteurs de jauge de contrainte réels (le bit blanc sur l'image) de la cellule de charge sont plus gros que le bloc de montage. Ceci est réalisé sous la cellule de charge avec les deux plaques d'extrémité imprimées en 3D "Really Smart Box" qui ont un petit bloc pour soulever la cellule de charge, sur le dessus de la cellule de charge se trouvent des blocs de rembourrage en acrylique découpés au laser.

Les cellules de charge sont connectées à un amplificateur de cellule de charge HX711. Celui-ci dispose de deux canaux (A et B) pouvant être sélectionnés, ce qui est parfait pour cette utilisation.

Chaque cellule de charge est constituée de jauges de contrainte dans une configuration de pont de Wheatstone, cela crée une paire déséquilibrée de diviseurs de potentiel, lorsque la cellule de charge est placée sous charge, la résistance des jauges de contrainte change et donc une différence entre les deux diviseurs de potentiel est créée , ceci est amplifié et mesuré par le HX711 qui effectue la conversion analogique-numérique pour nous.

J'ai utilisé deux cellules de charge de 5 kg pour ce projet, vous pouvez obtenir différentes notes (par exemple 1 kg et 10 kg) qui fonctionnent exactement de la même manière mais avec une sensibilité différente.

Lorsque vous placez les cellules de charge, assurez-vous que la flèche à l'extrémité de la cellule pointe vers le bas (dans la direction de la charge). Les cellules ont des trous taraudés M5 à une extrémité (généralement l'extrémité fixe) et M4 à l'autre (le côté sur lequel vous placez la charge.

Les fils rouge/noir sont l'alimentation, cela alimente le haut et le bas des diviseurs de potentiel et est partagé entre les deux cellules de charge. Le vert et le blanc sont les fils de détection du milieu des diviseurs de potentiel, ceux-ci sont connectés aux canaux A et B sur le HX711.

Le HX711 prend en charge 3 facteurs de gain, mais ceux-ci sont également utilisés pour la sélection des canaux. Des gains de 128 et 64 sont disponibles sur le canal A, tandis que la sélection d'un gain de 32 sélectionne le canal B. cela signifie que notre deuxième canal ne sera pas aussi sensible que le canal principal, c'est bien pour cette application.

Le HX711 peut être connecté à toutes les broches numériques de l'Arduino, j'ai utilisé D0 (Données) et D1 (Horloge), puis l'amplificateur a juste besoin d'être connecté à l'alimentation 3v3 de l'Arduino.

Vous pouvez en savoir plus sur les cellules de charge et le HX711 sur l'excellent didacticiel sur les cellules de charge SparkFuns.

Enfin un BME280 est connecté au bus I2C et utilisé pour détecter la température et l'humidité à l'intérieur de la box, cela peut aussi être utilisé pour détecter la pression mais cela n'a probablement que peu d'intérêt et nous n'avons que 12 octets de données sigfox pour jouer avec donc c'est non signalé.

L'électronique est montée sur une carte ThingySticks Arduino Prototype, j'ai ajouté un support de batterie (colle thermofusible à la feuille acrylique inférieure) et connecté l'antenne qui est d'un joli design plat donc a parfaitement fonctionné pour la plate-forme.

Calibrage de la cellule de charge :

Avant de pouvoir utiliser la plate-forme, les capteurs de pesage doivent être étalonnés. Chaque cellule de charge est unique, elle est fabriquée en attachant une jauge de contrainte à un bloc de métal et des trous y sont percés pour fournir une flexion suffisante qui peut être détectée sans qu'elle ne se brise, à cause de cela, nous devons calibrer chaque cellule de charge pour sa réponse au poids .

Une fois calibré, nous appliquons l'équation y=mx+c à la valeur ADC mesurée (x) pour obtenir le poids réel (y). Nous devons donc trouver c (le décalage) et m (la pente) pour notre cellule de charge.

J'ai retiré le dessus de la plate-forme principale et attaché un petit carré en acrylique à chaque cellule de charge à tour de rôle, et j'ai surveillé les valeurs mesurées (leur routage dans le micrologiciel pour ce faire, qui peut être démarré en envoyant un "c" au port série.

Initialement, la lecture d'une plate-forme vide a été mesurée, cela donne la valeur de décalage (c), puis en plaçant une charge de poids connu sur la cellule, la différence de lecture nous donne la pente.

Pente =(mesurée - offset) / poids (g).  

J'ai utilisé à la fois une petite canette de désodorisant (environ 230 g) et une bobine de filament d'imprimante (environ 1,5 kg) pour vérifier les valeurs, les deux ont donné à peu près la même pente, ce qui était rassurant.

Naturellement, le décalage mesuré avec le petit tampon acrylique est différent de celui rencontré avec la feuille supérieure complète, de même qu'il y a également une petite différence de pente lors de l'utilisation des deux cellules de charge, un étalonnage secondaire est donc nécessaire. Pour l'instant, un décalage d'un point zéro (tare) est utilisé, il est défini dans le micrologiciel mais peut également être défini à l'aide de la connexion série USB ou via un message de liaison descendante Sigfox une fois déployé.

Connexion Sigfox :

Avec le câble Really Smart Box, j'ai d'abord utilisé le port série USB pour surveiller la sortie afin de déboguer et de régler le système. De cette façon, vous pouvez voir les cellules de charge individuelles, les changements et le bruit. Cependant, cela ne fonctionnera pas pour une boîte déployée car elle doit être totalement sans fil.

Avec Sigfox, nous pouvons envoyer 12 octets de données jusqu'à 140 fois par jour à notre service en ligne, c'est plus que suffisant pour la Really Smart Box. La structure de données ci-dessous est utilisée dans l'Arduino et décrit comment nous utilisons les 12 octets.

typedef struct __attribute__ ((packed)) sigfox_message { uint8_t status; // indicateurs d'état int8_t humidité ; // humidité::int:8 - certains capteurs (HTU21D) read -ve humidité) int8_t temperature; // temperature::int:8 (pas de décimales). int16_t zeroWeight; // zeroWeight::int:16:little-endian int16_t weight; // poids::int:16:little-endian int16_t itemCount; // itemCount::int:16:little-endian (100x le nombre d'articles réel pour permettre 2,01 (car le poids ne correspond pas exactement) int8_t driftCorrection; // Correction de dérive pour les changements de poids zéro appliqués à la balance. int8_t filler; // Rien à voir ici, avancez.... int8_t lastStatus; // Dernier statut sigfox } SigfoxMessage;  

Le premier octet (état) est divisé en indicateurs de bits pour indiquer les problèmes :

// status::uint:8 -> Split to 8 bits // B7 - First run // B6 - Défaut HX711 // B5 - Défaut BME280// B4 - Alarme température// B3 - Alarme humidité // B2 - Alarme de poids// B1 - Stock bas// B0 - rechange  

Cette structure se réduit à 12 octets, mais nous devons la décompresser du côté de Sigfox pour la pousser sur Tinamous. Nous utilisons une configuration de charge utile personnalisée pour cela et il est préférable de régler cela au fur et à mesure que la structure de données est définie. Le nôtre est :

firstRun::bool:7 hx711Fault::bool:6 bmeFault::bool:5 temperatureAlarm::bool:4 humiditéAlarm::bool:3 weightAlarm::bool:2 lowStock::bool:1 b0::bool:0 status::int:8 humidité::int:8 temperature::int:8 zeroWeight::int:16:little-endian poids::int:16:little-endian itemCount::int:16:little -endian  

La charge utile personnalisée divise nos 12 octets au fur et à mesure de son analyse.

Notez que nous devons spécifier la nature petit-boutiste de tout ce qui dépasse 1 octet, car Sigfox utilise par défaut le gros-boutiste et l'Arduino utilise le petit-boutiste (c'est-à-dire que l'octet le moins significatif est le premier dans les mots multi-octets).

Notez également que la séparation des drapeaux booléens dans le premier octet ne fait pas progresser le marqueur d'octet comme c'est le cas avec toutes les autres lectures, donc l'octet d'état qui contient tous les drapeaux est également lu pour sauter le premier octet.

Les drapeaux sont des drapeaux d'alarme de température, d'humidité et de plage de poids, nous pourrions utiliser un service en ligne (c'est-à-dire Tinamous) pour surveiller la température, l'humidité et le poids hors plage, mais ceux-ci peuvent être de courte durée (quelques heures) et notre boîte peuvent être envoyés peu fréquemment (une ou deux fois par jour), les conditions environnementales potentiellement dommageables qui en résultent peuvent facilement être manquées, elles sont donc signalées sur l'appareil et envoyées (et réinitialisées après un envoi réussi).

Le nombre d'articles est en fait défini sur 100 fois le nombre réel d'articles. Je voulais autoriser des valeurs telles que 2,2 éléments (en raison d'une erreur de poids ou d'autres éléments dans la boîte) sans forcer un arrondi, de même 2,95 pourrait être arrondi à 2 si nous ne faisons pas attention et ce serait plus évocateur de 3 éléments dans la boite et une petite erreur. Je ne voulais pas non plus utiliser un flottant qui nécessiterait plus d'espace, j'ai donc utilisé un mot de 16 bits et appliqué un facteur pour permettre une conversion facile (il est également signé pour permettre une erreur nulle, ce qui pourrait entraîner un niveau de stock de -1 ou -2 etc.).

Très peu de choses doivent être faites pour activer la communication Sigfox. Dans l'Arduino, la bibliothèque Sigfox est ajoutée et les fonctions appropriées sont appelées pour extraire les données selon les exemples Arduino pour la bibliothèque Sigfox, mais nous devons enregistrer notre appareil avec Sigfox.

L'envoi d'un "s" au port série de la Really Smart Box imprime l'ID Sigfox et le code PAC, ceux-ci sont utilisés pour activer l'appareil au niveau du backend Sigfox. Nous nous dirigeons ensuite vers le service d'activation du backend Sigfox et suivons l'assistant, en sélectionnant d'abord notre appareil, puis le pays/fournisseur, puis quelques détails.

Et enfin notre appareil est activé et répertorié :

Sigfox attribue des appareils au groupe de types d'appareils, ce qui est judicieux car vous pouvez normalement en avoir plusieurs (des centaines, des milliers, etc.) du même type d'appareil sur lesquels vous souhaitez agir en tant que groupe. Avec le type d'appareil défini, nous pouvons configurer un rappel personnalisé pour transmettre les données que nous avons reçues à notre service en ligne. J'utilise Tinamous pour cela (indice :voir mon nom de profil - je pourrais être biaisé dans ma sélection).

Utilisation de la Really Smart Box :

Une fois câblée, boulonnée ensemble, le micrologiciel flashé et les batteries installées, la plate-forme est simplement déposée dans une boîte vraiment utile (tm) et prête à fonctionner.

L'alimentation doit être appliquée le plus tard possible car une fois que l'appareil est sous tension, il enverra le premier message Sigfox après 2 minutes et demandera des données de liaison descendante avec lui. Ces données peuvent inclure une commande « Zéro » pour mettre à zéro le poids des plates-formes. A défaut, une connexion série USB est nécessaire ou en attente de la prochaine demande de liaison descendante - celles-ci sont effectuées toutes les 12 heures.

Une fois opérationnelle, la plate-forme publiera des messages Sigfox toutes les 15 minutes pour envoyer le poids, le nombre d'articles, la température, l'humidité et les états d'alarme. La température, l'humidité et le poids sont mesurés toutes les minutes pour s'assurer qu'ils ne sont pas hors de portée, des alarmes étant signalées pour la prochaine transmission si elles ont été déclenchées.

Surveillance avec Tinamous :

Tinamous prend en charge les rappels personnalisés Sigfox en ajoutant un "Sigfox Bot" à notre compte, pour obtenir des instructions sur la façon de procéder, veuillez consulter mon didacticiel "Get Your Sigfox On" Hackster.io.

Lorsque vous ajoutez un Sigfox Bot à votre compte Tinamous, si vous incluez les paramètres de l'API, le Sigfox Bot recherchera vos appareils et les ajoutera à votre compte Tinamous, mais vous n'avez pas besoin de le faire car l'appareil sera automatiquement ajouté lorsque les données est publié.

Lorsque vous avez ajouté le Bot, un écran de configuration de rappel s'affiche pour vous aider à configurer les rappels Sigfox.

Vous pouvez ensuite créer un rappel personnalisé chez Sigfox, notez que la Really Smart Box utilise le rappel DATA -> BIDIR qui gère le rappel UPLINK normal et les rappels BIDIR (liaison montante et descendante).

C'est là que la charge utile personnalisée d'avant est utile, collez-la du code source dans la charge utile personnalisée et mettez à jour la section des champs pour refléter cela.

Lat et Lng sont spécifiés dans ce rappel qui donne un emplacement approximatif, cependant Sigfox sur l'Arduino prend en charge le réglage de l'emplacement amélioré, mais cela nécessite un deuxième rappel. Si vous utilisez la fonction de géolocalisation, ne spécifiez pas la Lat/Lng dans ce message car la Really Smart Box semblera se déplacer entre les emplacements.

Une fois configuré, il doit également être activé pour la liaison descendante, il est désactivé par défaut même si le BIDIR a été défini.

Notez que la capture d'écran sous l'option Downlink est "cochée", cela doit être fait manuellement et peut ne pas être disponible si le type de périphérique n'a pas été défini sur "CALLBACK" pour les données de liaison descendante (Type de périphérique -> Modifier -> Données de liaison descendante) .

Avec un rappel de liaison descendante, nous souhaitons également spécifier un rappel SERVICE -> ACKNOWLEDGE pour savoir que notre appareil a reçu les données de liaison descendante. En cliquant sur le Bot Sigfox dans Tinamous, d'autres configurations de rappel sont affichées, suivez les instructions pour les rappels ACKNOWLEDGE et GEOLOC.

Notez que vous devez copier l'en-tête d'autorisation du premier rappel de liaison montante/bidir car il s'agit d'un mot de passe crypté à sens unique dans Tinamous et qui n'est plus disponible pour l'affichage.

Avec nos rappels en place, les données publiées par notre appareil devraient maintenant être envoyées à Tinamous. Nous pourrions également ajouter des rappels par e-mail chez Sigfox, ce qui peut aider à confirmer que les données arrivent (mais peut également devenir très bruyant très rapidement).

Configuration de l'appareil Tinamous :

Une fois que le périphérique Sigfox a été vu sur Tinamous (soit via la recherche d'API ou un rappel), il sera affiché sur la page Périphériques, à partir de là, nous pouvons modifier les propriétés. Les champs sont automatiquement ajoutés au fur et à mesure qu'ils proviennent du rappel Sigfox, il est donc préférable d'attendre que l'appareil publie des données.

J'ai défini l'heure « Pas de rapport après » sur 1 heure (il est actuellement publié toutes les 15 minutes) afin que je puisse savoir si l'appareil est en panne et éventuellement en être informé.

Je ne voulais pas voir tous les champs envoyés par l'appareil sur la page graphique/détails (c'est beaucoup si vous incluez tous les drapeaux), donc Tinamous est configuré uniquement pour afficher le poids et le nombre d'articles. Des étiquettes et des unités conviviales ont également été appliquées ici.

Le champ Nombre d'articles est 100 fois supérieur au nombre réel d'articles, donc un étalonnage est appliqué à ce champ pour le réduire de 100 fois.

Certaines données de liaison descendante sont définies, ce qui entraînera la remise à zéro de la Really Smart Box et appliquera des limites de plage de température et d'humidité lors de la prochaine demande d'un message de liaison descendante (2 minutes après la mise sous tension, puis une fois toutes les 12 heures).

Affichage des informations sur la boîte vraiment intelligente :

Maintenant que les champs de l'appareil sont configurés, nous pouvons les surveiller via la page des détails de l'appareil (notez que je n'avais pas mis la plate-forme à zéro pour le moment, donc il pense qu'une 1/2 unité est présente - j'ai également remplacé le dessus en acrylique par un 5 mm version qui est plus lourde mais gérera mieux le filament de l'imprimante).

Nous pouvons également voir les interactions de rappel Sigfox à partir de la section Sigfox. Notez ici que les données de liaison descendante sont envoyées et accusées, mais l'Arduino signale une erreur. Plus à ce sujet à la fin.

Dans l'onglet Emplacement, nous pouvons également voir où se trouve notre Really Smart Box, ce qui peut être utile si vous oubliez à quel client elle se trouve ou si elle se trouve dans une camionnette.

Et naturellement, nous voulons une belle vue du tableau de bord de notre Really Smart Box, celle ci-dessous montre le poids du contenu de la boîte, les unités estimées qu'elle contient et le nombre d'appareils qui ne signalent pas afin que nous puissions dire si l'un est cassé.

Recevoir des notifications avec Tinamous :

Ensuite, j'ai configuré Tinamous pour envoyer un e-mail et un SMS lorsque le nombre d'articles est faible. Je l'ai fait en spécifiant une plage de travail de 3 à 300 pour le champ de nombre d'articles. Si la valeur est en dehors de cette plage, une mesure hors plage est même relevée.

En ajoutant une notification à Tinamous, nous pouvons être avertis lorsque cela se produit.

Nous pourrions spécifier uniquement le champ qui nous intéresse, mais laisser ce champ vide nous donne des notifications pour tout champ qui est hors de portée.

De même pour les appareils, laissez-le vide pour tous les appareils (c'est-à-dire le seul que nous ayons actuellement !)

Réglez les notifications répétées pour qu'elles se déclenchent une seule fois, jusqu'à ce qu'elles soient réinitialisées (tous les jours), sinon les notifications toutes les 15 minutes deviennent très rapidement ennuyeuses !

Sélectionnez ensuite comment être notifié, je l'ai configuré pour l'e-mail et le sms puis créez la notification :

Conclusion :

Je peux maintenant déployer la Really Smart Box et (espérons-le) l'oublier. Lorsque le niveau des stocks diminue, je serai averti et je pourrai vérifier sur le tableau de bord pour voir comment il se porte. En utilisant Sigfox, je n'ai pas à me soucier de l'alimentation de l'appareil autre qu'un changement de batterie occasionnel et aucune configuration WiFi sur site n'est requise, ce qui rend le déploiement extrêmement simple.

Je prévois de déployer cette unité dans l'une de nos boîtes de stockage de filaments à Cambridge Makespace pour surveiller les niveaux de stock de filaments.

Problèmes à résoudre :

Inutile de dire que ce n'est pas un projet fini de qualité de production, quelques problèmes doivent encore être résolus :

Lien descendant Sigfox :

Sigfox permet d'envoyer 4 messages par jour en réponse à un message de liaison montante. La Really Smart Box les utilise pour permettre la remise à zéro de la balance, le réglage des plages de température et d'humidité supérieure et inférieure et le poids de l'article. Cependant, tout en essayant de faire fonctionner cela, même si le message de liaison descendante semble être envoyé et est accusé (comme indiqué dans le backend Sigfox), l'Arduino signale une erreur d'état de 62, qui ne correspond à aucun indicateur d'erreur conditions répertoriées pour la puce ATA8520, en creusant dans les pilotes, la commande utilise une demande de liaison descendante qui n'est pas non plus répertoriée dans la fiche technique, donc une enquête plus approfondie doit être effectuée.

Débogage uniquement :

L'exécution des communications Sigfox avec le débogage désactivé entraîne l'activation du paramètre de faible puissance de l'Arduino et tue le port série USB.

Mode faible consommation :

Comme décrit pour le paramètre de débogage Sigfox, l'utilisation de la bibliothèque Low Power d'Arduino provoque la chute de la série USB, ce qui n'est donc pas activé pour le moment.

Dérive :

Aucune compensation pour la dérive n'a été ajoutée, il ne fait aucun doute que les capteurs de pesage dériveront lorsqu'ils seront maintenus sous une charge constante.

Mesures du bruit/non verticales :

Il est possible que la Really Smart Box se trouve à l'arrière d'une camionnette (par exemple, un nettoyeur mobile, un menuisier, un plombier, etc.). Il serait bon d'ajouter un accéléromètre à la plate-forme et de sauter les cycles de mesure lorsque la boîte n'est pas stable, de même si la boîte n'est pas verticale, le poids ne passera pas par les cellules de charge comme prévu.

Code

  • Code Arduino Really Smart Box
Code Arduino Really Smart BoxArduino
Ajoutez des bibliothèques pour Arduino MKR FOX 1200, HX711, AdaFruit BME280, Arduino Low Power. Utilisez l'IDE Arduino pour programmer normalement.
// Really Smart Box// Mesure le poids du contenu d'une boîte vraiment intelligente// Fabriquée par deux feuilles d'acrylique avec 2 cellules de charge entre elles // placées dans un vraiment smart box.// Comprend également un BME280 pour mesurer la température et la pression à l'intérieur de la box.// Auteur :Stephen Harrison// Licence :MIT#include #include #include  #include #include // ---------------------------------- ----// BME280 sur le port I2C.Adafruit_BME280 bme; // ----------------------------------------------------// Amplificateur de cellule de charge HX711.// 0 :D0 - DOUT// 1 :D1 - CLK// gain initial des échelles 128.HX711 (0, 1, 128); // Tableaux pour capteurs. Index 0 ==Canal A, Index 1 ==Canal B.float gain[] ={128,32};// Facteurs d'étalonnage.// nous utilisons y =mx + c (c =offset, m =scaleFactor)./ / pour convertir la valeur mesurée en poids.// Réglez ceci sur le décalage signalé par les cellules de charge.// sans poids sur elles.float offset[] ={0,54940} ; // Réglez ceci sur le facteur calculé lorsqu'un poids est placé sur la balance.// Réglez d'abord le décalage, re-flashez l'arduiono pour que cela prenne effet // placez un poids sur la balance et divisez la valeur brute mesurée par le weight.// using scaleFactor =valeur mesurée / weight.float scaleFactor[] ={378.f,260.9f};// ---------------------- --// Sigfox// C'est la structure de données que nous publions sur Sigfox.// Divisez les bits en tant qu'indicateurs booléens à partir du premier octet d'état mais l'octet a toujours besoin être // inclus sinon l'humidité devient le statut// 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-endiantypedef struct __attribute__ ((packed)) sigfox_message { uint8_t status; // status::uint:8 -> Split to 8 bits // B7 - First run, B6 - HX711 fault, B5 BME280 fault, B4 Temperature alarm, B3 - Humidity alarm, B2 - weight alarm, B1 - Low stock, B0 - spare int8_t humidity; // humidity::int:8 (yes some sensors (HTU21D read -ve humidity) int8_t temperature; // temperature::int:8 (no decimal places). int16_t zeroWeight; // zeroWeight::int:16:little-endian int16_t weight; // weight::int:16:little-endian int16_t itemCount; // itemCount::int:16:little-endian (100x actual item count to allow for 2.01 (as weight won't match exactly) int8_t driftCorrection; // Drift Correction for changes in zero weight applied to the scales. int8_t filler; int8_t lastStatus; // Last sigfox status} SigfoxMessage;// Time the last Sigfox message was published atlong lastPublish =0;// Time the last Sigfox downlink was requested.// Allowed max 4 per day of these.long lastDownlink =0;uint8_t lastSigfoxStatus =0;// --------------------------------------// Application/state// If the fist cycle (after a reset) for the measure/publish// cycle (this is used to request a downlink message from Sigfox).// Note that only 4 of them are allowed per day so becareful// when deploying code.bool isFirstCycle =true;// Application mode// 0:Normal// 1:Calibrationint mode =0;// Which channel should be read during calibration.int calibrate_channel =0;// The last average value measured for each channel.float lastAverage[] ={0,0};// The current weight of the contents of the boxfloat currentWeight =0;// The weight of the units the box will hold.// Updatable via Sigfox downlink message.float unitWeight =238;// Different to tare as it would be a manual// zero'd at a set reading from scales// This will most likely change with drift (time/temperature/etc)// and should be set once the scale is in place but not loaded.// Updatable via Sigfox downlink message.float zeroWeight =0;bool bmeOk =true;bool hx711Ok =true;// Alarms and alarm rangesfloat minTemperature =5.f;float maxTemperature =60.f;float minHumidity =0.f;float maxHumidity =60.f;float maxWeight =10000; // 10kgbool temperatureAlarm =false;bool humidityAlarm =false;bool weightAlarm =false;float currentTemperature =0;float currentHumidity =0;float stockLevel =0;bool lowStock =false;float minStock =5;// Setup the Arduino.void setup() { pinMode(LED_BUILTIN, OUTPUT); //Initialize serial:Serial.begin(9600); // NB:The sensor I'm using (from random eBay seller) // does not use the default address. bmeOk =bme.begin(0x76); if (!bmeOk) { Serial.println("Could not find a valid BME280 sensor!"); } // Delay for USB Serial connect and for the BME's first reading. retard (5000); Serial.println("Really Smart Box..."); printHeader();}int delayCounter =0;void loop() { switch (mode) { case 0:measureAndPublish(); //Sleep for 1 minutes // Causing problems with USB connected. //LowPower.sleep(1 * 60 * 1000); delay(60 * 1000); Pause; case 1:calibrate(); retard(1000); Pause; } // Check for user input via the serial port. checkSerial(); // measure is done on RTC timer tick (once per minute) delay(100);}void measureAndPublish() { // turn the LED on to indicate measuring. digitalWrite(LED_BUILTIN, HIGH); printBmeValues(); measureTemperatureAndHumidity(); measureWeight(true); // Weight, temperature and humidity are read every minute // however we only publish occasionally. if (shouldPublish()) { publishMeasurements(); } digitalWrite(LED_BUILTIN, LOW); }// Main measurement loop. Reads the weight from the load cells// and stores if no noise from the previous read.void measureWeight(bool printDetails) { scales.power_up(); retard (500); float delta =readDelta(printDetails); if (printDetails) { Serial.print("\t"); Serial.print(delta, 2); } // If the delta is between -1 and 1 (i.e. no noise) // update the change in overall weight and units contained // otherwise ignore and try again later on. // This ensures we use only stable readings when both channels have not changed for // two sets of measurements. if (delta <1.f &&delta> -1.f) { // Remember the previous measured weight so we can get a delta. float lastWeight =currentWeight; // Compute the weight. Take the weight of both load cells // added together then subtract the zero'd weight. currentWeight =lastAverage[0] + lastAverage[1] - zeroWeight; // Compute the difference in weight of the items in the box // compated to the last time we had a stable reading. float itemsWeightDelta =currentWeight - lastWeight; updateStockLevels(); if (printDetails) { Serial.print("\t"); Serial.print("\t"); Serial.print(currentWeight, 2); Serial.print("\t"); // divide by unit weight to estimate the stock level in the box Serial.print(currentWeight / unitWeight, 2); Serial.print("\t"); // the change in weight, (i.e. the weight if the items added) Serial.print(itemsWeightDelta, 2); Serial.print("\t"); // divide by unit weight to estimate the units removed/added Serial.print(itemsWeightDelta / unitWeight, 2); } } checkWeightLimits(); if (printDetails) { Serial.println(); } // put the ADC in sleep mode and switch // off the LED now we're done measuring. scales.power_down(); }void updateStockLevels() { stockLevel =currentWeight / unitWeight; // Unlike other alarms the low stock level // is reset if the stock is re-stocked. lowStock =stockLevel  maxWeight ) { weightAlarm =true; } if (lastAverage[0]> (maxWeight /2)) { weightAlarm =true; } if (lastAverage[1]> (maxWeight /2)) { weightAlarm =true; }}// Read the difference in weight from the last // average to this time across both load cells.// average value is stored in the lastAverage array.float readDelta(bool printDetails) { float aDelta =readChannel(0, true); if (printDetails) { Serial.print("\t"); } float bDelta =readChannel(1, true); return aDelta + bDelta;}// Read the weight from a channel. Stores the measured value in // the lastAverage array and retuns the delta of the measured value// from the previous lastAverage. This allows us to know if the weight// has changed.// channel 0 =A// channel 1 =Bfloat readChannel(int channel, bool printDetails) { // Gain:// Channel A supports 128 or 64. Default 128 // Channel B supports 32 // Select Channel B by using gain of 32. scales.set_gain(gain[channel]); // HX711 library only has one set of offset/scale factors // which won't work for use as we use both channels and they // have different gains, so each needs to have it's offset/scale set // before reading the value. scales.set_offset(offset[channel]); scales.set_scale(scaleFactor[channel]); // force read to switch to gain. scales.read(); scales.read(); float singleRead =scales.get_units(); float average =scales.get_units(10); float delta =average - lastAverage[channel]; if (printDetails) { Serial.print(singleRead, 1); Serial.print("\t"); Serial.print(average, 1); Serial.print("\t"); Serial.print(delta, 1); Serial.print("\t"); } lastAverage[channel] =average; return delta;}// print the header for the debug data pushed out when measuring.void printHeader() { Serial.print("BME280\t\t\t\t\t"); Serial.print("Channel A\t\t\t"); Serial.print("Channel B\t\t\t"); Serial.print("\t\t"); Serial.print("Totals \t\t\t"); Serial.println(""); Serial.print("Temp\t"); Serial.print("Pressure\t"); Serial.print("Humidity\t"); Serial.print("read\t"); Serial.print("average\t"); Serial.print("delta\t"); Serial.print("\t"); Serial.print("read\t"); Serial.print("average\t"); Serial.print("delta\t"); Serial.print("\t"); Serial.print("sum\t"); Serial.print("\t"); Serial.print("weight\t"); Serial.print("items\t"); Serial.print("change\t"); Serial.print("items added"); Serial.println("");}// Calibration - reads/prints selected channel values.void calibrate() { digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) scales.set_gain(gain[calibrate_channel]); scales.set_offset(offset[calibrate_channel]); scales.set_scale(scaleFactor[calibrate_channel]); // force read to switch to gain Serial.print("\t|CH:\t"); Serial.print(calibrate_channel,1); Serial.print("\traw:\t"); Serial.print(scales.read(),1); Serial.print("\t| raw:\t"); Serial.print(scales.read(),1); Serial.print("\t| units:\t"); Serial.print(scales.get_units(), 1); Serial.print("\t| gain:\t"); Serial.print(gain[calibrate_channel], 1); Serial.print("\t| factor:\t"); Serial.println(scaleFactor[calibrate_channel], 1); digitalWrite(LED_BUILTIN, LOW);}// check the serial port for input from a console to allow us to alter // the device mode etc.void checkSerial() { if(Serial.available()) { char instruction =Serial.read(); switch (instruction) { case '0':calibrate_channel =0; Serial.println("Channel 0 (A) Selected"); Pause; case '1':calibrate_channel =1; Serial.println("Channel 1 (B) Selected"); Pause; case 'm':// Measurement mode mode =0; Serial.println("Measurement Mode"); printHeader(); Pause; case 'c':// Calibration mode mode =1; Serial.println("Calibration Mode"); Pause; case 't':// Tare. Teset the scale to 0 Serial.println("Taring"); scales.power_up(); retard (500); scales.tare(5); // Need to do this for each channel // and update our stored offset. Serial.println("Not properly Tared!"); Pause; case 'h':printHeader(); Pause; case 'z':zeroScales(); Pause; case 's':printSigfoxModelDetails(); Pause; default:Serial.println("Unknown instruction. Select:0, 1, m, c, t, h, z, or s"); Serial.println("m - measurement mode"); Serial.println("c - Calibration mode"); Serial.println("0 - Channel 0 (A) Calibration"); Serial.println("1 - Channel 1 (B) Calibration"); Serial.println("t - Tare (scale)"); Serial.println("z - Zero (Weight)"); Serial.println("h - print Header"); Serial.println("s - print Sigfox model details"); Pause; } }}// Measure (and record) the temperature and humidity levels// Sets alarms if out of rage (we can't use limits on Internet service side// as the messages may only be sent a few times a day and a brief (maybe hours)// out of range temperature/humidity could easily be missed between// message publishing.void measureTemperatureAndHumidity() { if (!bmeOk) { return; } currentTemperature =bme.readTemperature(); if (currentTemperature  maxTemperature) { temperatureAlarm =true; } currentHumidity =bme.readHumidity(); if (currentHumidity  maxHumidity) { humidityAlarm =true; }}// Print the values read from the BME280 sensorvoid printBmeValues() { //Serial.print("Temperature ="); Serial.print(bme.readTemperature(), 1); Serial.print("\t"); Serial.print(bme.readPressure() / 100.0F, 0); Serial.print("\t\t"); Serial.print(bme.readHumidity(),1); Serial.print("\t\t ");}// =============================================================// Sigfox handing// =============================================================// Determine if we should publish the Sigfox message.// We may also wish to publish if the stock level has// changed (or a significant weight level has changed)// but we would need to be careful of exceeding the // 140 messages per day for a noisy system.bool shouldPublish() { // Publish every 15 minutes // this doesn't really need to be this often // but whilst developing it helps keep an eye on the system. int messageIntervalMinutes =15; // On first run after reset // allow a 2 minute delay for the platform to be placed into // the box and stabalise before doing first publish // which is also expected to include a check for zeroing the platform. if (isFirstCycle) { messageIntervalMinutes =2; Serial.println("First cycle"); } // How long ago we last publish a Sigfox message long millisAgo =millis() - lastPublish; return millisAgo> (messageIntervalMinutes * 60 * 1000);}// Publish our measurements (weight, temperature, humidity etc)// to Sigfox.void publishMeasurements() { Serial.println("Sending via Sigfox..."); bool useDownlink =shouldUseDownlink(); if (useDownlink) { Serial.println("Using Sigfox downlink..."); } // stub for message which will be sent SigfoxMessage msg =buildMessage(); SigFox.begin(); SigFox.debug(); // Wait at least 30mS after first configuration (100mS before) delay(100); // Clears all pending interrupts SigFox.status(); delay(1); SigFox.beginPacket(); SigFox.write((uint8_t*)&msg, 12); // endPacket actually sends the data. uint8_t statusCode =SigFox.endPacket(useDownlink); printSigfoxStatus(statusCode); // Status =0 for a successful send, otherwise indicates // a failure. // Store when we last published a Sigfox message // to allow for timed message sending. if (statusCode ==0) { resetAlarms(); } // Update the last publish/downlink times // even if an error resonse was received to prevent // repeated publishing lastPublish =millis(); isFirstCycle =false; if (useDownlink) { parseDownlinkData(statusCode); lastDownlink =lastPublish; } // Store the status value lastSigfoxStatus =statusCode; SigFox.end();}void printSigfoxStatus(uint8_t statusCode) { Serial.print("Response status code :0x"); Serial.println(statusCode, HEX); if (statusCode !=0) { Serial.print("Sigfox Status:"); Serial1.println(SigFox.status(SIGFOX)); Serial1.println(); Serial.print("Atmel Status:"); Serial1.println(SigFox.status(ATMEL)); Serial1.println(); }}// Create the message to be publish to Sigfox.SigfoxMessage buildMessage() { SigfoxMessage message; message.status =getStatusFlags(); message.humidity =(int8_t )currentHumidity; message.temperature =(int8_t)currentTemperature; message.zeroWeight =(int16_t)zeroWeight; message.weight =(int16_t)currentWeight; message.itemCount =(int16_t)(stockLevel * 100); message.driftCorrection =0; // TODO message.filler =0; message.lastStatus =lastSigfoxStatus; return message;}// Get the status flags for the Sigfox message.byte getStatusFlags() { byte status =0; // B7 - First run, // B6 - HX711 fault // B5 - BME280 fault // B4 - Temperature alarm // B3 - Humidity alarm // B2 - weight alarm // B1 - Low stock // B0 - spare // Upper Nibble (Charging/Battery) // Battery flat if (isFirstCycle) { status |=0x80; // 1000 0000 } // HX711 fault. // we don't have a way to check this yet. if (!hx711Ok) { status |=0x40; // 0100 0000 } // BME280 fault if (!bmeOk) { status |=0x20; // 0010 0000 } // Over/Under temperature alarm if (temperatureAlarm> 0) { status |=0x10; // 0001 0000 } // Over/Under humidity alarm if (humidityAlarm) { status |=0x08; // 0000 1000 } // Over/under? weight alarm if (weightAlarm) { status |=0x04; // 0000 0100 } // if computed stock level low. if (lowStock) { status |=0x02; // 0000 0010 } return status;}// Determine if we are requesting a downlink message.bool shouldUseDownlink() { // When debugging uncomment this so as to not keep requesting // downlink //return false; // On first run we want to request a downlink // message to help with zero'ing and setup. if (isFirstCycle) { return true; } // How long ago we last did a downlink message. long millisAgo =millis() - lastDownlink; // try every 12 hours, this keeps us under the // maximum 4 per day. return millisAgo> (12 * 60 * 60 * 1000);}// Parse downlinked data.void parseDownlinkData(uint8_t statusMessage) { if (statusMessage> 0) { Serial.println("No transmission. Status:" + String(statusMessage)); retourner; } // Max response size is 8 bytes // set-up a empty buffer to store this. (0x00 ==no action for us.) uint8_t response[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Expect... // Byte 0:Flags // B7:Zero scales // B6:Set Temperature range (ignore min/max temp if 0) // B5:Set Humidity range (ignore min/max humidity if 0) // B4:Set tolerance? // B3:Set ??? // B2:Update unit weight (ignore update if 0) // B1:// B0:// Byte 1:Min T // Byte 2:Max T // Byte 3:Min Humidity // byte 4:Max Humidity // byte 5:Read tolerence??? (+/- x) // byte 6 &7:Unit weight // Parse the response packet from Sigfox if (SigFox.parsePacket()) { Serial.println("Response from server:"); // Move the response into local buffer. int i =0; while (SigFox.available()) { Serial.print("0x"); int readValue =SigFox.read(); Serial.println(readValue, HEX); response[i] =(uint8_t)readValue; i++; } // byte 0 - flags. // 0b 1000 0000 if (response[0] &0x80 ==0x80) { zeroScales(); } // 0b 0100 0000 if (response[0] &0x40 ==0x40) { updateTemperatureAlarm(response[1], response[2]); } // 0b 0010 0000 if (response[0] &0x20 ==0x20) { updateHumidityAlarm(response[3], response[4]); } // 0b 0000 0100 if (response[0] &0x04 ==0x04) { // Little Endian format. (ff dd -> 0xddff uint16_t weight =response[7] <<8 &response[6]; updateUnitWeight(weight); } } else { Serial.println("No response from server"); } Serial.println();}void printSigfoxModelDetails() { if (!SigFox.begin()) { Serial.println("Shield error or not present!"); return; } // Output the ID and PAC needed to register the // device at the Sigfox backend. String version =SigFox.SigVersion(); String ID =SigFox.ID(); String PAC =SigFox.PAC(); // Display module informations Serial.println("MKRFox1200 Sigfox configuration"); Serial.println("SigFox FW version " + version); Serial.println("ID =" + ID); Serial.println("PAC =" + PAC); Serial.println(""); Serial.print("Module temperature:"); Serial.println(SigFox.internalTemperature()); Serial.println("Register your board on https://backend.sigfox.com/activate with provided ID and PAC"); delay(100); // Send the module to the deepest sleep SigFox.end();}// =============================================================// General helper methods// =============================================================// Reset the alarms after they have been published.void resetAlarms() { temperatureAlarm =false; humidityAlarm =false; weightAlarm =false;}void zeroScales() { zeroWeight =lastAverage[0] + lastAverage[1]; Serial.print("Zero'd:"); Serial.print(zeroWeight, 1); Serial.println();}void updateTemperatureAlarm(int8_t lower, int8_t upper) { Serial.print("Setting temperature alarm. Min:"); Serial.print(lower); Serial.print(", Max:"); Serial.println(upper); minTemperature =lower; maxTemperature =upper;}void updateHumidityAlarm(int8_t lower, int8_t upper) { Serial.print("Setting humidity alarm. Min:"); Serial.print(lower); Serial.print(", Max:"); Serial.println(upper); minHumidity =lower; maxHumidity =upper;}void updateUnitWeight(uint16_t weight) { Serial.print("Setting unit weight:"); Serial.println(weight); unitWeight =weight;}
Really Smart Box Github Repository
https://github.com/Tinamous/ReallySmartBox

Pièces et boîtiers personnalisés

Use this to cut the top and bottom acrylic sheets. cuttingguide_e7GNHf980M.svgThis sits between the lower acrylic sheet and load cell to raise it up a little and provide a edge to the platformsPrint 4 of these for each corner of the lower sheet if needed

Schémas

Nothing to complex.

Processus de fabrication

  1. Boîte à jus
  2. Boîte noire
  3. Cellule solaire
  4. Barman intelligent
  5. Feu de circulation intelligent
  6. poubelle intelligente
  7. Robot boîtier CD Raspberry Pi
  8. Boîte UVC d'un stérilisateur UV DIY
  9. Smart Plant IoT