bitluni/ESP32CameraI2S

Performance improvement for web server

Closed this issue · 23 comments

Hi,

I suggest writing the output to the client in one block instead of iterating.

client.write(bmpHeader, BMP::headerSize);
client.write(camera->frame, camera->xres * camera->yres * 2);

gave me a 6 frames per second instead of one frame every three seconds.

I added a pull request for these changes.

Cheers,
Derek

I use this code and the performance increase is amazing.
Thank you.

hi thanks for improving the code
can you add more explain because i don't know how to edit the part of the code that improve the preference

I use this code and the performance increase is amazing.
Thank you.

please help me to edit the program because i don't have any idea

Here you can see the pull request made by derekmulcahy. You have to make these changes.

i can't find the part in the code o that i can make the change
thank you for your attention

Here you can see the pull request made by derekmulcahy. You have to make these changes.

i can't find the part in the code o that i can make the change
thank you for your attention

The file ESP32_I2S_Camera/ESP32_I2S_Camera.ino , about line 90 . You have to found where you have installed the library.

The file ESP32_I2S_Camera/ESP32_I2S_Camera.ino , about line 90 . You have to found where you have installed the library.

i'm so stupid i guess
because i can't understand what you mean
can you make it easy for dummy me

The file ESP32_I2S_Camera/ESP32_I2S_Camera.ino , about line 90 . You have to found where you have installed the library.

i'm so stupid i guess
because i can't understand what you mean
can you make it easy for dummy me

i have done this
`#include <WiFi.h>
#include <esp_wifi.h>
#include <lwip/err.h>
#include <lwip/sockets.h>
#include "BMP.h"
#include "OV7670.h"
#include "wifi_internal.h"
WiFiClient client;

const int SIOD = SDA;
const int SIOC = SCL;

const int VSYNC = 32;
const int HREF = 39;

const int XCLK = 18;
const int PCLK = 36;

const int D0 = 12;
const int D1 = 14;
const int D2 = 27;
const int D3 = 26;
const int D4 = 25;
const int D5 = 35;
const int D6 = 34;
const int D7 = 33;

#define SSID "Test"

#define MAX_PACKET_SIZE 1460
#define MAX_DATA_SIZE 1450

struct PacketHeader {
uint8_t frame;
uint32_t offset;
uint32_t len;
};

OV7670 *camera;

byte bmpHeader[BMP::headerSize];
int udp_server = -1;
struct sockaddr_in destination;
bool video_running = false;

void setup() {
Serial.begin(115200);

WiFi.disconnect(true);

// Force data rate
wifi_internal_rate_t rate;
rate.fix_rate = RATE_MCS4_SP;
esp_wifi_internal_set_rate(100, 1, 4, &rate);

WiFi.softAP(SSID);
WiFi.onEvent(onClientChange, SYSTEM_EVENT_AP_STACONNECTED);
WiFi.onEvent(onClientChange, SYSTEM_EVENT_AP_STADISCONNECTED);

// Change beacon_interval because 100ms is crazy!
wifi_config_t conf;
esp_wifi_get_config(WIFI_IF_AP, &conf);
conf.ap.beacon_interval = 3000;
esp_wifi_set_config(WIFI_IF_AP, &conf);

camera = new OV7670(OV7670::Mode::QQVGA_RGB565, SIOD, SIOC, VSYNC, HREF, XCLK, PCLK, D0, D1, D2, D3, D4, D5, D6, D7);
BMP::construct16BitHeader(bmpHeader, camera->xres, camera->yres);

// Init socket
udp_server = socket(AF_INET, SOCK_DGRAM, 0);
destination.sin_addr.s_addr = (uint32_t)IPAddress(255,255,255,255);
destination.sin_family = AF_INET;
destination.sin_port = htons(3333);

Serial.println("Up, Waiting for clients");
}

void onClientChange(system_event_id_t event) {
// Only start sending video after a client connects to avoid flooding
// the channel when the client is attempting to connect.
video_running = WiFi.softAPgetStationNum() != 0;
if (video_running) {
Serial.println("Video started!");
} else {
Serial.println("STOPPED");
}
}

void loop() {
if (!video_running) {
client.write(bmpHeader, BMP::headerSize);
client.write(camera->frame, camera->xres * camera->yres * 2);
return;
}

camera->oneFrame();

static byte packet[MAX_PACKET_SIZE];
static uint8_t framenum = 0;
PacketHeader* header = (PacketHeader*)packet;
header->frame = framenum++;
header->len = camera->frameBytes;

for (int i = 0; i < camera->frameBytes; i += MAX_DATA_SIZE) {
bool end = i + MAX_DATA_SIZE > camera->frameBytes;
int len = end ? camera->frameBytes - i : MAX_DATA_SIZE;
header->offset = i;
memcpy(packet + sizeof(PacketHeader), camera->frame + i, len);
int sent = sendto(
udp_server,
packet, sizeof(PacketHeader) + len,
0,
(struct sockaddr*) &destination, sizeof(destination)
);
}

// FPS counter
static unsigned int frames = 0;
static unsigned long last = millis();
++frames;
unsigned long now = millis();
if (now - last > 1000) {
Serial.println(frames);
last = now;
frames = 0;
}
}`

i just need code that work well is too complected to get all this

thank you so much you for your attention to my problem
it have being compiled and no problem but did not try it yet

the code that you gave me little bit different then the one is in the link
i have a nother qeustion
should i upload only that code or all the other codes to the esp32
because he said
"Move sketch to an appropriately named subfolder"
what dose this mean

i get this error

ESP32_I2S_Camera:39:57: error: 'no' was not declared in this scope

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, 0/no reset/);

                                                     ^

Multiple libraries were found for "WiFi.h"
Used: C:\Users\pc\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1\libraries\WiFi
Not used: C:\Users\pc\Downloads\arduino-nightly\libraries\WiFi
Using library Adafruit-GFX-Library-master at version 1.3.6 in folder: C:\Users\pc\Documents\Arduino\libraries\Adafruit-GFX-Library-master
Using library Adafruit-ST7735-Library-master at version 1.2.7 in folder: C:\Users\pc\Documents\Arduino\libraries\Adafruit-ST7735-Library-master
Using library SPI at version 1.0 in folder: C:\Users\pc\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1\libraries\SPI
Using library WiFi at version 1.0 in folder: C:\Users\pc\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1\libraries\WiFi
exit status 1
'no' was not declared in this scope

i just need code that work well is too complected to get all this

#include "OV7670.h"

#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library

#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClient.h>
#include "BMP.h"

const int SIOD = 21; //SDA
const int SIOC = 22; //SCL

const int VSYNC = 34;
const int HREF = 35;

const int XCLK = 32;
const int PCLK = 33;

const int D0 = 27;
const int D1 = 17;
const int D2 = 16;
const int D3 = 15;
const int D4 = 14;
const int D5 = 13;
const int D6 = 12;
const int D7 = 4;

const int TFT_DC = 2;
const int TFT_CS = 5;
//DIN <- MOSI 23
//CLK <- SCK 18

#define ssid1 "ssid"
#define password1 "pass"
//#define ssid2 ""
//#define password2 ""

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, 0/no reset/);
OV7670 *camera;

WiFiMulti wifiMulti;
WiFiServer server(80);

unsigned char bmpHeader[BMP::headerSize];

void serve()
{
WiFiClient client = server.available();
if (client)
{
//Serial.println("New Client.");
String currentLine = "";
while (client.connected())
{
if (client.available())
{
char c = client.read();
//Serial.write(c);
if (c == '\n')
{
if (currentLine.length() == 0)
{
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
client.print(
"<style>body{margin: 0}\nimg{height: 100%; width: auto}</style>"
""
"");
client.println();
break;
}
else
{
currentLine = "";
}
}
else if (c != '\r')
{
currentLine += c;
}

    if(currentLine.endsWith("GET /camera"))
    {
        client.println("HTTP/1.1 200 OK");
        client.println("Content-type:image/bmp");
        client.println();
        
         client.write(bmpHeader, BMP::headerSize);
        client.write(camera->frame, camera->xres * camera->yres * 2);
    }
  }
}
// close the connection:
client.stop();
//Serial.println("Client Disconnected.");

}
}

void setup()
{
Serial.begin(115200);

wifiMulti.addAP(ssid1, password1);
//wifiMulti.addAP(ssid2, password2);
Serial.println("Connecting Wifi...");
if(wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}

camera = new OV7670(OV7670::Mode::QQVGA_RGB565, SIOD, SIOC, VSYNC, HREF, XCLK, PCLK, D0, D1, D2, D3, D4, D5, D6, D7);
BMP::construct16BitHeader(bmpHeader, camera->xres, camera->yres);

tft.initR(INITR_BLACKTAB);
tft.fillScreen(0);
server.begin();
}

void displayY8(unsigned char * frame, int xres, int yres)
{
tft.setAddrWindow(0, 0, yres - 1, xres - 1);
int i = 0;
for(int x = 0; x < xres; x++)
for(int y = 0; y < yres; y++)
{
i = y * xres + x;
unsigned char c = frame[i];
unsigned short r = c >> 3;
unsigned short g = c >> 2;
unsigned short b = c >> 3;
tft.pushColor(r << 11 | g << 5 | b);
}
}

void displayRGB565(unsigned char * frame, int xres, int yres)
{
tft.setAddrWindow(0, 0, yres - 1, xres - 1);
int i = 0;
for(int x = 0; x < xres; x++)
for(int y = 0; y < yres; y++)
{
i = (y * xres + x) << 1;
tft.pushColor((frame[i] | (frame[i+1] << 8)));
}
}

void loop()
{
camera->oneFrame();
serve();
displayRGB565(camera->frame, camera->xres, camera->yres);
}

please reply

i don't need to use the tft or mqtt
because basically i need to use the webserver to show date
can i remove the tft in the code dose it will work like that
because i did what he said in the video and comented them and is compiling

@malloc32 @derekmulcahy @akarsh98
can i not use the tft
because i want to access to camera jus with webserver
like this code
is compile
#include "OV7670.h"

#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library

#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClient.h>
#include "BMP.h"

const int SIOD = 21; //SDA
const int SIOC = 22; //SCL

const int VSYNC = 34;
const int HREF = 35;

const int XCLK = 32;
const int PCLK = 33;

const int D0 = 27;
const int D1 = 17;
const int D2 = 16;
const int D3 = 15;
const int D4 = 14;
const int D5 = 13;
const int D6 = 12;
const int D7 = 4;

const int TFT_DC = 2;
const int TFT_CS = 5;
//DIN <- MOSI 23
//CLK <- SCK 18

#define ssid1 "ssid"
#define password1 "pass"
//#define ssid2 ""
//#define password2 ""

//Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, 0/no reset/);
OV7670 *camera;

WiFiMulti wifiMulti;
WiFiServer server(80);

unsigned char bmpHeader[BMP::headerSize];

void serve()
{
WiFiClient client = server.available();
if (client)
{
//Serial.println("New Client.");
String currentLine = "";
while (client.connected())
{
if (client.available())
{
char c = client.read();
//Serial.write(c);
if (c == '\n')
{
if (currentLine.length() == 0)
{
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
client.print(
"<style>body{margin: 0}\nimg{height: 100%; width: auto}</style>"
""
"");
client.println();
break;
}
else
{
currentLine = "";
}
}
else if (c != '\r')
{
currentLine += c;
}

if(currentLine.endsWith("GET /camera"))
{
    client.println("HTTP/1.1 200 OK");
    client.println("Content-type:image/bmp");
    client.println();
    
     client.write(bmpHeader, BMP::headerSize);
    client.write(camera->frame, camera->xres * camera->yres * 2);
}

}
}
// close the connection:
client.stop();
//Serial.println("Client Disconnected.");
}
}

void setup()
{
Serial.begin(115200);

wifiMulti.addAP(ssid1, password1);
//wifiMulti.addAP(ssid2, password2);
Serial.println("Connecting Wifi...");
if(wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}

camera = new OV7670(OV7670::Mode::QQVGA_RGB565, SIOD, SIOC, VSYNC, HREF, XCLK, PCLK, D0, D1, D2, D3, D4, D5, D6, D7);
BMP::construct16BitHeader(bmpHeader, camera->xres, camera->yres);

//tft.initR(INITR_BLACKTAB);
//tft.fillScreen(0);
server.begin();
}

void displayY8(unsigned char * frame, int xres, int yres)
{
//tft.setAddrWindow(0, 0, yres - 1, xres - 1);
int i = 0;
for(int x = 0; x < xres; x++)
for(int y = 0; y < yres; y++)
{
i = y * xres + x;
unsigned char c = frame[i];
unsigned short r = c >> 3;
unsigned short g = c >> 2;
unsigned short b = c >> 3;
//tft.pushColor(r << 11 | g << 5 | b);
}
}

void displayRGB565(unsigned char * frame, int xres, int yres)
{
//tft.setAddrWindow(0, 0, yres - 1, xres - 1);
int i = 0;
for(int x = 0; x < xres; x++)
for(int y = 0; y < yres; y++)
{
i = (y * xres + x) << 1;
//tft.pushColor((frame[i] | (frame[i+1] << 8)));
}
}

void loop()
{
camera->oneFrame();
serve();
displayRGB565(camera->frame, camera->xres, camera->yres);
}

ok, then now it is working?, Some time ago I leave to program microcontrollers, no time :(.

i did not try it yet the esp32 will arive tomorrow
the i have edited on it is that okay because i don't want to use tft basically

i have compared the code you gave me and the original code
i found that you are missing somethings
and i have added it now it have compiled with not much change

just he one you have gave me
thank you all for the help and attention

`#include "OV7670.h"

#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library

#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClient.h>
#include "BMP.h"

const int SIOD = 21; //SDA
const int SIOC = 22; //SCL

const int VSYNC = 34;
const int HREF = 35;

const int XCLK = 32;
const int PCLK = 33;

const int D0 = 27;
const int D1 = 17;
const int D2 = 16;
const int D3 = 15;
const int D4 = 14;
const int D5 = 13;
const int D6 = 12;
const int D7 = 4;

const int TFT_DC = 2;
const int TFT_CS = 5;
//DIN <- MOSI 23
//CLK <- SCK 18

#define ssid1 "YOUR_WIFI_SSID"
#define password1 "YOUR_PASSWORD"
//#define ssid2 ""
//#define password2 ""

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, 0/no reset/);
OV7670 *camera;

WiFiMulti wifiMulti;
WiFiServer server(80);

unsigned char bmpHeader[BMP::headerSize];

void serve()
{
WiFiClient client = server.available();
if (client)
{
//Serial.println("New Client.");
String currentLine = "";
while (client.connected())
{
if (client.available())
{
char c = client.read();
//Serial.write(c);
if (c == '\n')
{
if (currentLine.length() == 0)
{
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
client.print(
"<style>body{margin: 0}\nimg{height: 100%; width: auto}</style>"
""
"");
client.println();
break;
}
else
{
currentLine = "";
}
}
else if (c != '\r')
{
currentLine += c;
}

    if(currentLine.endsWith("GET /camera"))
    {
        client.println("HTTP/1.1 200 OK");
        client.println("Content-type:image/bmp");
        client.println();
        
        client.write(bmpHeader, BMP::headerSize);
        client.write(camera->frame, camera->xres * camera->yres * 2);
    }
  }
}
// close the connection:
client.stop();
//Serial.println("Client Disconnected.");

}
}

void setup()
{
Serial.begin(115200);

wifiMulti.addAP(ssid1, password1);
//wifiMulti.addAP(ssid2, password2);
Serial.println("Connecting Wifi...");
if(wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}

camera = new OV7670(OV7670::Mode::QQVGA_RGB565, SIOD, SIOC, VSYNC, HREF, XCLK, PCLK, D0, D1, D2, D3, D4, D5, D6, D7);
BMP::construct16BitHeader(bmpHeader, camera->xres, camera->yres);

tft.initR(INITR_BLACKTAB);
tft.fillScreen(0);
server.begin();
}

void displayY8(unsigned char * frame, int xres, int yres)
{
tft.setAddrWindow(0, 0, yres - 1, xres - 1);
int i = 0;
for(int x = 0; x < xres; x++)
for(int y = 0; y < yres; y++)
{
i = y * xres + x;
unsigned char c = frame[i];
unsigned short r = c >> 3;
unsigned short g = c >> 2;
unsigned short b = c >> 3;
tft.pushColor(r << 11 | g << 5 | b);
}
}

void displayRGB565(unsigned char * frame, int xres, int yres)
{
tft.setAddrWindow(0, 0, yres - 1, xres - 1);
int i = 0;
for(int x = 0; x < xres; x++)
for(int y = 0; y < yres; y++)
{
i = (y * xres + x) << 1;
tft.pushColor((frame[i] | (frame[i+1] << 8)));
}
}

void loop()
{
camera->oneFrame();
serve();
displayRGB565(camera->frame, camera->xres, camera->yres);
}`

the code now worked but there is a problem the image dose not give me good colors
and i have changed pin 17 with pin 25 because i can't find pin 17
do yo have any ideas

ufff, sorry It was many time ago, even now I have not the schema..., but I remember that I had good colors, not very good image quality because the resolution.. and the price of the camera... about 2€ in aliexpress, so....

the colors i got is not good at all i berly can see my face
i have esp32 wroom and it dose not have pin 17
how can i change the pin with no problem
there the esp i have
51835731_804088226613058_8543762027120689152_n
and i want to know how the chnage the screen size so i can see it full using the phone

i have 17 pin is tx2 but now when i changed the router it dose not show the ip addressee of the esp in serial monitor

@derekmulcahy @malloc32
i need to add code led control in the camera code
any help?

did this code but i need to optimized it
#include "OV7670.h"

#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library

#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClient.h>
#include "BMP.h"

String header;

// Auxiliar variables to store the current output state
String output22State = "off";

// Assign output variables to GPIO pins
const int D25 = 25;
String output25State = "off";
const int SIOD = 21; //SDA
const int SIOC = 22; //SCL

const int VSYNC = 34;
const int HREF = 35;

const int XCLK = 32;
const int PCLK = 33;

const int D0 = 27;
const int D1 = 17;
const int D2 = 16;
const int D3 = 15;
const int D4 = 14;
const int D5 = 13;
const int D6 = 12;
const int D7 = 4;

const int TFT_DC = 2;
const int TFT_CS = 5;
//DIN <- MOSI 23
//CLK <- SCK 18

#define ssid1 "YOUR_WIFI_SSID"
#define password1 "YOUR_PASSWORD"
//#define ssid2 ""
//#define password2 ""

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, 0/no reset/);
OV7670 *camera;

WiFiMulti wifiMulti;
WiFiServer server(80);

unsigned char bmpHeader[BMP::headerSize];

void serve()
{
WiFiClient client = server.available();
if (client)
{
//Serial.println("New Client.");
String currentLine = "";
while (client.connected())
{
if (client.available())
{
char c = client.read();
//Serial.write(c);
if (c == '\n')
{
if (currentLine.length() == 0)
{
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
client.print(
"<style>body{margin: 0}\nimg{height: 100%; width: auto}</style>"
""
"");
client.println();
break;
}
else
{
currentLine = "";
}
}
else if (c != '\r')
{
currentLine += c;
}

    if(currentLine.endsWith("GET /camera"))
    {
        client.println("HTTP/1.1 200 OK");
        client.println("Content-type:image/bmp");
        client.println();
        
        client.write(bmpHeader, BMP::headerSize);
        client.write(camera->frame, camera->xres * camera->yres * 2);
    }
  }
}
// close the connection:
client.stop();
//Serial.println("Client Disconnected.");

}
}

void setup()
{
Serial.begin(115200);

pinMode(D25, OUTPUT);
// Set outputs to LOW
digitalWrite(D25, LOW);

wifiMulti.addAP(ssid1, password1);
//wifiMulti.addAP(ssid2, password2);
Serial.println("Connecting Wifi...");
if(wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}

camera = new OV7670(OV7670::Mode::QQVGA_RGB565, SIOD, SIOC, VSYNC, HREF, XCLK, PCLK, D0, D1, D2, D3, D4, D5, D6, D7);
BMP::construct16BitHeader(bmpHeader, camera->xres, camera->yres);

tft.initR(INITR_BLACKTAB);
tft.fillScreen(0);
server.begin();
}

void displayY8(unsigned char * frame, int xres, int yres)
{
tft.setAddrWindow(0, 0, yres - 1, xres - 1);
int i = 0;
for(int x = 0; x < xres; x++)
for(int y = 0; y < yres; y++)
{
i = y * xres + x;
unsigned char c = frame[i];
unsigned short r = c >> 3;
unsigned short g = c >> 2;
unsigned short b = c >> 3;
tft.pushColor(r << 11 | g << 5 | b);
}
}

void displayRGB565(unsigned char * frame, int xres, int yres)
{
tft.setAddrWindow(0, 0, yres - 1, xres - 1);
int i = 0;
for(int x = 0; x < xres; x++)
for(int y = 0; y < yres; y++)
{
i = (y * xres + x) << 1;
tft.pushColor((frame[i] | (frame[i+1] << 8)));
}
}

void loop()

{
camera->oneFrame();
serve();
displayRGB565(camera->frame, camera->xres, camera->yres);
{
WiFiClient client = server.available();
if (client)
{
//Serial.println("New Client.");
String currentLine = "";
while (client.connected())
{
if (client.available())
{
char c = client.read();
//Serial.write(c);
if (c == '\n')
{
if (currentLine.length() == 0)
{
if (header.indexOf("GET /26/on") >= 0) {
Serial.println("GPIO 26 on");
output25State = "on";
digitalWrite(D25, HIGH);
delay(7000);
digitalWrite(D25, LOW);

          Serial.println("GPIO 25 off");
          output25State = "off";
          digitalWrite(D25, LOW);
        
        }
        
        // Display the HTML web page
        client.println("<!DOCTYPE html><html>");
        client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
        client.println("<link rel=\"icon\" href=\"data:,\">");
        // CSS to style the on/off buttons 
        // Feel free to change the background-color and font-size attributes to fit your preferences
        client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
        client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
        client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
        client.println(".button2 {background-color: #555555;}</style></head>");
        
        // Web Page Heading
        client.println("<body><h1>ESP32 Web Server</h1>");
        
        // Display current state, and ON/OFF buttons for D25  
        client.println("<p>GPIO 25 - State " + output25State + "</p>");
        // If the output26State is off, it displays the ON button       
        if (output25State=="off") {
          client.println("<p><a href=\"/25/on\"><button class=\"button\">ON</button></a></p>");
        } else {
          client.println("<p><a href=\"/25/off\"><button class=\"button button2\">OFF</button></a></p>");
        }     
         client.println("</body></html>");
        
        // The HTTP response ends with another blank line
        client.println();
        // Break out of the while loop
        break;
      } else { // if you got a newline, then clear currentLine
        currentLine = "";
      }
    } else if (c != '\r') {  // if you got anything else but a carriage return character,
      currentLine += c;      // add it to the end of the currentLine
    }
  }
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");

}
}
}

Thanks for the merge!