rokito echolot

echolot

ein freund hat eine zisterne unterm garten und braucht ein gerät, dass misst wie voll die zisterne ist. das schreit geradezu nach einenm neuen projekt. 😃 ich baue ein kleines echolot mit einem ultraschall abstandsmesser und einer pixelring anzeige. besonderes augenmerk lege ich diesmal auf die später möglichst einfache installation des gerätes am zisternendeckel und eine möglichst hohe wetterbeständigkeit. die grundidee ist einfach, es gibt zwei teile, im unteren teil unter dem deckel der zisterne befindet sich ein ultraschall sensor. der misst, wie hoch der wasserspiegel in der zisterne ist. im oberen teil auf dem zisternendeckel befinden sich dann der nano mit batterie, ein neopixelring der den füllstand anzeigt und ein knopf. das ganze läuft dann nur auf knopfdruck.

material

das meiste habe ich wieder bei amazon gefunden. ein wenig aufwändiger war die suche nach einem geeigneten gehäuse, denn es muss einen transparenten deckel haben, unter dem dann der ring durchscheinen würde. außerdem muss der deckel trotzdem stabil genug sein, den knopf zu halten und beim knopfdruck nicht zu reißen. das besondere am gehäuse ist weiterhin die zweiteiligkeit, dafür benötigt es noch ein kabel mit einem möglichst schmalen stecker, denn durch den deckel der zisterne soll später nur ein kleines loch gebohrt werden, um beide teile zu verbinden. da fällt mir so ein typischer klinke-stecker ein, wie bei kopfhörern, die haben ja mehrere pole und lassen sich einfach benutzen und passen locker durch ein kleines loch, ideal also 👍

echolot

vorbereitung

echolot

anfänglich hatte ich vor einen adafruit trinket controller zu benutzen, der lag hier noch rum. an sich kein problem, nur hat die sache den haken, dass der trinket nach dem einschalten erst noch 10 sek. im bootloader hängen bleibt und dann erst den code ausführt 😳 damit scheidet der leider aus, ich hätte den gern hier verbaut, aber ich möchte dass das echolot mit möglichst wenig verzögerung nach dem einschalten sofort loslegt, wie man das von den nanos gewohnt ist. ich nehme also einen normalen nano klon. zuerst teste ich den neopixel ring. ein tolles ding, verkabeln, library einspielen und los gehts. ich verwende den testcode anbei. ich lege gleich ein paar allgemeine variablen fest und rechne die anzahl der leds in prozent um. damit kann ich später dann den prozentualen füllstand übergeben und diese schnittstelle berechnet bei wieviel prozent wieviele LEDs angehen müssen. dazu kommt noch eine verschiebung der hex werte, damit erzeuge ich einen verlauf im ring von rot (leer) nach grün (voll).

#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(24, A3);

uint32_t color = 0xff0000;
float pxls = 24;
float pcnt = 80;
float ppxl;
float cap = 300;
float vol = 123;

void setup() {
  pixels.begin();
  pixels.setBrightness(50); //max 255

  //calc percentual pixel usage
  pcnt = 100/cap*vol;
  ppxl = pxls/100*pcnt;

  for (int i=0; i <= ppxl; i++){
    color -= 0x090000;
    color += 0x000900;
    pixels.setPixelColor(i, color);
    pixels.show();
    delay(50);
  }

}

void loop() {

}

echolot

als nächstes gehe ich auf die maße der zisterne ein. für die zisterne gibt es ein schönes datenblatt von dem ich alles ablesen kann. dann muss ich natürlich berechnen, welches volumen die zisterne hat und wie voll sie bei gegebenem wasserspiegel ist. zeit mal wieder das alte tafelwerk rauszusuchen, die formeln sind recht einfach und eindeutig. zusätzlich ziehe ich von obe und unten noch ein distanz ab. die obere distanz entsteht dadurch, dass natürlich die zisterne nie randvoll ist und einen überlauf unterhalb des deckels hat, diesen leeren bereich muss ich sowieso abziehen. weiterhin liegt die pumpe in der zisterne nicht am boden, daraus ergibt sich eine art mindestfüllhöhe, auch die ziehe ich beim rechnen ab. daraus ergibt sich letztendlich eine kompakte formel. interessant wird es für besitzer einer liegenden zisterne, also einem klassischen tank. hier ist die formel etwas komplizierter. nach einiger recherche hilft wikipedia.

echolot

  //setup
  const float pi = 3.14159265359;

  //tank dimensions in cm
  float len = 313;
  float rad = 100;
  float top = 92;
  float gnd = 50;

  //measured distance
  float dst = 123;

  //normal situation
  cap = pi*square(rad)*(len-top-gnd);
  vol = pi*square(rad)*(len-dst-gnd);

  //tank problem
  float cap = pi*square(rad)*len;
  float vol = sq(rad)*len*(acos((rad-dst)/rad)-(rad-dst)*(sqrt(2*rad*dst-sq(dst))/sq(rad)));

mir kam noch der gedanke, dass man später den zuletzt gemessenen wert irgendwie anzeigen lassen könnte, damit kann man dann besser abschätzen wieviel wasser man beim blumengießen verbraucht hat oder wie dolle es geregent hat. dazu muss der wert irgendwie zwischengespechert werden, aber ohne dazu extra noch weiter breakouts etc. anzubauen. offenbar hat der nano ja einen festspeicher, irgendwo muss ja der programmcode gespeichert sein. ich habe dann etwas nachgelesen und bin auf den sog. EEPROM gestoßen. der ist genau das was ich brauche. im EEPROM können anscheinend bitweise kleinere werte gespeichert werden 👍 den speicher zu belegen und auszulesen ist auch sehr sehr einfach, dazu reicht der folgende code. interessant ist, dass irgendwie nur werte bis 255 angenommen werden, erklären kann ich es nicht genau, aber für meine zwecke reicht das, ich speichere dann einfach die anzahl der zuletzt leuchtenden pixel ab, da gibts ja nur 24... 😃 zum testen hatte ich den code auf meinen arduino uno geladen und den seriellen monitor geöffnet. bei jedem reset wird der wert inkrementiert und das auch nach dem ausschalten. funzt also.

#include <EEPROM.h>

void setup(){

  int eeval = EEPROM.read(1);
  eeval++;
  EEPROM.write(1,eeval); //255 is max

  Serial.begin(9600);
  Serial.println(eeval);

}

void loop(){
    //nuffin
}

zuletzt musste nur noch der abstandssensor getestet werden. für diese art von sensoren gibt es die new ping library von arduino. auch hier ist die installation sehr leicht, allzuviele pins gibt es nicht und der code hält sich auch in grenzen. ich hatte noch überlegt vielleicht einen richtigen parksensor zu nehmen, denn ich war etwas skeptisch, dass vielleicht die membranen vom sensor irgendwann schaden nehmen könnten 😳 der vorteil wäre dabei gewesen, dass die extra als externe sensoren gebaut werden, also ohne öffnungen im gehäuse und auch explizit wasserdicht was ja bei einer zisterne sinvoll wäre. leider haben die eine geringere reichweite und sind auch oft etwas gewinkelt verbaut, dadurch stimmt ja dann die gemessene distanz nicht mehr. vielleicht beim nächsten mal.

echolot

#include <NewPing.h>

#define TRIGGER_PIN 12
#define ECHO_PIN 11
#define MAX_DISTANCE 300

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);

void setup() {
  Serial.begin(9600);
}

void loop() {
  delay(200);
  unsigned int uS = sonar.ping();
  Serial.print(uS / US_ROUNDTRIP_CM);
  Serial.println("cm");
}

prototyp

nun kommt alles zusammen. ich stecke nochmal alles gemeinsam auf einem breadboard zusammen und lege meine pintabelle zur verkabelung fest. ich kombiniere die codeschnipsel für den pixelring, den sensor, den EEPROM und logge noch ein paar werte um später die funktionsweise überprüfen zu können. daraus ergibt sich der finale code. ich hatte mich noch entschieden die farbskala von rotgelbgrün auf gelb und blau umzustellen – rotgelbgrün empfand ich zu sehr als ampel oder einstufung, gelb passt nach meinem gefühl auch eher als trockene farbe zum etsprechenden zustand, also wenn die zisterne leer ist und blau als nasse farbe, die eher für wasser und einen hohen füllstand steht... um alles abzurunden lasse ich den wert der im EEPROM gesichert ist auf dem pixelring blinken, damit erennt man den letzten füllstand.

nano GND 5V A3 D11 D12
ultrasonic GND VCC ECHO TRIG
neopixel GND PWR IN

echolot

//get ready
#include <NewPing.h>
#define TRIGGER_PIN 12
#define ECHO_PIN 11
#define MAX_DISTANCE 350
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(24, A3);
#include <EEPROM.h>
uint32_t color = 0xffff00;
uint32_t blnk;
const float pi = 3.14159265359;
const float pxls = 24;
float lpxl,pcnt,ppxl,dst,vol,cap;

//tank dimensions in cm
float len = 313;
float rad = 100;
float top = 92;
float gnd = 50;

//run
void setup() {

  //boot pixel
  pixels.begin();
  pixels.setBrightness(35);
  pixels.setPixelColor(0,0x55ff00);
  pixels.show();
  delay(500);

  //measure average fill height
  for (int i=0; i<10; i++){
    delay(50);
    unsigned int uS = sonar.ping();
    dst += (uS/US_ROUNDTRIP_CM);
  }
  dst = dst/10;

  //calc max capacity and filled volume
  cap = pi*square(rad)*(len-top-gnd);
  vol = pi*square(rad)*(len-dst-gnd);

  //calc percentual pixel usage
  pcnt = 100/cap*vol;
  ppxl = pxls/100*pcnt;

  //fill ring
  for (int i=0; i<=ppxl; i++){
    if (6<i && i<14){
      color -= 0x1b1b00;
      color += 0x00001b;
    }
    pixels.setPixelColor(i,color);
    pixels.show();
    delay(35);
  }

  //manage value memory
  lpxl = EEPROM.read(1);
  EEPROM.write(1,ppxl);

  //get blink color
  blnk = pixels.getPixelColor(lpxl);
  if (blnk == 0x000000){
    blnk = 0xffffff;
  }

  //log
  Serial.begin(9600);
  Serial.println();
  Serial.println("TANK");
  Serial.print("l=");
  Serial.println(len);
  Serial.print("r=");
  Serial.println(rad);
  Serial.print("t=");
  Serial.println(top);
  Serial.print("g=");
  Serial.println(gnd);
  Serial.println();
  Serial.println("MEASURE");
  Serial.print("d=");
  Serial.println(dst);
  Serial.println();
  Serial.println("CALC");
  Serial.print("c=");
  Serial.println(cap);
  Serial.print("v=");
  Serial.println(vol);
  Serial.print("%=");
  Serial.println(pcnt);
  Serial.print("p=");
  Serial.println(ppxl);
  Serial.println();
  Serial.println("EEPROM");
  Serial.print("e=");
  Serial.println(lpxl);

}

void loop() {

  //blink
  delay(450);
  pixels.setPixelColor(lpxl, 0x000000);
  pixels.show();
  delay(900);
  pixels.setPixelColor(lpxl, blnk);
  pixels.show();
  delay(450);

}

fertigung

dann geht es ans zusammenbauen. ich hatte leider den fehler gemacht erst alles zu löten – dann fiel mir auf, dass ich ja erst den button in den deckel schrauben müsste, weil ja die kabel von unten heranführen müssen und der button aber von oben in den deckel geschraubt wird 😳 okay nochmal ablöten, richtigrum einbauen und wieder verlöten. dann fiel mir noch auf , dass ich ja eigentlich erstmal den ring an die deckelunterseite kleben sollte denn sonst wären die kabel des buttons ja drunter im weg 😳 okay, dann nochmal die kontakte ablöten und erstmal den ring aufkleben. heißkleber ist das mittel der wahl. ich liebe heißkleber 😃 dann die kontakte am button wieder festlöten und den rest festekleben. alle öffnungen verschließe ich so gut es geht mit der klebepistole, ich denke das sollte ausreichend wetterfest sein.

echolot

fertig! 👍 jetzt fehlt nur noch das anbringen an der zisterne. da bin ich doch auf den nächsten besuch gespannt. finales foto folgt... 😃

echolot