Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

Echt slimme doos

Componenten en benodigdheden

Acryl plaat (bijv. 3 mm perspex)
× 1
Arduino MKR FOX 1200
× 1
SparkFun HX711 Load Cell-versterker
× 1
Belastingcel van 5 kg
× 2
Adafruit BME280
× 1

Benodigde gereedschappen en machines

Lasersnijder (algemeen)
3D-printer (algemeen)

Apps en online services

Tinamous
Sigfox

Over dit project

Het Really Smart Box-platform maakt van een Really Useful Box (tm) een intelligente, op internet aangesloten opbergdoos voor voorraadbewaking. Gebaseerd op de Sigfox Arduino MKR FOX 1200, detecteert het het gewicht van de dingen die in de doos zijn opgeslagen, samen met de temperatuur en vochtigheid en gebruikt het de Sigfox-radio met laag vermogen om deze informatie door te geven.

Use Case - 3D Printer Filament Opslag:

Als u een 3D-printer bezit, zult u zich hoogstwaarschijnlijk zorgen maken over de manier waarop uw filament wordt opgeslagen. Dit geldt niet alleen voor printerfilament, maar veel andere dingen moeten worden bewaard binnen acceptabele temperatuur- en vochtigheidsbereiken (bijvoorbeeld schilderskit kan onbruikbaar worden als het wordt blootgesteld temperaturen onder het vriespunt).

Als een van de mensen die verantwoordelijk zijn voor het onderhoud van de 3D-printers in mijn lokale makerruimte, moet ik ervoor zorgen dat we voldoende voorraad filament hebben en dat het droog wordt gehouden.

Met de Really Smart Box kan ik het gewicht van de gloeidraad in de gaten houden en zo weten of we bijna leeg raken, samen met de luchtvochtigheid in de doos om te zien of de silicagel moet worden vervangen.

Use Case - Voorraadbeheer verbruiksartikelen:

Een schoonmaakaannemer wil misschien een voorraad luchtverfrisser, handzeep of andere verbruiksartikelen op de locatie van de klant houden, de klant zou de aannemer waarschijnlijk geen wifi-toegang verlenen of een dergelijk apparaat van stroom voorzien wanneer deze niet aanwezig is, maar het aannemersbedrijf moet dit wel doen. weet wanneer u nieuwe voorraad moet insturen, wat de schoonmaaktijd en extra papierwerk extra kost dat niemand leuk vindt.

Het Really Smart Box-platform valt gewoon in een opbergdoos, omdat het Sigfox gebruikt, het geen verbinding hoeft te maken met het netwerk van de klant en weinig stroom verbruikt, dus werkt het op een set batterijen. Omdat de inhoud van de doos zeer zelden zou veranderen, kan de Arduino het grootste deel van de tijd in een lage energiestand worden gehouden, wat de levensduur van de batterij verlengt.

Het platform kan het gewicht van het itemtype dat in de doos is opgeslagen (d.w.z. de luchtverfrissers) worden verteld en zo berekenen hoeveel er in de doos zitten. Dit kan vervolgens naar het schoonmaakbedrijf worden gestuurd om hen te waarschuwen wanneer ze meer stookolie nodig hebben om op de locatie van de klant te worden afgeleverd.

Het platform maken:

Het platform heeft een eenvoudige constructie, het hoofdgedeelte is gemaakt van twee stukken lasergesneden acryl (ik gebruikte 3 mm, dikker zou beter zijn voor zware items zoals printerfilament) met een paar loadcellen ertussen.

Ik heb de schroefgaten voor de loadcel handmatig verzonken om een ​​mooiere afwerking te geven, ik heb nog geen lasersnijder gevonden die verzinkt!

Het acryl kan op elke maat worden gesneden om bij de doos te passen die u wilt, maar let op de positionering van de loadcellen en de lengte van de draad hierop, aangezien deze vrij kort is. De bovenste acrylplaat is iets kleiner dan de onderste om te voorkomen dat deze aan de zijkanten van de doos blijft haken.

Het onderste blad heeft een uitsparing zodat de elektronica zonder afstandhouders op het acryl kan worden gemonteerd en de gesoldeerde apparaatpoten erdoorheen kunnen steken. Ik heb een M3-kraan op de hoeksecties van de uitsparing gebruikt om de PCB er rechtstreeks op te schroeven. Er werden ook 3D-geprinte voetjes op de hoeken aangebracht om ervoor te zorgen dat eventuele schroeven van de loadcel die niet in lijn waren met het acryl de balans niet beïnvloedden.

Het gewicht wordt gemeten met behulp van twee 5 kg loadcellen. Een set van 4 wordt normaal gesproken gebruikt in weegschalen en dit zou misschien beter zijn, maar ik kon geen goede manier vinden om ze aan het acryl te bevestigen en om de benodigde afstand voor de elektronica te bieden.

De loadcellen hebben wat vulling aan de boven- en onderkant nodig om een ​​beetje te kunnen buigen en de eigenlijke rekstrookjes (het witte stukje op de afbeelding) van de loadcel zijn dikker dan het montageblok. Dit wordt bereikt onder de loadcel met de twee "Really Smart Box" 3D-geprinte eindplaten die een klein blok hebben om de loadcel omhoog te brengen, aan de bovenkant van de loadcel bevinden zich enkele lasergesneden acrylkussenblokken.

De loadcellen zijn aangesloten op een HX711 loadcelversterker. Deze heeft twee kanalen (A en B) die kunnen worden geselecteerd, wat perfect is voor dit gebruik.

Elke loadcel is gemaakt van rekstrookjes in een Wheatstone-brugconfiguratie, dit creëert een ongebalanceerd paar potentiaalverdelers, wanneer de loadcel onder belasting wordt geplaatst, verandert de weerstand van de rekstrookjes en daardoor ontstaat er een verschil tussen de twee potentiaalverdelers , dit wordt versterkt en gemeten door de HX711 die de analoog naar digitaal conversie voor ons uitvoert.

Ik heb twee loadcellen van 5 kg gebruikt voor dit project, je kunt verschillende classificaties krijgen (bijv. 1 kg en 10 kg) die precies hetzelfde werken, maar met een verschillende gevoeligheid.

Zorg er bij het plaatsen van de loadcellen voor dat de pijl aan het uiteinde van de cel naar beneden wijst (in de richting van de load). De cellen hebben M5-tapgaten aan het ene uiteinde (meestal het vaste uiteinde) en M4 aan het andere (de kant waarop u de lading plaatst.

De rood/zwarte draden zijn stroom, deze voedt de boven- en onderkant van de potentiaalverdelers en wordt gedeeld tussen de twee loadcellen. Groen en wit zijn de meetdraden vanuit het midden van de potentiaalverdelers, deze zijn aangesloten op kanaal A en B op de HX711.

De HX711 ondersteunt 3 versterkingsfactoren, maar deze worden ook gebruikt voor kanaalselectie. Gains van 128 en 64 zijn beschikbaar op het A-kanaal, terwijl het selecteren van een gain van 32 het B-kanaal selecteert. dit betekent dat ons tweede kanaal niet zo gevoelig zal zijn als het hoofdkanaal, dit is prima voor deze toepassing.

De HX711 kan worden aangesloten op elke digitale pinnen op de Arduino, ik heb D0 (Data) en D1 (Clock) gebruikt, dan hoeft de versterker alleen maar te worden aangesloten op de Arduino's 3v3-voeding.

U kunt meer lezen over loadcellen en de HX711 op SparkFuns uitstekende loadcell-tutorial.

Ten slotte is een BME280 aangesloten op de I2C-bus en gebruikt om de temperatuur en vochtigheid in de doos te meten, dit kan ook worden gebruikt om druk te voelen, maar dit is waarschijnlijk van weinig belang en we hebben slechts 12 bytes aan sigfox-gegevens om mee te spelen, dus het is niet gerapporteerd.

De elektronica is gemonteerd op een ThingySticks Arduino Prototype-bord, ik heb een batterijhouder toegevoegd (hotmelt gelijmd op de onderste acrylplaat) en de antenne aangesloten, die een mooi plat ontwerp heeft, dus perfect werkte voor het platform.

Laadcelkalibratie:

Voordat we het platform kunnen gebruiken, moeten de loadcellen worden gekalibreerd. Elke loadcel is uniek, hij is gemaakt door een rekstrookje te bevestigen aan een blok metaal en er zijn gaten in geboord om voldoende buiging te bieden die kan worden waargenomen zonder dat hij breekt. Daarom moeten we elke loadcel kalibreren voor zijn reactie op het gewicht .

Eenmaal gekalibreerd passen we de vergelijking y=mx+c toe op de gemeten ADC-waarde (x) om het werkelijke gewicht (y) te krijgen. We moeten dus c (de offset) en m (de helling) vinden voor onze load cell.

Ik heb de bovenkant van het hoofdplatform verwijderd en op zijn beurt een klein acrylvierkant aan elke loadcel bevestigd en de gemeten waarden gecontroleerd (hiervoor is een routering in de firmware die kan worden gestart door een "c" naar de seriële poort te sturen.

Aanvankelijk werd de aflezing voor een leeg platform gemeten, dit geeft de offset (c) -waarde, vervolgens wordt een belasting met een bekend gewicht op de cel geplaatst, het verschil in aflezingen geeft ons de helling.

Helling =(gemeten - offset) / gewicht (g).  

Ik heb zowel een klein blikje luchtverfrisser (ca. 230 g) als een spoel printerfilament (ca. 1,5 kg) gebruikt om de waarden te controleren, beide gaven ongeveer dezelfde helling, wat geruststellend was.

Natuurlijk is de offset gemeten met het kleine acrylkussen anders dan die ervaren met het volledige bovenvel, en er is ook een klein hellingsverschil bij gebruik van beide loadcellen, dus een secundaire kalibratie is nodig. Voor nu wordt een nulpuntverschuiving (tarra) van één punt gebruikt, dit wordt ingesteld in de firmware, maar kan ook worden ingesteld met behulp van de USB-seriële verbinding of via een Sigfox-downlinkbericht zodra het is geïmplementeerd.

Sigfox-verbinding:

Met de Really Smart Box wire-up gebruikte ik in eerste instantie de USB-seriële poort om de output te bewaken om het systeem te helpen debuggen en afstemmen. Op deze manier kunt u de individuele loadcellen, veranderingen en ruis zien. Dit gaat echter niet werken voor een ingezette box, omdat deze volledig draadloos moet zijn.

Met Sigfox kunnen we tot wel 140 keer per dag 12 bytes aan data naar onze online service sturen, dit is meer dan genoeg voor de Really Smart Box. De onderstaande gegevensstructuur wordt gebruikt in de Arduino beschrijft hoe we de 12 bytes gebruiken.

typedef struct __attribute__ ((ingepakt)) sigfox_message { uint8_t status; // statusvlaggen int8_t vochtigheid; // vochtigheid::int:8 - sommige sensoren (HTU21D) lezen -ve vochtigheid) int8_t temperatuur; // temperatuur::int:8 (geen decimalen). int16_t nulGewicht; // zeroWeight::int:16:little-endian int16_t weight; // weight::int:16:little-endian int16_t itemCount; // itemCount::int:16:little-endian (100x het werkelijke aantal items om 2,01 toe te staan ​​(omdat het gewicht niet exact overeenkomt) int8_t driftCorrection; // Driftcorrectie voor wijzigingen in het gewicht van nul toegepast op de weegschaal. int8_t filler; // Niets te zien hier, ga verder.... int8_t lastStatus; // Last sigfox status } SigfoxMessage;  

De eerste byte (status) wordt opgesplitst in bitvlaggen om de aangegeven problemen aan te geven:

// status::uint:8 -> Split to 8 bits // B7 - Eerste run// B6 - HX711 fout // B5 - BME280 fout// B4 - Temperatuuralarm// B3 - Vochtigheidsalarm // B2 - Gewichtsalarm// B1 - Weinig voorraad// B0 - reserve  

Deze structuur comprimeert tot 12 bytes, maar we moeten het aan de Sigfox-kant uitpakken om naar Tinamous te gaan. We gebruiken daarvoor een Custom Payload-configuratie en het is het beste om dit uit te werken als de datastructuur is gedefinieerd. Die van ons is:

firstRun::bool:7 hx711Fault::bool:6 bmeFault::bool:5 temperatuurAlarm::bool:4 vochtigheidAlarm::bool:3 weightAlarm::bool:2 lowStock::bool:1 b0::bool:0 status::int:8 vochtigheid::int:8 temperatuur::int:8 nulGewicht::int:16:little-endian gewicht::int:16:little-endian itemCount::int:16:little -endian  

De aangepaste payload splitst onze 12 bytes naar beneden terwijl deze wordt geparseerd.

Merk op dat we de little-endian-aard van alles groter dan 1 byte moeten specificeren, aangezien Sigfox standaard big-endian is en de Arduino little-endian gebruikt (d.w.z. de minst significante byte staat eerst in multi-byte woorden).

Merk ook op dat het splitsen van de booleaanse vlaggen in de eerste byte de bytemarkering niet voortzet zoals bij alle andere leesbewerkingen, dus de statusbyte die alle vlaggen bevat, wordt ook gelezen om de eerste byte over te slaan.

Inbegrepen in de vlaggen zijn temperatuur-, vochtigheids- en gewichtsbereik alarmvlaggen, we zouden een online service (bijv. Tinamous) kunnen gebruiken om te controleren op buiten bereik temperatuur, vochtigheid en gewicht, maar deze kunnen van korte duur zijn (een paar uur) en onze doos verzendt mogelijk niet vaak (een of twee keer per dag), de resulterende mogelijke schadelijke omgevingsconditie kan gemakkelijk worden gemist, dus worden ze gemarkeerd op het apparaat en verzonden (en gereset na een succesvolle verzending).

Het aantal artikelen is feitelijk ingesteld op 100 keer het werkelijke aantal artikelen. Ik wilde waarden als 2,2 items (vanwege gewichtsfout of andere items in de doos) toestaan ​​zonder een afronding te forceren, evenzo zou 2,95 naar beneden kunnen worden afgerond naar 2 als we niet voorzichtig zijn en het zou meer suggestief zijn voor 3 items in de doos en een kleine fout. Ik wilde ook geen float gebruiken die meer ruimte zou vergen, dus ik heb een 16-bits woord gebruikt en een factor toegepast om gemakkelijke conversie mogelijk te maken (het is ook ondertekend om een ​​nulfout toe te staan, wat kan resulteren in een voorraadniveau van -1 of -2 enz.).

Er hoeft maar heel weinig te worden gedaan om Sigfox-communicatie mogelijk te maken. Binnen de Arduino is de Sigfox-bibliotheek toegevoegd en worden geschikte functies aangeroepen om de gegevens naar buiten te duwen volgens de Arduino-voorbeelden voor de Sigfox-bibliotheek, maar we moeten ons apparaat bij Sigfox registreren.

Door een "s" naar de seriële poort van de Really Smart Box te sturen, worden de Sigfox ID en PAC-code afgedrukt, deze worden gebruikt om het apparaat op de Sigfox-backend te activeren. We gaan dan naar de Sigfox-backend-activeringsservice en volgen de wizard, eerst ons apparaat selecteren, dan land/provider en dan enkele details.

En tot slot is ons apparaat geactiveerd en weergegeven:

Sigfox wijst apparaten toe aan apparaattypegroepering, wat verstandig is, aangezien u normaal gesproken veel (honderden, duizenden, enz.) van hetzelfde apparaattype kunt hebben waarmee u als groep wilt werken. Met het gedefinieerde apparaattype kunnen we een aangepaste callback configureren om de gegevens die we hebben ontvangen naar onze online service te sturen. Ik gebruik hiervoor Tinamous (hint:zie mijn profielnaam - ik kan bevooroordeeld zijn in mijn selectie).

De echt slimme doos gebruiken:

Eenmaal aangesloten, aan elkaar vastgeschroefd, firmware geflitst en batterijen geplaatst, wordt het platform eenvoudig in een Really Useful Box (tm) geplaatst en is het klaar voor gebruik.

De stroom moet zo laat mogelijk worden ingeschakeld, want zodra het apparaat is ingeschakeld, zal het na 2 minuten het eerste Sigfox-bericht verzenden en er downlink-gegevens mee opvragen. Deze gegevens kunnen een "Zero"-opdracht bevatten om het platformgewicht op nul te zetten. Als dit niet lukt, is ofwel een seriële USB-verbinding nodig of wacht u op het volgende downlinkverzoek - deze worden elke 12 uur gedaan.

Eenmaal in gebruik zal het platform elke 15 minuten Sigfox-berichten publiceren om het gewicht, het aantal artikelen, de temperatuur, de vochtigheid en de alarmstatussen te verzenden. Temperatuur, vochtigheid en gewicht worden elke minuut gemeten om ervoor te zorgen dat deze niet buiten bereik zijn, waarbij alarmen worden gemarkeerd voor de volgende transmissie als ze zijn geactiveerd.

Monitoring met Tinamous:

Tinamous ondersteunt aangepaste Sigfox-callbacks door een "Sigfox Bot" aan ons account toe te voegen. Voor instructies over hoe u dat moet doen, zie mijn "Get Your Sigfox On" Hackster.io-tutorial.

Wanneer u een Sigfox Bot toevoegt aan uw Tinamous-account en u de API-instellingen opneemt, zoekt de Sigfox Bot uw apparaten op en voegt ze toe aan uw Tinamous-account, maar u hoeft dit niet te doen omdat het apparaat automatisch wordt toegevoegd wanneer gegevens is gepubliceerd.

Wanneer je de Bot hebt toegevoegd, krijgen we een Callback-configuratiescherm te zien om te helpen bij het instellen van de Sigfox Callbacks.

U kunt dan een aangepaste callback maken bij Sigfox, houd er rekening mee dat de Really Smart Box DATA -> BIDIR-callback gebruikt die de normale UPLINK-callback en de BIDIR-callbacks (up en downlink) afhandelt.

Dit is waar de aangepaste payload van vroeger van pas komt, plak deze uit de broncode in de aangepaste payload en werk de veldensectie bij om dit weer te geven.

Lat en Lng worden gespecificeerd in deze callback die een geschatte locatie geeft, maar Sigfox op de Arduino ondersteunt verbeterde locatie-instelling, maar dit heeft een tweede callback nodig. Als u de Geo-locatiefaciliteit gebruikt, specificeer dan de Lat/Lng niet in dit bericht, aangezien de Really Smart Box lijkt te bewegen tussen locaties.

Zodra dit is geconfigureerd, moet het ook worden ingeschakeld voor Downlink, dit is standaard uitgeschakeld, ook al is de BIDIR ingesteld.

Let op de schermafbeelding onder de Downlink-optie is "aangevinkt", dit moet handmatig worden gedaan en is mogelijk niet beschikbaar als het apparaattype niet is ingesteld op "CALLBACK" voor downlinkgegevens (Apparaattype -> Bewerken -> Downlinkgegevens) .

Met een downlink-callback willen we ook een SERVICE -> ACKNOWLEDGE callback specificeren om te weten dat ons apparaat de downlink-gegevens heeft ontvangen. Door op de Sigfox Bot in Tinamous te klikken, worden andere callback-configuraties weergegeven, volg de instructies voor de ACKNOWLEDGE- en GEOLOC-callbacks.

Houd er rekening mee dat u de autorisatieheader van de eerste uplink/bidir-callback moet kopiëren, aangezien dit een wachtwoord is dat in één richting is versleuteld in Tinamous en niet langer beschikbaar is voor weergave.

Met onze callbacks zouden de gegevens die door ons apparaat zijn gepubliceerd nu naar Tinamous moeten worden verzonden. We kunnen ook e-mailcallbacks toevoegen aan Sigfox, wat kan helpen bevestigen dat de gegevens binnenkomen (maar kan ook zeer snel erg luidruchtig worden).

Het Tinamous-apparaat configureren:

Zodra het Sigfox-apparaat is gezien bij Tinamous (hetzij via de api-lookup of een callback), wordt het weergegeven op de pagina Apparaten, vanaf hier kunnen we de eigenschappen bewerken. Velden worden automatisch toegevoegd zodra ze afkomstig zijn van de Sigfox-callback, dus u kunt het beste wachten tot het apparaat gegevens publiceert.

Ik heb de tijd voor 'Niet rapporteren na' ingesteld op 1 uur (het wordt momenteel elke 15 minuten gepubliceerd), zodat ik kan zien of het apparaat defect is en optioneel hiervan op de hoogte kan worden gesteld.

Ik wilde niet alle velden zien die door het apparaat zijn verzonden op de kaart / detailpagina (het zijn er veel als je alle vlaggen opneemt), dus Tinamous is alleen geconfigureerd om het gewicht en het aantal artikelen weer te geven. Ook hier werden mensvriendelijke labels en eenheden aangebracht.

Het veld Aantal artikelen is 100x het werkelijke aantal artikelen, dus er wordt een kalibratie op dat veld toegepast om het 100 keer te verminderen.

Sommige downlink-gegevens zijn ingesteld waardoor de Really Smart Box op nul wordt gezet en temperatuur- en vochtigheidsbereiklimieten toepast wanneer deze de volgende keer om een ​​downlink-bericht vraagt ​​(2 minuten na inschakelen, daarna eenmaal per 12 uur).

De echt Smart Box-informatie bekijken:

Nu de apparaatvelden zijn geconfigureerd, kunnen we ze controleren via de pagina met apparaatdetails (merk op dat ik het platform op dit moment niet op nul heb gezet, vandaar dat het denkt dat een 1/2 eenheid aanwezig is - ik heb ook de acryltop vervangen door een 5 mm versie die zwaarder is maar beter bestand is tegen printerfilament).

We kunnen ook de Sigfox-callback-interacties zien in de Sigfox-sectie. Merk hier op dat de downlink-gegevens worden verzonden en bevestigd, maar de Arduino meldt een fout. Daarover aan het einde meer.

Op het tabblad Locatie kunnen we ook zien waar onze Really Smart Box is, wat handig kan zijn als je vergeet bij welke klant hij is, of als hij zich in een busje bevindt.

En natuurlijk willen we een mooi dashboardoverzicht van onze Really Smart Box, de onderstaande toont het gewicht van de doosinhoud, de geschatte eenheden erin en een aantal apparaten die niet rapporteren, zodat we kunnen zien of er een kapot is.

Meldingen ontvangen met Tinamous:

Vervolgens heb ik Tinamous ingesteld om een ​​e-mail en sms te sturen wanneer het aantal items laag is. Ik deed dit door een werkbereik van 3 - 300 op te geven voor het veld voor het aantal artikelen. Als de waarde buiten dit bereik ligt, wordt een meting buiten het bereik zelfs verhoogd.

Door een melding aan Tinamous toe te voegen, kunnen we een melding krijgen wanneer dat gebeurt.

We kunnen alleen het veld specificeren waarin we geïnteresseerd zijn, maar als we dit veld leeg laten, krijgen we meldingen voor elk veld dat buiten bereik is.

Evenzo voor apparaten, laat het leeg voor alle apparaten (d.w.z. de enige die we momenteel hebben!)

Stel de herhalingsmeldingen in om slechts één keer te worden geactiveerd, totdat deze wordt gereset (elke dag), anders worden meldingen om de 15 minuten erg snel vervelend!

Selecteer vervolgens hoe u op de hoogte wilt worden gesteld, ik stel het in voor e-mail en sms en maak vervolgens de melding:

Conclusie:

Ik kan nu de Really Smart Box inzetten en (hopelijk) vergeten. Als het voorraadniveau laag wordt, krijg ik een melding en kan ik op het dashboard kijken hoe het ervoor staat. Met Sigfox hoef ik me geen zorgen te maken over het voeden van het apparaat, behalve dat de batterij af en toe moet worden vervangen, en er is geen wifi-installatie ter plaatse vereist, waardoor de implementatie uiterst eenvoudig is.

Ik ben van plan dit apparaat te gebruiken in een van onze opbergdozen voor filament in Cambridge Makespace om de filamentvoorraad te controleren.

Op te lossen problemen:

Onnodig te zeggen dat dit geen voltooid project van productiekwaliteit is, maar een paar problemen moeten nog worden opgelost:

Sigfox-downlink:

Met Sigfox kunnen 4 berichten per dag worden verzonden als reactie op een uplinkbericht. De Really Smart Box gebruikt deze om de weegschaal opnieuw op nul te zetten, de bovenste en onderste temperatuur- en vochtigheidsbereiken en het gewicht van het artikel in te stellen. Terwijl het echter probeert om dit te laten werken, hoewel het downlinkbericht lijkt te worden verzonden en wordt bevestigd (zoals weergegeven in de Sigfox-backend), meldt de Arduino een statusfout van 62, die niet wordt toegewezen aan een foutvlag voorwaarden vermeld voor de ATA8520-chip, gravend in de stuurprogramma's gebruikt de opdracht een verzoek voor downlink dat ook niet wordt vermeld in het gegevensblad, dus er moet wat meer onderzoek worden gedaan.

Alleen foutopsporing:

Als de Sigfox-communicatie wordt uitgevoerd met debug uitgeschakeld, wordt de instelling voor laag vermogen van de Arduino geactiveerd en wordt de seriële USB-poort uitgeschakeld.

Laag stroomverbruik:

Zoals beschreven voor de Sigfox-foutopsporingsinstelling, zorgt het gebruik van de Low Power-bibliotheek van de Arduino ervoor dat de USB-serie uitvalt, dus dit is op dit moment niet ingeschakeld.

Drift:

Er is geen compensatie voor drift toegevoegd, de loadcellen zullen ongetwijfeld driften wanneer ze onder constante belasting worden gehouden.

Ruis/niet-verticale metingen:

Het is mogelijk dat de Really Smart Box zich achter in een bestelwagen bevindt (bijv. mobiele schoonmaker, timmerman, loodgieter, enz.). Het zou goed zijn om een ​​versnellingsmeter aan het platform toe te voegen en meetcycli over te slaan wanneer de doos niet stabiel is, en als de doos niet verticaal staat, zal het gewicht niet door de loadcellen gaan zoals verwacht.

Code

  • Echt Smart Box Arduino-code
Echt Smart Box Arduino CodeArduino
Bibliotheken toevoegen voor Arduino MKR FOX 1200, HX711, AdaFruit BME280, Arduino Low Power. Gebruik de Arduino IDE om normaal te programmeren.
// Really Smart Box// Meet het gewicht van de inhoud van een echt slimme doos// Gemaakt door twee vellen acryl met 2 loadcellen ertussen // geplaatst in een echt slimme doos slimme doos.// Bevat ook een BME280 om temperatuur en druk in de doos te meten.// Auteur:Stephen Harrison// Licentie:MIT#include #include #include  #include #include // ---------------------------------- ----// BME280 op de I2C-poort.Adafruit_BME280 bme; // --------------------------------------// HX711 load cell versterker.// 0 :D0 - DOUT// 1:D1 - CLK// initiële versterking van 128.HX711 schalen (0, 1, 128);// Arrays voor loadcellen. Index 0 ==Kanaal A, Index 1 ==Kanaal B.float gain[] ={128,32};// Kalibratiefactoren.// we gebruiken y =mx + c (c =offset, m =scaleFactor)./ / om de gemeten waarde om te zetten in een gewicht.// Stel dit in op de offset gerapporteerd door de loadcellen.// zonder gewicht erop.float offset[] ={0,54940}; // Stel dit in op de factor die wordt berekend wanneer een gewicht op de weegschaal wordt geplaatst.// Stel eerst de offset in, flash de arduiono opnieuw om dit van kracht te laten worden// plaats een gewicht op de weegschaal en deel de ruwe gemeten waarde door de weight.// met scaleFactor =gemeten waarde / weight.float scaleFactor[] ={378.f,260.9f};// ---------------------- ----------------// Sigfox// Dit is de gegevensstructuur die we publiceren naar Sigfox.// Splits de bits op als bool-vlaggen vanaf de eerste statusbyte, maar de byte moet nog to be // included otherwise humidity becomes the status// 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. vertraging (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); break; case 1:calibrate(); vertraging (1000); break; } // 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(); vertraging (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"); break; case '1':calibrate_channel =1; Serial.println("Channel 1 (B) Selected"); break; case 'm':// Measurement mode mode =0; Serial.println("Measurement Mode"); printHeader(); break; case 'c':// Calibration mode mode =1; Serial.println("Calibration Mode"); break; case 't':// Tare. Teset the scale to 0 Serial.println("Taring"); scales.power_up(); vertraging (500); scales.tare(5); // Need to do this for each channel // and update our stored offset. Serial.println("Not properly Tared!"); break; case 'h':printHeader(); break; case 'z':zeroScales(); break; case 's':printSigfoxModelDetails(); break; 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"); break; } }}// 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)); return; } // 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

Aangepaste onderdelen en behuizingen

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

Schema's

Nothing to complex.

Productieproces

  1. Juice Box
  2. Black Box
  3. Zonnecel
  4. Slimme barman
  5. Slim verkeerslicht
  6. Slimme afvalbak
  7. Raspberry Pi CD Box Robot
  8. UVC Box een doe-het-zelf UV-sterilisator
  9. Smart Plant IoT
  10. Slimme jaloezieën
  11. Smart Touch-schakelbord