Wiring W5500 Ethernet Shield to ESP8266

Good day, everyone!

I'm reaching out again for another problem of mine with my contraption. See, it's been almost 6 months since I made a machine which dispenses Internet voucher codes when coin is inserted. Its been doing fine but I did encounter a consistent hiccup with it, the wifi. The ESP8266 almost always loses wifi connection and so it can't connect to my Firebase database which stores the voucher codes. And users complain about this alot. I already tried placing a wifi extender next to it but it still loses connection one way or another. Now I thought of using an ethernet shield, W5500 Lite (like the one below:)

My problem now is thinking of a way how to wire this to my existing machine when almost all the pins on the right side are already in use. Only D7 (GPIO13) remains.

I already scoured the internet but couldn't find the correct solution. I respectfully hope you can give me an insight to my predicament and respond to my post, thank you in advance!

Best regards,
Billy

you need all spi pins

maybe simpler to move to a WT32-ETH01 Ethernet Module ?

or add a Lantronix Xport module to the ESP8266

I think you still should try to resolve the wifi issue. Especially since you are selling wifi connections...

Totally, You must be doing something to disturb the WiFi connection. Anything that might be turning off interrupts ?
Without the code, we are of course just guessing.

Yes D7, D8 & D9 + a CS pin.

A clear case where a Fritzing diagram, just does not suffice. Please post an actual schematic

Sorr about that, here's my complete code for your reference. Sorry for the long read, thanks!

#include <Adafruit_Thermal.h>
#include <SoftwareSerial.h>
#include <LiquidCrystal_I2C.h>
#include <FirebaseESP8266.h>
#include "vglogoII.h"
#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"

//Network
#include "ESP8266WiFi.h"

#include "NTPClient.h"
#include "WiFiUdp.h"

//Firebase
#define DB_URL "https://vg-wifi-kerberos-default-rtdb.asia-southeast1.firebasedatabase.app/"
#define FB_API_KEY "AIzaSyCIUI08MowggqoiRO8eh7PXGXTB_XB0SqM"

//Firebase and Database usage
FirebaseData fbd;
QueryFilter queryFilter;
FirebaseJson* fbJson;
FirebaseAuth fbauth;
FirebaseConfig fbconfig;
String fbCode = "";

//Wifi, Date & Time
const char* ssid = "wifiname";
const char* password = "wifipassword";
WiFiUDP ntpUDP;
const long utcOffsetInSeconds = 28800;
NTPClient timeClient(ntpUDP, "time.nist.gov", utcOffsetInSeconds);

//Pin assignment
const int BTN_LED_PIN = 0;
const int COIN_PIN = 2;
const int BUTTON_PIN = 3;
const int BUZZ_PIN = 12;
const int COIN_SET_PIN = 15;
const int TX_PIN = 14; //Green Wire
const int RX_PIN = 16; //Yellow Wire

LiquidCrystal_I2C lcd(0x27, 16, 2);
SoftwareSerial mySerial(RX_PIN, TX_PIN);
Adafruit_Thermal printer(&mySerial);

//Button variables
volatile int btnState = 0;
volatile bool btnPressed = false;
volatile bool btnDisabled = false;

//Countdown variables
const long interval = 1000;
unsigned long previousMillis = 0;
volatile int countdown = 21;
int printCountdown = 6;
bool countdownStarted = false;
bool printCountdownStarted = false;

//Ethernet variables
String currentMacAddress = "";
String currentIpAddress = "";

//Other variables
volatile int totalAmount = 0;
int resetCounter = 20;
volatile int lcdState = 1;

//Execute-once Boolean lcd clearing variables
bool tier0Clear = true;
bool tierIClear = true;
bool tierIIClear = true;
bool tierIVClear = true;
bool tierVClear = true;
bool tierVIClear = true;
bool firstEntry = true;

void IRAM_ATTR coinInterrupt() {
  if(btnState == 1) {
    countdown = 21;
    totalAmount++;
    lcdState = 2;
  }
}

void IRAM_ATTR btnInterrupt() {
  static unsigned long lastExec = 0;
  unsigned long currentExec = millis();
  if(currentExec - lastExec > 500 && !btnDisabled) {
    btnPressed = true;
    btnDisabled = true;
    btnState++;
  }
  lastExec = currentExec;
}

void connectWifi() {
  if(WiFi.status() != WL_CONNECTED) {  // Connect to Wi-Fi network with SSID and password
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
      lcd.setCursor(0,0);
      lcd.print("Connecting Wifi");
      delay(600);
      lcd.clear();
    }
    WiFi.setAutoReconnect(true);
    WiFi.persistent(true);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
}

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  pinMode(BTN_LED_PIN, OUTPUT);
  pinMode(COIN_PIN, INPUT_PULLUP);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(COIN_SET_PIN, OUTPUT);
  pinMode(BUZZ_PIN, OUTPUT);
  digitalWrite(BTN_LED_PIN, LOW);
  digitalWrite(COIN_SET_PIN, LOW);
  digitalWrite(BUZZ_PIN, LOW);
  attachInterrupt(digitalPinToInterrupt(COIN_PIN), coinInterrupt, RISING);
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), btnInterrupt, FALLING);
  lcd.init();
  lcd.clear();
  lcd.backlight();
  printer.begin();
  delay(1000L);
  connectWifi();
  timeClient.begin();
  fbconfig.api_key = FB_API_KEY;
  fbconfig.database_url = DB_URL;
  if(Firebase.signUp(&fbconfig, &fbauth, "", "")){
    Serial.println("Firebase signup ok");
    Firebase.begin(&fbconfig, &fbauth);
    sendLog(getDate(), getDate() +"-" +getTime() +": Firebase sign up ok;");
    buzz();
  }
  else{
    Serial.printf("%s\n", fbconfig.signer.signupError.message.c_str());
    sendLog(getDate(), getDate() +"-" +getTime() +": Firebase error; " +fbconfig.signer.signupError.message.c_str());
  }
  fbconfig.token_status_callback = tokenStatusCallback;
  Firebase.begin(&fbconfig, &fbauth);
  Firebase.reconnectWiFi(true);
  sendLog(getDate(), getDate() +"-" +getTime() +": System boot up;");
}

void loop() {
  unsigned long currentMillis = millis();
  // if WiFi is down, try reconnecting
  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >= 180000)) {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.begin(ssid, password);
    previousMillis = currentMillis;
  }
  switch(lcdState) {
    case 1: //Home state
      lcd.setCursor(1, 0);
      lcd.print("VGWifi Hotspot");
      lcd.setCursor(2, 1);
      lcd.print("                ");
      lcd.setCursor(2, 1);
      lcd.print("Press button");
      digitalWrite(BTN_LED_PIN, HIGH);
      delay(500);
      lcd.setCursor(2, 1);
      lcd.print("                ");
      digitalWrite(BTN_LED_PIN, LOW);
      delay(500);
      break;
    case 2: //Counter state
      if(totalAmount <= 0 && firstEntry) {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Insert coins now");
        firstEntry = false;
      }
      lcd.setCursor(0, 0);
      if(totalAmount > 0 && totalAmount < 5) {
        if(tier0Clear) {
          lcd.setCursor(0,0);
          lcd.print("                ");
          tier0Clear = false;
        }
        lcd.setCursor(0,0);
        lcd.print("0 MINS");
      } else if(totalAmount >= 5 && totalAmount < 10) {
        if(tierIClear) {
          lcd.setCursor(0,0);
          lcd.print("                ");
          tierIClear = false;
        }
        lcd.setCursor(0,0);
        lcd.print("30 MINS");
      } else if(totalAmount >= 10 && totalAmount < 15) {
        if(tierIIClear) {
          lcd.print("        ");
          tierIIClear = false;
        }
        lcd.setCursor(0,0);
        lcd.print("1 HOUR");
      } else if(totalAmount >= 15 && totalAmount < 25) {
        lcd.print("3 HOURS");
      } else if(totalAmount >= 25 && totalAmount < 35) {
        if(tierIVClear) {
          lcd.print("        ");
          tierIVClear = false;
        }
        lcd.setCursor(0,0);
        lcd.print("1 DAY");
      } else if(totalAmount >= 35 && totalAmount <50) {
        if(tierVClear) {
          lcd.print("        ");
          tierVClear = false;
        }
        lcd.setCursor(0,0);
        lcd.print("3 DAYS");
      } else if(totalAmount >= 50) {
        if(tierVIClear) {
          lcd.print("        ");
          tierVIClear = false;
        }
        lcd.setCursor(0,0);
        lcd.print("7 DAYS");
      }
      lcd.setCursor(0, 1);
      if(totalAmount > -1) {
        lcd.print("P");
        lcd.print(totalAmount);
        lcd.print(".00");
      }
      break;
  }
  if(btnPressed) {
    switch(btnState) {
      case 1:
        enableCoinSlot();
        btnPressed = false;
        countdownStarted = true;
        lcdState = 2;
        digitalWrite(BTN_LED_PIN, LOW);
        break;
      case 2:
        printCountdown = 6;
        printCountdownStarted = false;
        finishTransaction();
        break;
    }
  }
  //Countdown for insert coins
  if(countdownStarted && (currentMillis - previousMillis >= interval)) {
    previousMillis = currentMillis;
    countdown--;
    if(countdown < 10) {
      lcd.setCursor(15, 1);
      lcd.print(" ");
    }
    lcd.setCursor(11, 1);  // Set cursor position
    lcd.print("(");
    lcd.print(countdown);
    lcd.print("s)");
  }
  if(printCountdownStarted && (currentMillis - previousMillis >= interval)) {
    previousMillis = currentMillis;
    printCountdown--;
    lcd.setCursor(15, 0);
    lcd.print(printCountdown);
  }
  if(countdown <= 0) {
    if(totalAmount >= 5) {
      displayPrintPrompt();
    } else {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Not enough coins");
      lcd.setCursor(0, 1);
      lcd.print("Minimum is P5.00");
      disableCoinSlot();
      delay(3000);
      lcdState = 1;
      if(totalAmount > 0) {
        finishTransaction();
      }
      lcd.clear();
      totalAmount = 0;
      btnState = 0;
      btnDisabled = false;
    }
    countdown = 21;
    countdownStarted = false;
  }
  if(printCountdown <= 0) {
    finishTransaction();
    printCountdown = 6;
    printCountdownStarted = false;
  }
}

String getDate() {
  timeClient.update();
  time_t epochTime = timeClient.getEpochTime();
  struct tm *ptm = gmtime ((time_t *)&epochTime);
  int currentMonth = ptm->tm_mon+1;
  int monthDay = ptm->tm_mday;
  int currentYear = ptm->tm_year+1900;
  String date = String(currentMonth) +"-" +monthDay +"-" +currentYear;
  return date;
}

String getTime() {
  timeClient.update();
  return String(timeClient.getFormattedTime());
}

void displayPrintPrompt() {
  printCountdownStarted = true;
  lcdState = 3;
  btnDisabled = false;
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Print voucher?");
  lcd.setCursor(0, 1);
  lcd.print("Press button");
  disableCoinSlot();
  digitalWrite(BTN_LED_PIN, HIGH);
}

void enableCoinSlot() {
  digitalWrite(COIN_SET_PIN, HIGH);
}

void disableCoinSlot() {
  digitalWrite(COIN_SET_PIN, LOW);
}

void finishTransaction() {
  disableCoinSlot();
  getVoucher(totalAmount);
  btnPressed = false;
  digitalWrite(BTN_LED_PIN, LOW);
  delay(5000);
  buzz();
  lcd.clear();
  btnDisabled = false;
  btnState = 0;
  lcdState = 1;
  totalAmount = 0;
  resetCounter--;
  if(resetCounter == 0) {
    sendLog(getDate(), getDate() +"-" +getTime() +": System reboot;");
    ESP.reset();
  }
}

void getVoucher(int amt) {
  String date = getDate();
  String time = getTime();
  String desc;
  String valid;
  String fbdpath;
  String code;
  String log;
  int change;
  int tier;
  if(amt > 0 && amt < 5) {
    tier = 0;
    code = "-";
  } else {
    if(amt >= 5 && amt < 10) {
      valid = "30 MINS";
      tier = 5;
      fbdpath = "/P05V/";
    } else if(amt >= 10 && amt < 15) {
      valid = "1 HOUR";
      tier = 10;
      fbdpath = "/P10V/";
    } else if(amt >= 15 && amt < 25) {
      valid = "3 HOURS";
      tier = 15;
      fbdpath = "/P15V/";
    } else if(amt >= 25 && amt < 35) {
      valid = "1 DAY";
      tier = 25;
      fbdpath = "/P25V/";
    } else if(amt >= 35 && amt <50) {
      valid = "3 DAYS";
      tier = 35;
      fbdpath = "/P35V/";
    } else if(amt >= 50) {
      valid = "7 DAYS";
      tier = 50;
      fbdpath = "/P50V/";
    }
    lcd.clear();
    lcd.setCursor(3, 0);
    lcd.print("Generating");
    lcd.setCursor(3, 1);
    lcd.print("voucher...");
    if(Firebase.ready()) {
      queryFilter.orderBy("$key");
      queryFilter.limitToLast(1);
      if(Firebase.getJSON(fbd, fbdpath, queryFilter)) {
        fbJson = fbd.to<FirebaseJson *>();
        size_t length = fbJson->iteratorBegin();
        size_t i = 0;
        FirebaseJson::IteratorValue element;
        element = fbJson->valueAt(i);
        code = element.value.c_str();
        code.replace('"', ' ');
        Serial.println("Key: " +String(element.key.c_str()));
        Serial.println("Value: "+String(element.value.c_str()));
        fbJson->iteratorEnd();
        fbJson->clear();
        Serial.printf("Delete element from db: %s\n", Firebase.deleteNode(fbd, fbdpath + element.key.c_str()) ? "ok" : fbd.errorReason().c_str());
      } 
    }
  }
  change = amt-tier;
  if(change > 0) {
    String chg = String(change);
    if(tier == 0) {
      desc = "Get your P" +chg +".00 back";
      log = date +"-" +time +": User inserted insufficient amount of coins. Return P" +chg +".00.";
    } else {
      desc = "Your change is P" +chg +".00";
      log = date +"-" +time +": Purchase of P" +String(tier) +".00 voucher with code: " +code +" completed; User's change is P" +chg +".00.";
    }
  } else {
    log = date +"-" +time +": Purchase of P" +String(tier) +".00 voucher with code: " +code +" completed;";
  }
  lcd.clear();
  lcd.print(valid);
  lcd.print(" Voucher:");
  lcd.setCursor(4, 1);
  lcd.print(code);
  printer.wake();
  printVoucher(code, tier, valid, desc, date, time);
  sendLog(date, log);
  firstEntry = true;
}

void printVoucher(String code, int amount, String valid, String change, String date, String time) {
  printer.wake();
  String tier = " (P" +String(amount) +".00)";
  printer.wake();
  printer.justify('C');
  printer.setFont('C');
    //printer.setSize('M');
  //printer.println("VG Wifi");
  printer.setSize('S');
  printer.println("");
  printer.println("Voucher Code:");
  printer.setSize('L');
  printer.println(code);
  printer.setSize('S');
  printer.println("");
  printer.setSize('M');
  printer.print(valid);
  printer.println(tier);
  printer.boldOff();
  printer.setSize('S');
  printer.println(change);
  printer.println(date);
  printer.print(time);
  printer.println(F("\n"));
  printer.println(F("\n"));
  printer.setDefault();
  tier0Clear = true;
  tierIClear = true;
  tierIIClear = true;
  tierIVClear = true;
  tierVClear = true;
  tierVIClear = true;
}

void sendLog(String date, String log) {
  String logPath = "/logs/" +date;
  Serial.printf("Send log: %s\n", Firebase.pushString(fbd, logPath, log) ? "ok" : fbd.errorReason().c_str());
}

void buzz() {
  digitalWrite(BUZZ_PIN, HIGH);
  delay(1000);
  digitalWrite(BUZZ_PIN, LOW);
}

The wifi that users use is thrown by completely different network/system (Omada). I only use the ESP8266's wifi exclusively for fetching voucher codes from my Firebase realtime database and the network it is connected to is also from a different router :slight_smile:

Thank you for the code. So the first thing i see is the use of swSerial. If i am not mistaken (i never use swSerial on an ESP) you are supposed to use EspSoftwareSerial on an ESP.

That said, i am not convince you need to use swSerial at all. You use it for a printer, but only use the swSerial TX pin and the RX pin is unconnected.

Outside of the possibility of just using the default UART0 TX & RX pins (GPIO 1 & 3) which would free up 2 pins, you could also use UART1 (Serial1) on GPIO 2 which only works for TX, but that is all you need. That can get rid of swSerial all together.

swSerial, the way you use it is probably the cause of you using the WiFi connection, and EspSoftwareSerial will probably fix that already, but switching to the use of hwSerial will be even better.
You can use Serial and still use the RX-pin (3) as a GPIO if you call
Serial.begin() first and pinMode(3, INPUT); afterwards
Now you have pins 13, 14 & 16 free. You can move the BUZZ_PIN to 16, now all you need is a CS pin, and to be fair, you may need to be able to reset the W5500, though that could be a shared pin with the COIN_SET_PIN. You could consider using the analog pin for the button, but that is not an interrupt pin. Still if you 'poll' on that pin, it will suffice for a button. Keep in mind that the input level is between 0-1v i think.
Or combine the RST & CS on the W5500. It needs to be enabled for which it should be LOW, but you never share the SPI bus, so it is nearly a constant state.
Best would be to experiment first on how you could best achieve this with the minimum of pin use, maybe with a logic level inverter or something.