Nur ne kleine Server Bastelei - ePaper fürs neue NAS

Bohnenhans

Commander
Registriert
Okt. 2022
Beiträge
2.393
Das mit Abstand Wichtigste zuerst - jupp der Nachtfalter wurde vorsichtig ins schattige Freie umgesetzt :D bei uns stehen die Server halt im Gartengeschoss / Keller (Hangbebauung) und da muss man wenn Fenster offen sind öfters mal das lokale Ökosystem durch Umsetzen wieder ins Gleichgewicht bringen.

Weil's hier so oft geregnet hat hab ich mal meinen neuesten Server um ein kleines schwarz/weiss/rot ePaper Display ergänzt finde das ganz praktisch, dass man sieht was los ist wenn man dran vorbeikommt vor allem bei der roten Fabe fällt das dann doch auf - und auch was draufsteht wenn der aus ist (wir haben aktuell 4 Server, 3 davon in identischen Gehäusen)

Falls ein Fehler vorliegt z.B. HDD Ausfall CPU oder HDD zu warm oder kein Netzwerk etc lass ich den Fehler auch stehen wenn der Rechner ausgeschaltet ist, die Displays halten das ohne Strom auch über viele Jahre - echt ein coole Technik.

Evtl druck ich mir da so nen kleines Gehäuse für aber wegen der Gehäuserundung naja .... vielleicht im Winter
 

Anhänge

  • server_epaper1.jpg
    server_epaper1.jpg
    840,1 KB · Aufrufe: 278
  • server_epaper2.jpg
    server_epaper2.jpg
    94,6 KB · Aufrufe: 268
  • server_epaper3.jpg
    server_epaper3.jpg
    111,3 KB · Aufrufe: 265
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: CitroenDsVier, Fusionator, nononkk und 6 andere
Sehr cool, aber: Mehr Details please :hammer_alt:

Wo gekauft, wie angeschlossen, wie angesteuert? Über ein Script oder wie?
 
Angeschlossen hab ich das über einen ESP32-S2 aber halt nur weil ich davon einige in der Bastelkiste habe da hätte auch sicher was viel kleineres gereicht.

So Displays hab ich mir mal irgendwann bei reichelt mitbestellt - sowas kann man ich immer brauchen :D sind halt 2,13" rot, schwarz,weiss ePaper mit SPI von Waveshare - eigentlich zu teuer aber ePaper ist halt goil.

Ansonsten hab ich auf dem ESP einfach ein kleines Progrämmchen zusammengefrickelt das die serielle Schnittstelle ausliest - im ESP ist im USB Anschluss für die Stromversorgung ein USB Seriell Wandler verbaut.

Und da schieb ich dann halt einfach aus Linux im Script so Zeugs rein wie

Code:
echo "[INFO]Bla bla bla [NEWLINE]2.Zeile[NEWLINE]3.Zeile usw...." > /dev/ttyUSB0

Und halt je nach [INFO], [URGENT] [STATUS] etc werden halt dann die Farben im ESP32 gesetzt.

Also jetzt überhaupt nix dolles aber halt wenn man die Sachen eh da hat trotzdem ganz spassig und halt flexibel was ich ausgeben will. Stromverbrauch sind 0,05 W im Schnitt .... (Hab den ESP32 auf 80 mhz heruntergetaktet, Wifi off und ePaper Display wird immer auf PowerOff geschaltet wenn es was ausgegeben hat)

Und da an USB angeschlossen kann ich das Display bzw die Displays halt recht flexibel wie im Supermarkt ranmachen, wenn die Server mal nebeneneinander ins (Schwerlast) Regal kommen :daumen:
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: CitroenDsVier und Fusionator
Hmmm glaube das ist nicht so einfach mit Synology

Schliesslich müsste man damit so eine Anzeige Sinn macht dann ja auf dem Synology Scripte laufen lassen und aus Scripten auf HW Daten zugreifen können wie CPU Temp, HDD Temp und auf Daten zum freien Speicherplatz, RAID Info usw.
 
  • Gefällt mir
Reaktionen: Giggity
Das der Code auf dem ESP32 - der ist nur so lang weil ich mir die Möglichkeit offen halten will INFO, ALARME usw unterschiedlich darzustellen - aktuell sind nur die Textfarben anders - aber man weiss ja nie :D


Code:
#include <Arduino.h>
#include <GxEPD.h>
#include <GxIO/GxIO_SPI/GxIO_SPI.h>
#include <GxIO/GxIO.h>
#include <Wire.h>
#include <WiFi.h>
#include <GxGDEW0213Z16/GxGDEW0213Z16.h>  // 2.13" b/w/r
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>


// #include <Fonts/FreeMonoBold18pt7b.h>
// #include <Fonts/FreeMonoBold24pt7b.h>
// SPIClass fspi = SPIClass(FSPI);
                                                                                                                                                                                                                                                                                                                       
GxIO_Class io(SPI, /*CS*/GPIO_NUM_34, /*DC*/GPIO_NUM_37, /*RST*/GPIO_NUM_11);
GxEPD_Class display(io, /*RST*/GPIO_NUM_11, /*BUSY*/GPIO_NUM_10 );
const uint myDisplayRotation = 1;
String mySerialReceivedData_Old = "";


// Display.Font muss gesetzt sein
String shortenToDisplay(String _myMessage)
{
  if (_myMessage.length() > 0)
  {
    int16_t myTextX, myTextY;
    uint16_t myTextWidth, myTextHeight;
    uint16_t myTextMaxHeight;
    // weil automatischer Zeilenumbruch auf virt. Display
    display.getTextBounds("8ygF", 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextMaxHeight);
    myTextMaxHeight++;
    myTextMaxHeight++;
    display.getTextBounds(_myMessage, 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextHeight);
    while (myTextHeight > myTextMaxHeight)
    {
      if (_myMessage.length() > 0)
        _myMessage.remove(_myMessage.length()-1,1);
      else return("");
      display.getTextBounds(_myMessage, 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextHeight);
    }
  }
  return(_myMessage);
}

void drawAlert(String _Message1, String _Message2)
{
  if ((_Message1.length() == 0) && (_Message2.length() == 0))
     return;
  uint16_t myTextPosX = 0;
  uint16_t myTextPosY = 0;
  int16_t myTextX, myTextY;
  uint16_t myTextWidth, myTextHeight;
  display.setRotation(myDisplayRotation);
  display.fillScreen(GxEPD_RED);
  display.setTextColor(GxEPD_WHITE);
  if (_Message1.length() > 0)
  {
    display.setFont(&FreeMonoBold12pt7b);
    _Message1 = shortenToDisplay(_Message1);
    display.getTextBounds(_Message1, 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextHeight);
    myTextPosX = ((display.width() -  myTextWidth) / 2) - myTextX;
    myTextPosY = ((display.height() - myTextHeight) / 2) - myTextY;
 
    if (_Message2.length() > 0)
      myTextPosY = myTextPosY - (myTextHeight/2);
 
    display.setCursor(myTextPosX, myTextPosY);
    display.print(_Message1.c_str());
  }
  if (_Message2.length() > 0)
  {
    display.setFont(&FreeMonoBold9pt7b);
    _Message2 = shortenToDisplay(_Message2);
    display.getTextBounds(_Message2, 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextHeight);
    myTextPosX = ((display.width() -  myTextWidth) / 2) - myTextX;
    display.setCursor(myTextPosX, (display.height()-myTextHeight));
    display.print(_Message2.c_str());
  }
}

void drawUrgent(String _Message1, String _Message2)
{
  if ((_Message1.length() == 0) && (_Message2.length() == 0))
     return;
  uint16_t myTextPosX = 0;
  uint16_t myTextPosY = 0;
  int16_t myTextX, myTextY;
  uint16_t myTextWidth, myTextHeight;
  display.setRotation(myDisplayRotation);
  display.fillScreen(GxEPD_WHITE);
  display.setTextColor(GxEPD_RED);
  if (_Message1.length() > 0)
  {
    display.setFont(&FreeMonoBold12pt7b);
    _Message1 = shortenToDisplay(_Message1);
    display.getTextBounds(_Message1, 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextHeight);
    myTextPosX = ((display.width() -  myTextWidth) / 2) - myTextX;
    myTextPosY = ((display.height() - myTextHeight) / 2) - myTextY;
 
    if (_Message2.length() > 0)
      myTextPosY = myTextPosY - (myTextHeight/2);
 
    display.setCursor(myTextPosX, myTextPosY);
    display.print(_Message1.c_str());
  }
  if (_Message2.length() > 0)
  {
    display.setFont(&FreeMonoBold9pt7b);
    _Message2 = shortenToDisplay(_Message2);
    display.getTextBounds(_Message2, 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextHeight);
    myTextPosX = ((display.width() -  myTextWidth) / 2) - myTextX;
    display.setCursor(myTextPosX, (display.height()-myTextHeight));
    display.print(_Message2.c_str());
  }
}
void drawInfo(String _Message1, String _Message2)
{
  if ((_Message1.length() == 0) && (_Message2.length() == 0))
     return;
  uint16_t myTextPosX = 0;
  uint16_t myTextPosY = 0;
  int16_t myTextX, myTextY;
  uint16_t myTextWidth, myTextHeight;
  display.setRotation(myDisplayRotation);
  display.fillScreen(GxEPD_BLACK);
  display.setTextColor(GxEPD_WHITE);
  if (_Message1.length() > 0)
  {
    display.setFont(&FreeMonoBold12pt7b);
    _Message1 = shortenToDisplay(_Message1);
    display.getTextBounds(_Message1, 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextHeight);
    myTextPosX = ((display.width() -  myTextWidth) / 2) - myTextX;
    myTextPosY = ((display.height() - myTextHeight) / 2) - myTextY;
 
    if (_Message2.length() > 0)
      myTextPosY = myTextPosY - (myTextHeight/2);
 
    display.setCursor(myTextPosX, myTextPosY);
    display.print(_Message1.c_str());
  }
  if (_Message2.length() > 0)
  {
    display.setFont(&FreeMonoBold9pt7b);
    _Message2 = shortenToDisplay(_Message2);
    display.getTextBounds(_Message2, 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextHeight);
    myTextPosX = ((display.width() -  myTextWidth) / 2) - myTextX;
    display.setCursor(myTextPosX, (display.height()-myTextHeight));
    display.print(_Message2.c_str());
  }
}



void drawStatus(String _Message1, String _Message2, String _Message3, String _Message4)
{
  display.setRotation(myDisplayRotation);
  display.setTextColor(GxEPD_BLACK);
  display.fillScreen(GxEPD_WHITE);
  display.setFont(&FreeMonoBold9pt7b);
  _Message1 = shortenToDisplay(_Message1);
  _Message2 = shortenToDisplay(_Message2);
  _Message3 = shortenToDisplay(_Message3);
  _Message4 = shortenToDisplay(_Message4);

  uint16_t myTextPosX = 0;
  uint16_t myTextPosY = 0;
  int16_t myTextX, myTextY;
  uint16_t myTextWidth, myTextHeight;
  display.getTextBounds("8ygF", 0, 0, &myTextX, &myTextY, &myTextWidth, &myTextHeight);
  uint16_t myYPosAdd = (display.height()-5) / 4;
  if (_Message1.length() > 0)
  {
    display.setCursor(0, myYPosAdd);
    display.print(_Message1.c_str());
  }
 
  if (_Message2.length() > 0)
  {
    display.setCursor(0, myYPosAdd*2);
    display.print(_Message2.c_str());
  }
 
  if (_Message3.length() > 0)
  {
    display.setCursor(0, myYPosAdd*3);
    display.print(_Message3.c_str());
  }
 
  if (_Message4.length() > 0)
  {
    display.setCursor(0, myYPosAdd*4);
    display.print(_Message4.c_str());
  }
}

String getSubString(String _myString, String _mySeparator, uint8_t _myPos)
{
  int16_t LastPos = 0;
  String myResultString = "";
  for (int i=0; i < _myPos; i++)
  {
    LastPos = _myString.indexOf(_mySeparator, LastPos);
    if (LastPos < 0)
       return(myResultString);
    LastPos = LastPos + _mySeparator.length();
  }
  if (LastPos >= 0)
  {
    myResultString = _myString.substring(LastPos);
    if (myResultString.indexOf(_mySeparator) > -1)
       return(myResultString.substring(0, myResultString.indexOf(_mySeparator)));
    else return(myResultString);
  }
  else return (myResultString);
}
void setup()
{
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
 
  setCpuFrequencyMhz(80);
  Serial.begin(115200);
  display.init();
 
  drawInfo("STARTE...","");
  display.update();
  display.powerDown();
}
void loop()
{
  String myReceivedSerialData = "";
  while (Serial.available())
  {
    myReceivedSerialData = Serial.readStringUntil('\r');
    if (myReceivedSerialData == nullptr) return;
    if (myReceivedSerialData.length() < 5) return;
    if (myReceivedSerialData == mySerialReceivedData_Old) return;
 
    String myStatusMessageLine1 = "";
    String myStatusMessageLine2 = "";
    String myStatusMessageLine3 = "";
    String myStatusMessageLine4 = "";
    if (myReceivedSerialData.startsWith("[ALERT]"))
    {
      mySerialReceivedData_Old = myReceivedSerialData;
    
      myReceivedSerialData.remove(0, 7);
      if (myReceivedSerialData.indexOf("[NEWLINE]") >= 0)
      {
        myStatusMessageLine2 = myReceivedSerialData.substring(myReceivedSerialData.indexOf("[NEWLINE]")+9);
        myStatusMessageLine1 = myReceivedSerialData.substring(0, myReceivedSerialData.indexOf("[NEWLINE]"));
      }
      else myStatusMessageLine1 = myReceivedSerialData;
    
      drawAlert(myStatusMessageLine1, myStatusMessageLine2);
      display.update();
    }
    else if (myReceivedSerialData.startsWith("[INFO]"))
    {
      mySerialReceivedData_Old = myReceivedSerialData;
      myReceivedSerialData.remove(0, 6);
      if (myReceivedSerialData.indexOf("[NEWLINE]") >= 0)
      {
        myStatusMessageLine2 = myReceivedSerialData.substring(myReceivedSerialData.indexOf("[NEWLINE]")+9);
        myStatusMessageLine1 = myReceivedSerialData.substring(0, myReceivedSerialData.indexOf("[NEWLINE]"));
      }
      else myStatusMessageLine1 = myReceivedSerialData;
  
      drawInfo(myStatusMessageLine1, myStatusMessageLine2);
      display.update();
    }
 
    else if (myReceivedSerialData.startsWith("[URGENT]"))
    {
      mySerialReceivedData_Old = myReceivedSerialData;
      myReceivedSerialData.remove(0, 8);
      if (myReceivedSerialData.indexOf("[NEWLINE]") >= 0)
      {
        myStatusMessageLine2 = myReceivedSerialData.substring(myReceivedSerialData.indexOf("[NEWLINE]")+9);
        myStatusMessageLine1 = myReceivedSerialData.substring(0, myReceivedSerialData.indexOf("[NEWLINE]"));
      }
      else myStatusMessageLine1 = myReceivedSerialData;
  
      drawUrgent(myStatusMessageLine1, myStatusMessageLine2);
      display.update();
    }
  
    else if (myReceivedSerialData.startsWith("[STATUS]"))
    {
      mySerialReceivedData_Old = myReceivedSerialData;
      myReceivedSerialData.remove(0,8);
      myStatusMessageLine1 = getSubString(myReceivedSerialData, "[NEWLINE]", 0);
      myStatusMessageLine2 = getSubString(myReceivedSerialData, "[NEWLINE]", 1);
      myStatusMessageLine3 = getSubString(myReceivedSerialData, "[NEWLINE]", 2);
      myStatusMessageLine4 = getSubString(myReceivedSerialData, "[NEWLINE]", 3);
      drawStatus(myStatusMessageLine1, myStatusMessageLine2, myStatusMessageLine3, myStatusMessageLine4);
      display.update();
    }       
    display.powerDown();
  }
};


und auf der Linux Kiste rufe ich das halt dann aus einem bashscript z.B. so auf das zeigt dann halt z.B. wenn ein Scrub aktuell läuft - und wie lange der noch braucht

ich setze halt immer die Baudrate mit stty neu dann kann man das Display vom USB entfernen und es tut wieder auf 115.200 Baud sobald man es wieder an USB ansteckt

Code:
myScrubStatus=$(zpool status | grep "scrub in progress")
if [ -n "$myScrubStatus" ]; then
   myScrubToGo=$(zpool status -v tank | grep repaired | sed 's/.*done, //')
   stty 115200 -F /dev/ttyUSB0 && echo "[INFO]ZFS Scrub[NEWLINE]$myScrubToGo" > /dev/ttyUSB0
   myZFSStatus=""
fi
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Chris3511
So habe mal das Display etwas beschleunigt - damit verzichte ich zwar auf die rote Farbe aber dafür geht es jetzt echt fix für ein ePaper - beim Verzicht auf rot kann ich den Bildschirm in Teilbereichen updaten ohne das gesamte Display updaten zu müssen

Und da ich den Partial Code eh schon woanders hatte, hat das auch nur 10 Min gedauert.

Nun habe ich dieses "Flackern" beim Aufbau von einem neuen Displayinhalt der TrueNAS Daten halt nicht mehr das wenn man die 3 Farben nutzen will echt lange dauert....

"ZFS Ok" hab ich auch als Statusmeldung rausgeworfen, ich zeige jetzt nur an wenn es irgendwelche Fehler im ZFS gibt - und das dann halt so lange bis der Fehler behoben ist, das macht mehr Sinn.

Und ein OFF Bild wenn der TrueNas Server ganz aus oder ohne Strom ist.

Und da die Server bei uns im Keller in einem Raum stehen habe ich noch 2 LEDs angeschlossen, die mir dann auf den ersten Blick per LED zusätzlich zeigen ob das RAID ok ist = grün oder was nicht passt = rot.

Das fällt einfach mehr auf beim Vorbeigehen - hehe und der ESP32 hat halt noch soooo viele freie Pins :D

Hoffe dass der durch die Zusatz LEDs auch mehr FPS oder mehr Speicherplatz hat.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: dms
So sieht's jetzt mal aus - bis auf die Gehäuseintegration jetzt eigentlich fertig für das was ich so aktuell wollte

- mit einem simulierten ZFS Ausfall - hehe noch simuliert :D

Ich find das halt einfach praktisch dass ich wenn ich dran vorbeilaufe und der Server gerade an ist ich sofort sehe ob da was nicht passt.
 

Anhänge

  • truenas_display.mp4
    9 MB
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Chris3511
Zurück
Oben