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

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Onderdelen en benodigdheden

Onderdeel Aantal
Acrylaatplaat (bijv. 3mm perspex) 1
Arduino MKR FOX 1200 1
SparkFun HX711 Belastingcel-versterker 1
5kg belastingcel 2
Adafruit BME280 1

Benodigde tools en machines

Tool
Lasersnijder
3D-printer

Apps en online diensten

App/Dienst
Tinamous
Sigfox

Over dit project

Het Really Smart Box-platform verandert een Really Useful Box (tm) in een intelligente, internetverbonden opslagdoos voor voorraadmonitoring. Gebaseerd op de Sigfox Arduino MKR FOX 1200 meet het het gewicht van de inhoud, samen met temperatuur en vochtigheid, en stuurt deze informatie via de lage-energie Sigfox-radio.

Use Case – 3D-printer filament opslag

Als je een 3D-printer hebt, weet je dat filament correct opgeslagen moet worden. Dit geldt ook voor veel andere materialen die binnen acceptabele temperatuur- en vochtigheidsgrenzen bewaard moeten worden (bijv. kit die onbruikbaar wordt bij vriestemperaturen).

Als een van de verantwoordelijken voor de 3D-printers in mijn lokale makerspace, moet ik ervoor zorgen dat we voldoende filament hebben en dat het droog blijft.

Met de Really Smart Box kan ik het gewicht van het filament monitoren (om te weten of de voorraad laag wordt) en de vochtigheid in de doos controleren (om te zien of het silicagel vervangen moet worden).

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Use Case – Verbruiksartikelen voorraadbeheer

Een schoonmaakbedrijf wil misschien een voorraad luchtverfrissers, zeep of andere verbruiksartikelen bij een klant bewaren. De klant zal waarschijnlijk geen WiFi-toegang geven of een apparaat laten draaien wanneer het bedrijf niet aanwezig is. Toch moet het bedrijf weten wanneer nieuwe voorraad geleverd moet worden, wat overhead en papierwerk oplevert.

Het Really Smart Box-platform valt gewoon in een opslagdoos. Omdat het Sigfox gebruikt, hoeft het niet verbonden te zijn met het netwerk van de klant en draait het op batterijen. Omdat de inhoud zelden verandert, kan de Arduino de meeste tijd in een lage-energiestand staan, wat de batterijduur verlengt.

Het platform kan verteld worden wat het gewicht is van het opgeslagen artikeltype (bijv. luchtverfrissers) en daaruit berekenen hoeveel er in de doos zitten. Dit kan dan naar het schoonmaakbedrijf gestuurd worden om ze te waarschuwen wanneer er nieuwe voorraad nodig is.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Het platform maken

De constructie is simpel: de hoofdbestaat uit twee gelaserde acrylaatplaten (ik gebruikte 3mm, dikker is beter voor zware items zoals filament) met twee belastingcellen ertussen.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Ik heb de schroefgaten voor de belastingcellen handmatig ingetrokken voor een nette afwerking. Nog geen lasersnijder gevonden die kontersinken kan!

Het acrylaat kan op maat gesneden worden voor elke doos. Let op de positionering van de belastingcellen en de kabellengte (die vrij kort is). De bovenste plaat is iets kleiner dan de onderste om te voorkomen dat het aan de zijkanten hangt.

De onderste plaat heeft een uitsparing voor de elektronica, zodat de gesoldeerde poten door de plaat kunnen steken. Ik gebruikte een M3-tap in de hoeksecties van de uitsparing om de PCB direct te bevestigen. 3D-geprinte voertjes op de hoeken zorgen ervoor dat eventuele schroeven van de belastingcel die niet vlak zitten, de balans niet beïnvloeden.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Het gewicht wordt gemeten met twee 5kg belastingcellen. Een set van 4 wordt normaal in persoonlijke weegschalen gebruikt en had beter kunnen zijn, maar ik kon geen nette manier vinden om ze aan acrylaat te bevestigen én de benodigde ruimte voor de elektronica te bieden.

De belastingcellen hebben boven en onder padding nodig voor een beetje buiging. De daadwerkelijke rekstrekkensoren (het witte deel) zijn dikker dan het montageblok. Dit wordt opgelost onder de belastingcel met de 3D-geprinte eindplaten "Really Smart Box" die een kleine blok hebben om de cel op te tillen, en bovenaan gelaserde acrylaat-paddingblokken.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

De belastingcellen zijn aangesloten op een HX711-versterker. Deze heeft twee kanalen (A en B) die geselecteerd kunnen worden – perfect voor dit gebruik.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Elke belastingcel bestaat uit rekstrekkensoren in een Wheatstone-brugconfiguratie. Dit creëert een ongebalanceerd paar potentiële delers. Bij belasting verandert de weerstand van de rekstrekkensoren, wat een verschil tussen de delers creëert. Dit wordt versterkt en gemeten door de HX711, die de analoge-naar-digitale conversie voor ons doet.

Ik gebruikte twee 5kg cellen. Er zijn verschillende belastingklassen (bijv. 1kg, 10kg) die hetzelfde werken maar met andere gevoeligheid.

Bij het plaatsen van de cellen: zorg dat de pijl op het einde van de cel naar beneden wijst (in de richting van de belasting). De cellen hebben M5-gaten aan één kant (vaast vast) en M4 aan de andere (belastingkant).

Rood/zwart draden zijn voeding (gedeeld tussen beide cellen). Groen en wit zijn de meetdraden van het midden van de delers, aangesloten op kanaal A en B van de HX711.

De HX711 ondersteunt 3 versterkingsfactoren, die ook voor kanaalselectie gebruikt worden. Versterking 128 en 64 zijn voor kanaal A, versterking 32 selecteert kanaal B. Dit betekent dat ons tweede kanaal minder gevoelig is, wat voor deze toepassing prima is.

De HX711 kan op elke digitale pin van de Arduino aangesloten worden. Ik gebruikte D0 (Data) en D1 (Clock), en de versterker op de 3v3-voeding van de Arduino.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Meer over belastingcellen en de HX711 vind je in de uitstekende tutorial van SparkFun.

Tot slot is een BME280 aangesloten op de I2C-bus voor temperatuur- en vochtigheidsmeting in de doos. Druk kan ook gemeten worden, maar is waarschijnlijk minder relevant en we hebben maar 12 bytes Sigfox-data, dus wordt niet verstuurd.

De elektronica zit op een ThingySticks Arduino Prototype-board. Ik voegde een batterijhouder toe (warmtegelijmd aan de onderste acrylaatplaat) en de antenne (vlak ontwerp, werkt perfect).

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Belastingcel kalibratie

Voordat we het platform gebruiken, moeten de belastingcellen gekalibreerd worden. Elke cel is uniek (rekstrekkensoren op metalen blok met gaten voor buiging), dus we moeten elke cel kalibreren voor zijn respons op gewicht.

Eenmaal gekalibreerd passen we de vergelijking y = mx + c toe op de gemeten ADC-waarde (x) om het daadwerkelijke gewicht (y) te krijgen. We moeten c (offset) en m (helling) bepalen.

Ik haalde de bovenste plaat af en bevestigde een klein acrylaatvierkantje aan elke belastingcel om de bewaarde waarden te monitoren (er zit een routine in de firmware die gestart kan worden door een 'c' naar de seriële poort te sturen).

Eerst de meting van een leeg platform (geeft offset c). Dan een bekend gewicht op de cel leggen – het verschil in metingen geeft de helling.

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

Ik gebruikte een kleine luchtverfrisser (ca. 230g) en een filamentrol (ca. 1.5kg) om de waarden te controleren – beide gaven ongeveer dezelfde helling, geruststellend.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Naturellijk is de offset met het kleine acrylaatvierkantje anders dan met de volledige bovenplaat, en is er ook een kleine hellingsverschil bij gebruik van beide cellen. Een secundaire kalibratie is nodig. Voor nu wordt een eenpunt-nuloffset (tare) gebruikt, ingesteld in de firmware maar ook via USB-serieel of Sigfox-downlink na uitrol.

Sigfox-verbinding

Ik gebruikte eerst de USB-seriële poort om de output te monitoren voor debugging. Maar voor een uitgezette doos moet het volledig draadloos zijn.

Met Sigfox kunnen we 12 bytes data sturen, maximaal 140 keer per dag – meer dan genoeg. Hieronder de datastructuur in de Arduino:

typedef struct __attribute__ ((packed)) sigfox_message {
  uint8_t status;        // status flags
  int8_t humidity;       // vochtigheid::int:8
  int8_t temperature;    // temperatuur::int:8
  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 werkelijke count)
  int8_t driftCorrection;// Drift correctie
  int8_t filler;         // reserve
  int8_t lastStatus;     // Laatste sigfox status
} SigfoxMessage;

De eerste byte (status) is opgesplitst in bit-vlaggen:

// B7 - Eerste run
// B6 - HX711 fout
// B5 - BME280 fout
// B4 - Temperatuur alarm
// B3 - Vochtigheid alarm
// B2 - Gewicht alarm
// B1 - Lage voorraad
// B0 - reserve

Deze structuur kompakteert tot 12 bytes. Aan de Sigfox-kant moeten we uitpakken voor Tinamous. We gebruiken een Custom Payload Configuration:

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

Let op: little-endian specificeren voor multi-byte waarden (Sigfox default is big-endian, Arduino is little-endian). De boolean-vlaggen in de eerste byte verplaatsen de byte-marker niet, daarom lezen we de status-byte ook nog eens om over de eerste byte heen te komen.

In de zitten temperatuur-, vochtigheids- en gewichtsalarmvlaggen. Omdat berichten zelden verstuurd worden (1-2x per dag), kunnen kortdurende afwijkingen gemist worden. Daarom worden ze op het apparaat gevlagd en verstuurd (en gereset na succesvolle verzending).

De item-count is 100x de werkelijke count. Dit toestaat waarden als 2.2 items (vanwege weegfout of andere items) zonder afronding. 2.95 zou anders naar 2 afgerond kunnen worden terwijl het 3 items suggereert. Ik wilde geen float gebruiken (meer ruimte), dus een 16-bit woord met factor.

Sigfox-communicatie inschakelen is simpel: Sigfox-bibliotheek toevoegen en de juiste functies aanroepen per Arduino-voorbeelden. Wel moet het apparaat geregistreerd worden bij Sigfox.

Stuur een 's' naar de seriële poort om Sigfox ID en PAC-code te printen. Deze gebruiken we voor activatie op de Sigfox-backend.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing
Really Smart Box: IoT-ingeschakelde slimme opslagoplossing
Really Smart Box: IoT-ingeschakelde slimme opslagoplossing
Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Apparaat geactiveerd en gelist:

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Sigfox wijst apparaten toe aan Device Type groeperingen. Met de Device Type gedefinieerd, kunnen we een custom callback configureren om data naar onze onlinedienst te sturen. Ik gebruik Tinamous.

De Really Smart Box gebruiken

Eenmaal bekabeld, gemonteerd, firmware geflasht en batterijen geplaatst: platform in een Really Useful Box (tm) leggen en klaar.

Stroom zo laat mogelijk aanzetten: na 2 minuten verstuurt het de eerste Sigfox-bericht en vraagt downlink-data aan (kan een "Zero"-commando bevatten om het gewicht te nullijnen. Anders USB-serieel of wachten op volgende downlink-aanvraag (elke 12 uur).

Eenmaal draaiend publiceert het platform elke 15 minuten een Sigfox-bericht met gewicht, item-count, temperatuur, vochtigheid en alarmstatussen. Temperatuur, vochtigheid en gewicht worden elke minuut gemeten voor alarmdetectie.

Monitoren met Tinamous

Tinamous ondersteunt Sigfox custom callbacks door een "Sigfox Bot" toe te voegen. Zie mijn "Get Your Sigfox On" Hackster.io tutorial voor instructies.

Bij het toevoegen van een Sigfox Bot, als je de API-instellingen meeneemt, zoekt de bot je apparaten op en voegt ze toe. Je hoeft dit niet te doen – het apparaat wordt automatisch toegevoegd bij eerste data.

Je krijgt een Callback-configuratiescherm om de Sigfox Callbacks in te stellen.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Maak een custom callback bij Sigfox. De Really Smart Box gebruikt DATA → BIDIR callback (normale UPLINK + BIDIR).

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Plak de custom payload uit de broncode in het veld "Custom Payload" en werk de veldensectie bij.

Lat en Lng in deze callback geven een benaderde locatie. Sigfox op Arduino ondersteunt verbeterde locatie, maar vereist een tweede callback. Als je Geo-locatie gebruikt, specificeer Lat/Lng niet in dit bericht, anders lijkt de box te verhuizen.

De callback moet ook voor Downlink ingeschakeld worden (standaard uit, zelfs bij BIDIR).

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Voor Downlink callback ook een SERVICE → ACKNOWLEDGE callback specificeren om te weten of het apparaat de downlink ontving. Klik op de Sigfox Bot in Tinamous voor meer callback-configuraties (ACKNOWLEDGE en GEOLOC). Kopieer de autorisatie-header uit de eerste uplink/bidir callback (dit is een wachtwoord dat eenrichtings-geëncrypteerd is in Tinamous).

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Met callbacks op hun plaats zou data nu naar Tinamous moeten stromen. Je kunt ook e-mail-callbacks bij Sigfox toevoegen voor bevestiging (maar kan snel vervelend worden).

Tinamous-apparaat configureren

Zodra het Sigfox-apparaat bij Tinamous binnenkomt (via API-lookup of callback), verschijnt het op de Apparaten-pagina. Velden worden automatisch toegevoegd zoals ze uit de callback komen – wacht tot data gepubliceerd wordt.

Ik stelde "Not Reporting After" in op 1 uur (publiceert nu elke 15 minuten) om te weten of het apparaat kapot is.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Ik wilde niet alle velden (veel vlaggen) op het grafiek/detailpagina zien, dus Tinamous toont alleen gewicht en item-count. Mensvriendelijke labels en eenheden toegepast.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Item Count is 100x de werkelijke count, dus kalibratie toegepast om het 100x te delen.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Downlink-data ingesteld die de Really Smart Box zal nullijnen en temperatuur/vochtigheidsgrenzen instellen bij volgende downlink-aanvraag (2 min na inschakelen, dan elke 12 uur).

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Really Smart Box informatie bekijken

Nu de velden geconfigureerd zijn, kunnen we ze monitoren via de apparaat-detailpagina (ik had het platform nog niet genulld, dus denkt hij dat 0.5 eenheden aanwezig zijn – ik verving ook de acrylaat-top door een 5mm versie die zwaarder is maar filament beter aan kan).

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Sigfox callback-interacties zijn zichtbaar in de Sigfox-sectie. Downlink-data wordt verstuurd en bevestigd, maar de Arduino rapporteert een fout. Meer daarover aan het einde.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Op het Locatie-tabblad zien we waar de box is – handig als je vergeet bij welke klant het staat of als het in een bus zit.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

En natuurlijk een mooi dashboard: inhoudsgewicht, geschatte eenheden, en telling van apparaten die niet rapporteren.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Notificaties ontvangen met Tinamous

Ik stelde Tinamous in om e-mail en SMS te sturen bij lage voorraad. Werkbereik van 3–300 voor item-count. Buiten dit bereik = measurement out of range event.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Notificatie toevoegen in Tinamous voor wanneer dit gebeurt.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Velden leeg laten = notificaties voor elk veld buiten bereik. Apparaten leeg laten = alle apparaten (nu maar 1).

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing
Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Repeat Notifications op "trigger once only, until reset (daily)" zetten, anders elke 15 minuten een notificatie!

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

E-mail en SMS selecteren, notificatie aanmaken:

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Conclusie

Ik kan de Really Smart Box nu uitrollen en (hopelijk) vergeten. Bij lage voorraad krijg ik een notificatie en kan ik op het dashboard checken hoe het gaat. Met Sigfox geen zorgen over voeding (behalve occasionele batterijwissel) en geen WiFi-setup op locatie – implementatie is extreem simpel.

Ik plan om deze unit in een van onze filament-opslagdozen in Cambridge Makespace te plaatsen voor voorraadmonitoring.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Te verhelpen problemen

Dit is geen productie-klaar project, nog een paar issues:

Sigfox Downlink: Sigfox staat 4 downlink-berichten per dag toe. De Really Smart Box gebruikt deze voor hernullijnen, temperatuur/vochtigheidsgrenzen en item-gewicht. Maar de Arduino rapporteert statusfout 62, wat niet in de ATA8520-datasheet staat. De commando gebruikt een downlink-verzoek dat ook niet in de datasheet staat – meer onderzoek nodig.

Alleen debug: Zonder debug activeert de lage-energiestand van de Arduino, wat de USB-seriële poort doodt.

Lage-energiestand: Zie boven – USB-serieel valt uit, dus nog niet ingeschakeld.

Drift: Geen compensatie voor drift toegevoegd. Belastingcellen zullen waarschijnlijk driften bij constant gewicht.

Ruimte / niet-verticale metingen: De box kan in een bus liggen (mobiele schoonmaker, timmeraar, loodgieter). Een accelerometer toevoegen om metingen over te slaan wanneer de box niet stilstaat, en verticale oriëntatie controleren.

Code

Really Smart Box Arduino-code Arduino

Voeg bibliotheken toe voor Arduino MKR FOX 1200, HX711, AdaFruit BME280, Arduino Low Power. Programmeer via Arduino IDE.

// Really Smart Box
// Meet het gewicht van de inhoud van een really smart box
// Gemaakt van twee acrylaatplaten met 2 belastingcellen ertussen
// In een really useful box geplaatst.
// Ook een BME280 voor temperatuur en druk in de box.
// Auteur: Stephen Harrison
// Licentie: MIT

#include <Adafruit_BME280.h>
#include <HX711.h>
#include <SigFox.h>
#include <SigFox.h>
#include <ArduinoLowPower.h>

// BME280 op I2C
Adafruit_BME280 bme;

// HX711 belastingcel versterker
// 0: D0 - DOUT
// 1: D1 - CLK
// initiële versterking 128
HX711 scales(0, 1, 128);

// Arrays voor belastingcellen. Index 0 = Kanaal A, Index 1 = Kanaal B
float gain[] = {128, 32};

// Kalibratiefactoren
// y = mx + c (c = offset, m = scaleFactor)
// om gemeten waarde om te zetten in gewicht
float offset[] = {0, 54940};
float scaleFactor[] = {378.f, 260.9f};

// Sigfox datastructuur
typedef struct __attribute__ ((packed)) sigfox_message {
  uint8_t status;
  int8_t humidity;
  int8_t temperature;
  int16_t zeroWeight;
  int16_t weight;
  int16_t itemCount;
  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;
int calibrate_channel = 0;
float lastAverage[] = {0, 0};
float currentWeight = 0;
float unitWeight = 238;
float zeroWeight = 0;
bool bmeOk = true;
bool hx711Ok = true;
float minTemperature = 5.f;
float maxTemperature = 60.f;
float minHumidity = 0.f;
float maxHumidity = 60.f;
float maxWeight = 10000;
bool temperatureAlarm = false;
bool humidityAlarm = false;
bool weightAlarm = false;
float currentTemperature = 0;
float currentHumidity = 0;
float stockLevel = 0;
bool lowStock = false;
float minStock = 5;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(9600);
  bmeOk = bme.begin(0x76);
  if (!bmeOk) {
    Serial.println("Geen geldige BME280 sensor gevonden!");
  }
  delay(5000);
  Serial.println("Really Smart Box...");
  printHeader();
}

int delayCounter = 0;
void loop() {
  switch (mode) {
    case 0: measureAndPublish(); delay(60 * 1000); break;
    case 1: calibrate(); delay(1000); break;
  }
  checkSerial();
  delay(100);
}

void measureAndPublish() {
  digitalWrite(LED_BUILTIN, HIGH);
  printBmeValues();
  measureTemperatureAndHumidity();
  measureWeight(true);
  if (shouldPublish()) publishMeasurements();
  digitalWrite(LED_BUILTIN, LOW);
}

void measureWeight(bool printDetails) {
  scales.power_up();
  delay(500);
  float delta = readDelta(printDetails);
  if (printDetails) { Serial.print("\t"); Serial.print(delta, 2); }
  if (delta < 1.f && delta > -1.f) {
    float lastWeight = currentWeight;
    currentWeight = lastAverage[0] + lastAverage[1] - zeroWeight;
    float itemsWeightDelta = currentWeight - lastWeight;
    updateStockLevels();
    if (printDetails) {
      Serial.print("\t\t"); Serial.print(currentWeight, 2);
      Serial.print("\t"); Serial.print(currentWeight / unitWeight, 2);
      Serial.print("\t"); Serial.print(itemsWeightDelta, 2);
      Serial.print("\t"); Serial.print(itemsWeightDelta / unitWeight, 2);
    }
  }
  checkWeightLimits();
  if (printDetails) Serial.println();
  scales.power_down();
}

void updateStockLevels() {
  stockLevel = currentWeight / unitWeight;
  lowStock = stockLevel < minStock;
}

void checkWeightLimits() {
  if (currentWeight > maxWeight) weightAlarm = true;
  if (lastAverage[0] > (maxWeight / 2)) weightAlarm = true;
  if (lastAverage[1] > (maxWeight / 2)) weightAlarm = true;
}

float readDelta(bool printDetails) {
  float aDelta = readChannel(0, true);
  if (printDetails) Serial.print("\t");
  float bDelta = readChannel(1, true);
  return aDelta + bDelta;
}

float readChannel(int channel, bool printDetails) {
  scales.set_gain(gain[channel]);
  scales.set_offset(offset[channel]);
  scales.set_scale(scaleFactor[channel]);
  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;
}

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("");
}

void calibrate() {
  digitalWrite(LED_BUILTIN, HIGH);
  scales.set_gain(gain[calibrate_channel]);
  scales.set_offset(offset[calibrate_channel]);
  scales.set_scale(scaleFactor[calibrate_channel]);
  scales.read();
  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);
}

void checkSerial() {
  if (Serial.available()) {
    char instruction = Serial.read();
    switch (instruction) {
      case '0': calibrate_channel = 0; Serial.println("Kanaal 0 (A) Geselecteerd"); break;
      case '1': calibrate_channel = 1; Serial.println("Kanaal 1 (B) Geselecteerd"); break;
      case 'm': mode = 0; Serial.println("Meetmodus"); printHeader(); break;
      case 'c': mode = 1; Serial.println("Kalibratie modus"); break;
      case 't': Serial.println("Taren"); scales.power_up(); delay(500); scales.tare(5); Serial.println("Niet correct getaard!"); break;
      case 'h': printHeader(); break;
      case 'z': zeroScales(); break;
      case 's': printSigfoxModelDetails(); break;
      default: Serial.println("Onbekende instructie. Kies: 0, 1, m, c, t, h, z, of s");
    }
  }
}

void measureTemperatureAndHumidity() {
  if (!bmeOk) return;
  currentTemperature = bme.readTemperature();
  if (currentTemperature < minTemperature) temperatureAlarm = true;
  if (currentTemperature > maxTemperature) temperatureAlarm = true;
  currentHumidity = bme.readHumidity();
  if (currentHumidity < minHumidity) humidityAlarm = true;
  if (currentHumidity > maxHumidity) humidityAlarm = true;
}

void printBmeValues() {
  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");
}

bool shouldPublish() {
  int messageIntervalMinutes = 15;
  if (isFirstCycle) {
    messageIntervalMinutes = 2;
    Serial.println("Eerste cyclus");
  }
  long millisAgo = millis() - lastPublish;
  return millisAgo > (messageIntervalMinutes * 60 * 1000);
}

void publishMeasurements() {
  Serial.println("Versturen via Sigfox...");
  bool useDownlink = shouldUseDownlink();
  if (useDownlink) Serial.println("Sigfox downlink gebruiken...");
  SigfoxMessage msg = buildMessage();
  SigFox.begin();
  SigFox.debug();
  delay(100);
  SigFox.status(); delay(1);
  SigFox.beginPacket();
  SigFox.write((uint8_t*)&msg, 12);
  uint8_t statusCode = SigFox.endPacket(useDownlink);
  printSigfoxStatus(statusCode);
  if (statusCode == 0) resetAlarms();
  lastPublish = millis();
  isFirstCycle = false;
  if (useDownlink) { parseDownlinkData(statusCode); lastDownlink = lastPublish; }
  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();
  }
}

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;
  message.filler = 0;
  message.lastStatus = lastSigfoxStatus;
  return message;
}

byte getStatusFlags() {
  byte status = 0;
  if (isFirstCycle) status |= 0x80;
  if (!hx711Ok) status |= 0x40;
  if (!bmeOk) status |= 0x20;
  if (temperatureAlarm) status |= 0x10;
  if (humidityAlarm) status |= 0x08;
  if (weightAlarm) status |= 0x04;
  if (lowStock) status |= 0x02;
  return status;
}

bool shouldUseDownlink() {
  if (isFirstCycle) return true;
  long millisAgo = millis() - lastDownlink;
  return millisAgo > (12 * 60 * 60 * 1000);
}

void parseDownlinkData(uint8_t statusMessage) {
  if (statusMessage > 0) {
    Serial.println("Geen transmissie. Status: " + String(statusMessage));
    return;
  }
  uint8_t response[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  if (SigFox.parsePacket()) {
    Serial.println("Response van server:");
    int i = 0;
    while (SigFox.available()) {
      Serial.print("0x"); int readValue = SigFox.read(); Serial.println(readValue, HEX);
      response[i] = (uint8_t)readValue; i++;
    }
    if (response[0] & 0x80) zeroScales();
    if (response[0] & 0x40) updateTemperatureAlarm(response[1], response[2]);
    if (response[0] & 0x20) updateHumidityAlarm(response[3], response[4]);
    if (response[0] & 0x04) {
      uint16_t weight = response[7] << 8 | response[6];
      updateUnitWeight(weight);
    }
  } else {
    Serial.println("Geen response van server");
  }
  Serial.println();
}

void printSigfoxModelDetails() {
  if (!SigFox.begin()) { Serial.println("Shield fout of niet aanwezig!"); return; }
  String version = SigFox.SigVersion();
  String ID = SigFox.ID();
  String PAC = SigFox.PAC();
  Serial.println("MKRFox1200 Sigfox configuratie");
  Serial.println("SigFox FW versie " + version);
  Serial.println("ID = " + ID);
  Serial.println("PAC = " + PAC);
  Serial.println("");
  Serial.print("Module temperatuur: "); Serial.println(SigFox.internalTemperature());
  Serial.println("Registreer je board op https://backend.sigfox.com/activate met ID en PAC");
  delay(100);
  SigFox.end();
}

void resetAlarms() {
  temperatureAlarm = false; humidityAlarm = false; weightAlarm = false;
}

void zeroScales() {
  zeroWeight = lastAverage[0] + lastAverage[1];
  Serial.print("Genuld: "); Serial.print(zeroWeight, 1); Serial.println();
}

void updateTemperatureAlarm(int8_t lower, int8_t upper) {
  Serial.print("Temperatuur alarm instellen. Min: "); Serial.print(lower); Serial.print(", Max: "); Serial.println(upper);
  minTemperature = lower; maxTemperature = upper;
}

void updateHumidityAlarm(int8_t lower, int8_t upper) {
  Serial.print("Vochtigheid alarm instellen. Min: "); Serial.print(lower); Serial.print(", Max: "); Serial.println(upper);
  minHumidity = lower; maxHumidity = upper;
}

void updateUnitWeight(uint16_t weight) {
  Serial.print("Eenheidsgewicht instellen: "); Serial.println(weight);
  unitWeight = weight;
}

Really Smart Box GitHub Repository

https://github.com/Tinamous/ReallySmartBox

Aangepaste onderdelen en behuizingen

Gebruik dit om de boven- en onderste acrylaatplaten te snijden.

cuttingguide_e7GNHf980M.svg

Dit zit tussen de onderste acrylaatplaat en belastingcel om het iets op te tillen en een rand te geven aan het platform. Print 4 van deze voor elke hoek van de onderste plaat indien nodig.

Schema's

Niets te complex.

Really Smart Box: IoT-ingeschakelde slimme opslagoplossing

Productieproces

  1. De Sapdoos Revolutie: Geschiedenis, Ontwerp en Milieueffect
  2. Black Box Vluchtrecorders: Van Vroege Prototypen tot Toekomstige Geïntegrerde Ontwerpen
  3. Zonnecellen: van vroege experimenten tot moderne fotovoltaïsche technieken – Technologie, productie en toekomstperspectieven
  4. Slimme Barman: Bouw Je Eigen Raspberry Pi-Cocktailstation
  5. Slimme Verkeerslicht: Adaptieve Straatverlichting Aangedreven door IoT
  6. Intelligente Slimme Vuilnisbak: Afvalophaal Optimaliseren met IoT-sensoren
  7. DIY Wall-E Geïnspireerde Raspberry Pi CD-Doos Robot
  8. Zelfbouw UVC-sterilisatiedoos – Goedkope, makkelijke UV-box voor medische en persoonlijke items
  9. Slimme Plant IoT: Verbonden kassen bouwen met Arduino & Azure
  10. Slimme Jaloezieën: Geautomatiseerd Lichtbeheer met Arduino & Zonne-energie
  11. Upgrade je huis: Slimme touch-schakelaar met temperatuurbewaking