ESP8265 VFD Clock von "FLORA" auf GitHub

Ich hatte noch vier Stück IV-22 (VFD) in der Schublade und versuche den Sketch auf GitHub :
https://github.com/mcer12/Flora-ESP8266/tree/main/Firmware/FLORA_FIRMWARE
erst einmal zu testen bevor ich die Hardware aufbaue.
Das Projekt ist recht interessant und schon ein Versuch wert.
Das Überprüfen / kompilieren läuft durch, auch habe ich bei

 const char* ssid = json["ssid"].as<const char*>(); // meine SSID
 const char* pass = json["pass"].as<const char*>(); // mein Password

eingetragen aber die ESP verbindet sich nicht meinen Router / Fritzbox.
Auch sehe ich nicht den AP "FLORA" in meiner WLAN Liste.
Die Ausgabe auf den seriellen Monitor beschränkt sich nur die die Anzeige
des wdt rest und der checksum - es erfolgt also auch keine Ausgabe über
"Serial.print".
Da ich mit der ESP noch nicht viel gemacht habe, wäre etwas Hilfe notwendig.
Kann dass sein, dass es nur an der "nackten" ESP leigt - keine weitere
Hardware angeschlossen ist und diese "irgendwo festhängt" ?
"FLORA" sagt auf GitHub, dass alles getestet ist und läuft.
Bin jetzt etwas ratlos ...
Danke für das helfen.

Kannst du uns bitte einen Link des Teils posten ?

Die IV-22 ist eine VFD (grün leuchtend) :

IV-22.pdf (238.7 KB)

Der Schaltplan ist auch auf GitHub - hier mal extra angehängen:

IV-22_V2_SCHEMATIC.pdf (274.5 KB)

Das Gute ist, "FLORA" bietet die kompletten Gerber Files mit an - d.h. man muss
nicht von Null anfängen.
Leider scheitere ich schon bei den ersten Test.
Fertige "VFD Clocks" gibt es u.a. hier :

https://kama-labs.com/vfd_iv-22

aber ich habe ja schon fast alles in der "Bastelkiste"

P.S. die IV-22 gibt es u.a. bei eBay - z.B. :
https://www.ebay.de/itm/276530464398

  • wenn es ein weiteres Interesse an den Projekt gibt

Genau so is es, der Serial Monitor bekommt die Daten vom ESP.
Noch was du darfst die NIXIE nicht direkt verbinden mit ESP, musst ein Treiber IC vor dem klemmen.
Ich sollte mir auch den Schaltplan ansehen :wink:

Durchaus möglich. Zeige mal den Sketch den du verwendest.

Ich habe den Schaltplan mit angegeben - Treiber sind vorhanden TD62783AD
(Darlington Treiber bis max. 50V).
P.S NIXIE und VFD ist nicht gleich - wird aber gern in einem Topf geworfen.

Sorry,
habe den Schaltplan übersehen :wink:

/*
 * GNU General Public License v3.0
 * Copyright (c) 2021 Martin Cerny
*/

#include <FS.h>
#include <ArduinoJson.h>
#include <math.h>

#include "ESP8266TimerInterrupt.h"
#include "SPI.h"

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <DNSServer.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <Ticker.h>

#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>

#include <TimeLib.h>
#include <Timezone.h>

// Pick a clock version below!
//#define CLOCK_VERSION_IV6
#define CLOCK_VERSION_IV6_V2
//#define CLOCK_VERSION_IV12
//#define CLOCK_VERSION_IV22

#if !defined(CLOCK_VERSION_IV6) && !defined(CLOCK_VERSION_IV6_V2) && !defined(CLOCK_VERSION_IV12) && !defined(CLOCK_VERSION_IV22)
#error "You have to select a clock version! Line 25"
#endif

#define AP_NAME "FLORA_"
#define FW_NAME "FLORA"
#define FW_VERSION "5.1.3"
#define CONFIG_TIMEOUT 300000 // 300000 = 5 minutes

// ONLY CHANGE DEFINES BELOW IF YOU KNOW WHAT YOU'RE DOING!
#define NORMAL_MODE 0
#define OTA_MODE 1
#define CONFIG_MODE 2
#define CONFIG_MODE_LOCAL 3
#define CONNECTION_FAIL 4
#define UPDATE_SUCCESS 1
#define UPDATE_FAIL 2
#define DATA 13
#define CLOCK 14
#define LATCH 15
#define TIMER_INTERVAL_uS 200 // 200 = safe value for 6 digits. You can go down to 150 for 4-digit one. Going too low will cause crashes.

// User global vars
const char* dns_name = "flora"; // only for AP mode
const char* update_path = "/update";
const char* update_username = "flora";
const char* update_password = "flora";
const char* ntpServerName = "pool.ntp.org";

const int dotsAnimationSteps = 2000; // dotsAnimationSteps * TIMER_INTERVAL_uS = one animation cycle time in microseconds

const uint8_t PixelCount = 14; // Addressable LED count

HsbColor red[] = {
  HsbColor(RgbColor(100, 0, 0)), // LOW
  HsbColor(RgbColor(150, 0, 0)), // MEDIUM
  HsbColor(RgbColor(200, 0, 0)), // HIGH
};
HsbColor green[] = {
  HsbColor(RgbColor(0, 100, 0)), // LOW
  HsbColor(RgbColor(0, 150, 0)), // MEDIUM
  HsbColor(RgbColor(0, 200, 0)), // HIGH
};
HsbColor blue[] = {
  HsbColor(RgbColor(0, 0, 100)), // LOW
  HsbColor(RgbColor(0, 0, 150)), // MEDIUM
  HsbColor(RgbColor(0, 0, 200)), // HIGH
};
HsbColor yellow[] = {
  HsbColor(RgbColor(100, 100, 0)), // LOW
  HsbColor(RgbColor(150, 150, 0)), // MEDIUM
  HsbColor(RgbColor(200, 200, 0)), // HIGH
};
HsbColor purple[] = {
  HsbColor(RgbColor(100, 0, 100)), // LOW
  HsbColor(RgbColor(150, 0, 150)), // MEDIUM
  HsbColor(RgbColor(200, 0, 200)), // HIGH
};
HsbColor azure[] = {
  HsbColor(RgbColor(0, 100, 100)), // LOW
  HsbColor(RgbColor(0, 150, 150)), // MEDIUM
  HsbColor(RgbColor(0, 200, 200)), // HIGH
};

#if defined(CLOCK_VERSION_IV6) || defined(CLOCK_VERSION_IV6_V2)
HsbColor colonColorDefault[] = {
  HsbColor(RgbColor(30, 70, 50)), // LOW
  HsbColor(RgbColor(50, 100, 80)), // MEDIUM
  HsbColor(RgbColor(80, 130, 100)), // HIGH
};
#else
HsbColor colonColorDefault[] = {
  HsbColor(RgbColor(30, 70, 50)), // LOW
  HsbColor(RgbColor(50, 100, 80)), // MEDIUM
  HsbColor(RgbColor(120, 220, 140)), // HIGH
};
/*
  RgbColor colonColorDefault[] = {
  RgbColor(30, 70, 50), // LOW
  RgbColor(50, 100, 80), // MEDIUM
  RgbColor(100, 200, 120), // HIGH
  };
*/
#endif

/*
  RgbColor colonColorDefault[] = {
  RgbColor(30, 6, 1), // LOW
  RgbColor(38, 8, 2), // MEDIUM
  RgbColor(50, 10, 2), // HIGH
  };
*/
RgbColor currentColor = RgbColor(0, 0, 0);
//RgbColor colonColorDefault = RgbColor(90, 27, 7);
//RgbColor colonColorDefault = RgbColor(38, 12, 2);

#if defined(CLOCK_VERSION_IV6)
const uint8_t registersCount = 6;
const uint8_t segmentCount = 8;
const uint8_t digitPins[registersCount][segmentCount] = {
  {40, 41, 42, 43, 44, 45, 46, 47}, // BR | B | BL | TL | M | T | TR | DOT
  {32, 33, 34, 35, 36, 37, 38, 39}, // BR | B | BL | TL | M | T | TR | DOT
  {27, 25, 26, 28, 29, 30, 31, 24}, // BR | B | BL | TL | M | T | TR | DOT
  {16, 17, 18, 19, 20, 21, 22, 23}, // BR | B | BL | TL | M | T | TR | DOT
  {8, 9, 10, 11, 12, 13, 14, 15}, // BR | B | BL | TL | M | T | TR | DOT
  {0, 1, 2, 3, 4, 5, 6, 7}, // BR | B | BL | TL | M | T | TR | DOT
};
#elif defined(CLOCK_VERSION_IV6_V2)
const uint8_t registersCount = 6;
const uint8_t segmentCount = 8;
const uint8_t digitPins[registersCount][segmentCount] = {
  {40, 41, 42, 43, 44, 45, 46, 47}, // BR | B | BL | TL | M | T | TR | DOT
  {39, 32, 33, 34, 35, 36, 37, 38}, // BR | B | BL | TL | M | T | TR | DOT
  {31, 24, 25, 26, 27, 28, 29, 30}, // BR | B | BL | TL | M | T | TR | DOT
  {16, 17, 18, 19, 20, 21, 22, 23}, // BR | B | BL | TL | M | T | TR | DOT
  {8, 9, 10, 11, 12, 13, 14, 15}, // BR | B | BL | TL | M | T | TR | DOT
  {7,0, 1, 2, 3, 4, 5, 6}, // BR | B | BL | TL | M | T | TR | DOT
};
#elif defined(CLOCK_VERSION_IV12)
const uint8_t registersCount = 6;
const uint8_t segmentCount = 7; // IV12 doesn't have dot
const uint8_t digitPins[registersCount][segmentCount] = {
  {46, 47, 41, 40, 42, 44, 45}, // BR | B | BL | TL | M | T | TR | DOT
  {39, 32, 34, 33, 35, 37, 38}, // BR | B | BL | TL | M | T | TR | DOT
  {31, 24, 26, 25, 27, 29, 30}, // BR | B | BL | TL | M | T | TR | DOT
  {23, 16, 18, 17, 19, 21, 22}, // BR | B | BL | TL | M | T | TR | DOT
  {15, 8, 10, 9, 11, 13, 14}, // BR | B | BL | TL | M | T | TR | DOT
  {1, 0, 4, 3, 5, 7, 2}, // BR | B | BL | TL | M | T | TR | DOT
};
#elif defined(CLOCK_VERSION_IV22)
const uint8_t registersCount = 4;
const uint8_t segmentCount = 8;
const uint8_t digitPins[registersCount][segmentCount] = {
  {26, 24, 31, 30, 27, 29, 28, 25}, // BR | B | BL | TL | M | T | TR | DOT
  {20, 16, 23, 22, 19, 17, 18, 21}, // BR | B | BL | TL | M | T | TR | DOT
  {14, 9, 10, 8, 13, 11, 12, 15}, // BR | B | BL | TL | M | T | TR | DOT
  {2, 0, 7, 6, 3, 5, 4, 1}, // BR | B | BL | TL | M | T | TR | DOT
};
#endif

uint8_t letter_p[8] = {0, 0, 1, 1, 1, 1, 1, 0};
uint8_t letter_i[8] = {0, 0, 1, 1, 0, 0, 0, 0};
uint8_t dot[8] = {0, 0, 0, 0, 0, 0, 0, 1};
uint8_t numbers[10][8] = {
  {1, 1, 1, 1, 0, 1, 1, 0}, // 0 ==>  BR | B | BL | TL | M | T | TR | DOT
  {1, 0, 0, 0, 0, 0, 1, 0}, // 1 ==>  BR | B | BL | TL | M | T | TR | DOT
  {0, 1, 1, 0, 1, 1, 1, 0}, // 2 ==>  BR | B | BL | TL | M | T | TR | DOT
  {1, 1, 0, 0, 1, 1, 1, 0}, // 3 ==>  BR | B | BL | TL | M | T | TR | DOT
  {1, 0, 0, 1, 1, 0, 1, 0}, // 4 ==>  BR | B | BL | TL | M | T | TR | DOT
  {1, 1, 0, 1, 1, 1, 0, 0}, // 5 ==>  BR | B | BL | TL | M | T | TR | DOT
  {1, 1, 1, 1, 1, 1, 0, 0}, // 6 ==>  BR | B | BL | TL | M | T | TR | DOT
  {1, 0, 0, 0, 0, 1, 1, 0}, // 7 ==>  BR | B | BL | TL | M | T | TR | DOT
  {1, 1, 1, 1, 1, 1, 1, 0}, // 8 ==>  BR | B | BL | TL | M | T | TR | DOT
  {1, 1, 0, 1, 1, 1, 1, 0}, // 9 ==>  BR | B | BL | TL | M | T | TR | DOT
};
volatile uint8_t segmentBrightness[registersCount][8];
volatile uint8_t targetBrightness[registersCount][8];

// 32 steps of brightness * 200uS => 6.4ms for full refresh => 160Hz... pretty good!
// 48 steps => 100hz
volatile uint8_t shiftedDutyState[registersCount];
const uint8_t pwmResolution = 48; // should be in the multiples of dimmingSteps to enable smooth crossfade
const uint8_t dimmingSteps = 8;

// MAX BRIGHTNESS PER DIGIT
// These need to be multiples of 8 to enable crossfade! Must be less or equal as pwmResolution.
// Set maximum brightness for reach digit separately. This can be used to normalize brightness between new and burned out tubes.
// Last two values are ignored in 4-digit clock
uint8_t bri_vals_separate[3][6] = {
  {8, 8, 8, 8, 8, 8}, // Low brightness
  {24, 24, 24, 24, 24, 24}, // Medium brightness
  {48, 48, 48, 48, 48, 48}, // High brightness
};


// Better left alone global vars
volatile bool isPoweredOn = true;
unsigned long configStartMillis, prevDisplayMillis;
volatile int activeDot;
uint8_t deviceMode = NORMAL_MODE;
bool timeUpdateFirst = true;
volatile bool toggleSeconds;
bool breatheState;
byte mac[6];
volatile int dutyState = 0;
volatile uint8_t digitsCache[] = {0, 0, 0, 0};
volatile byte bytes[registersCount];
volatile byte prevBytes[registersCount];
volatile uint8_t bri = 0;
volatile uint8_t crossFadeTime = 0;
uint8_t timeUpdateStatus = 0; // 0 = no update, 1 = update success, 2 = update fail,
uint8_t failedAttempts = 0;
volatile bool enableDotsAnimation;
volatile unsigned short dotsAnimationState;
RgbColor colonColor;
IPAddress ip_addr;

TimeChangeRule EDT = {"EDT", Last, Sun, Mar, 1, 120};  //UTC + 2 hours
TimeChangeRule EST = {"EST", Last, Sun, Oct, 1, 60};  //UTC + 1 hours
Timezone TZ(EDT, EST);
NeoPixelBus<NeoGrbFeature, NeoWs2813Method> strip(PixelCount);
NeoGamma<NeoGammaTableMethod> colorGamma;
NeoPixelAnimator animations(PixelCount);
DynamicJsonDocument json(2048); // config buffer
Ticker fade_animation_ticker;
Ticker onceTicker;
Ticker colonTicker;
ESP8266Timer ITimer;
DNSServer dnsServer;
ESP8266WebServer server(80);
WiFiUDP Udp;
ESP8266HTTPUpdateServer httpUpdateServer;
unsigned int localPort = 8888;  // local port to listen for UDP packets


// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  Serial.begin(115200);
  Serial.println("");

  if (!SPIFFS.begin()) {
    Serial.println("[CONF] Failed to mount file system");
  }
  readConfig();

  initStrip();
  initRgbColon();
  initScreen();

  WiFi.macAddress(mac);

  const char* ssid = json["ssid"].as<const char*>(); // meine ssid
  const char* pass = json["pass"].as<const char*>(); // mein password
  const char* ip = json["ip"].as<const char*>();
  const char* gw = json["gw"].as<const char*>();
  const char* sn = json["sn"].as<const char*>();

  if (ssid != NULL && pass != NULL && ssid[0] != '\0' && pass[0] != '\0') {
    Serial.println("[WIFI] Connecting to: " + String(ssid));
    WiFi.mode(WIFI_STA);

    if (ip != NULL && gw != NULL && sn != NULL && ip[0] != '\0' && gw[0] != '\0' && sn[0] != '\0') {
      IPAddress ip_address, gateway_ip, subnet_mask;
      if (!ip_address.fromString(ip) || !gateway_ip.fromString(gw) || !subnet_mask.fromString(sn)) {
        Serial.println("[WIFI] Error setting up static IP, using auto IP instead. Check your configuration.");
      } else {
        WiFi.config(ip_address, gateway_ip, subnet_mask);
      }
    }
    // serializeJson(json, Serial);

    enableDotsAnimation = true; // Start the dots animation

    updateColonColor(yellow[bri]);
    strip_show();

    WiFi.hostname(AP_NAME + macLastThreeSegments(mac));
    WiFi.begin(ssid, pass);

    //startBlinking(200, colorWifiConnecting);

    for (int i = 0; i < 1000; i++) {
      if (WiFi.status() != WL_CONNECTED) {
        if (i > 200) { // 20s timeout
          enableDotsAnimation = false;
          deviceMode = CONFIG_MODE;
          updateColonColor(red[bri]);
          strip_show();
          Serial.print("[WIFI] Failed to connect to: " + String(ssid) + ", going into config mode.");
          delay(500);
          break;
        }
        delay(100);
      } else {
        updateColonColor(green[bri]);
        enableDotsAnimation = false;
        strip_show();
        Serial.print("[WIFI] Successfully connected to: ");
        Serial.println(WiFi.SSID());
        Serial.print("[WIFI] Mac address: ");
        Serial.println(WiFi.macAddress());
        Serial.print("[WIFI] IP address: ");
        Serial.println(WiFi.localIP());
        delay(1000);
        break;
      }
    }
  } else {
    deviceMode = CONFIG_MODE;
    Serial.println("[CONF] No credentials set, going to config mode.");
  }

  if (deviceMode == CONFIG_MODE || deviceMode == CONNECTION_FAIL) {
    startConfigPortal(); // Blocking loop
  } else {
    ndp_setup();
    startServer();
  }

  //initScreen();

  if (json["rst_cycle"].as<unsigned int>() == 1) {
    cycleDigits();
    delay(500);
  }

  if (json["rst_ip"].as<unsigned int>() == 1) {
    showIP(5000);
    delay(500);
  }

  /*
    if (!MDNS.begin(dns_name)) {
      Serial.println("[ERROR] MDNS responder did not setup");
    } else {
      Serial.println("[INFO] MDNS setup is successful!");
      MDNS.addService("http", "tcp", 80);
    }
  */
}

// the loop function runs over and over again forever
void loop() {

  if (timeUpdateFirst == true && timeUpdateStatus == UPDATE_FAIL || deviceMode == CONNECTION_FAIL) {
    setAllDigitsTo(0);
    updateColonColor(red[bri]); // red
    strip_show();
    delay(10);
    return;
  }

  if (millis() - prevDisplayMillis >= 1000) { //update the display only if time has changed
    prevDisplayMillis = millis();
    toggleNightMode();

    if (timeUpdateStatus) {
      if (timeUpdateStatus == UPDATE_SUCCESS) {
        setTemporaryColonColor(5, green[bri]);
      }
      if (timeUpdateStatus == UPDATE_FAIL) {
        if (failedAttempts > 2) {
          colonColor = red[bri];
        } else {
          setTemporaryColonColor(5, red[bri]);
        }
      }
      timeUpdateStatus = 0;
    }

    handleColon();
    showTime();
  }

  animations.UpdateAnimations();
  strip_show();

  //MDNS.update();
  server.handleClient();
  delay(1); // Keeps the ESP cold!

}

Macht nichts - alles Gut , geht mir manchmal auch so.

Ok, da der Sketch offensichtlich nicht von dir geschrieben wurde, halte ich mich mal mit meinem Kommentar zurück. Nur soweit, der sollte mal aufgeräumt werden. Die doppelten "include" machen es nicht übersichtlicher.

Und mein Tipp:
Fange klein an und baue nach den aktuellen Beispielen in der IDE ein funktionierendes Beispiel auf.

Naja - Beispiele aus der IDE ?
Dass hilft mir in diesen konkreten Fall auch nicht weiter

Und wieso nicht ?
Bisher haben alle, zumindest die meisten damit etwas gelernt.
Warum sollte dir das nicht helfen ?
Du musst doch erstmal eine funktionierende Verbindung zum Router aufbauen. Wenn das dann klappt, kannst du den funktionierenden Sketch mit dem fremden Sketch vergleichen. So kann man das einfach nachvollziehen und sogar daraus lernen. Wenn man es dann will. :wink:

Edit:
In deinem Sketch ist z.B. nicht zu erkennen, wie der sich im Netzwerk anmeldet.

Am Rechner sehe das :wink:

WiFi.begin(ssid, pass);

    //startBlinking(200, colorWifiConnecting);

    for (int i = 0; i < 1000; i++) {
      if (WiFi.status() != WL_CONNECTED) {
        if (i > 200) { // 20s timeout
          enableDotsAnimation = false;
          deviceMode = CONFIG_MODE;
          updateColonColor(red[bri]);
          strip_show();
          Serial.print("[WIFI] Failed to connect to: " + String(ssid) + ", going into config mode.");
          delay(500);
          break;
        }
        delay(100);
      } else {
        updateColonColor(green[bri]);
        enableDotsAnimation = false;
        strip_show();
        Serial.print("[WIFI] Successfully connected to: ");
        Serial.println(WiFi.SSID());
        Serial.print("[WIFI] Mac address: ");
        Serial.println(WiFi.macAddress());
        Serial.print("[WIFI] IP address: ");
        Serial.println(WiFi.localIP());
        delay(1000);
        break;
      }
    }

Ja...stimmt.
Ein gut lesbarer Sketch ist was anderes.
Aber ok, ist ein anderes Thema.

Dann bleibt noch die Frage, ob das alles in ein ESP8265 reinpasst.
Der hat ja nur 1 MB und das alles mit FS und OTA ?

Naja - der Upload geht - aber keine Funktion wie gewünscht.
Daher denke ich, dass der Speicherplatz ausreichend ist.

Was sagt denn der Speicherverbrauch in der IDE ?

Ups, meinst du jetzt ESP8265 oder ESP8285 ?
Den x65 gibt es nicht.

ESP8285 - Zitat :
"Der Sketch verwendet 443753 Bytes (49%) des Programmspeicherplatzes.
Das Maximum sind 892912 Bytes.
Globale Variablen verwenden 38832 Bytes (47%) des dynamischen Speichers, 43088 Bytes für lokale Variablen verbleiben. Das Maximum sind 81920 Bytes.

Ok, dann ist das wohl kein Problem.
Stellt sich nur die Frage, ob der ESP8285 auch mit den Libraries klar kommt.
Auch wenn der Sketch fehlerfrei kompiliert, kann es durchaus Probleme geben.

Nochmal, du solltest einfach einen Testaufbau mit den Beispielen der IDE machen, um dem Fehler auf die Spur zu kommen.

Leider hast du meine Frage in Post#12 zu diesem Thema noch nicht beantwortet.

Ich bitte um Nachsicht, aber der Code von "Flora" ist zu komplex - wie soll ich
als "Bastler" ohne IT Kenntnisse mit nur den Beispielen aus der IDE den Fehler
finden ?
Die Beispiele aus der IDE sind nicht so verschachtelt wie der Code von "Flora".
Dass ist m.M. nicht 1:1 übertragbar - also bitte nicht nur sagen ich bin faul
oder "lernresistent" .
Wer glaubt über allen zu stehen , der urteilt gern über andere.
Andere "Teilnehmer" dieser Diskussion mit mehr Wissen sind auch deutlich überfordert -
wie soll ich da erst reagieren ?

Wenn man zu erst Raketenwissenschaft verstehen will und erst später sich um das kleine EinMalEins kümmern will, weil einem das ja angeblich nicht hilft, geht man falsch ans Lernen ran.
Du sollst mit den Beispielen der IDE die Grundlagen lernen/verstehen. Mit diesem Grundlagenwissen kann es Dir mit der Zeit gelingen Deinen komplexen Code zu verstehen.

Gruß Tommy