I have implemented websocket++ client in x86 its working fine but in arm arch when ever I call the connect it crashes when there is no server or internet available,but works well when internet or server available.
Code snippet added here connect call is crashing
con = m_EndPoint.connect(con);
CWebSocketEndpoint(CWebSocketPlusPlus* uopCWebSocketPlusPlus) : m_next_id(0)
{
muopCWebSocketPlusPlus = uopCWebSocketPlusPlus;
m_EndPoint.clear_access_channels(websocketpp::log::alevel::all);
m_EndPoint.clear_error_channels(websocketpp::log::elevel::all);
m_EndPoint.init_asio();
m_EndPoint.start_perpetual();
// mThread.reset(new websocketpp::lib::thread(&client::run, &m_EndPoint));
mThread = websocketpp::lib::make_shared<websocketpp::lib::thread>(&client::run, &m_EndPoint);}
int connect(std::string const & uri)
{
websocketpp::lib::error_code ec;
client::connection_ptr con = m_EndPoint.get_connection(uri, ec);
if (ec)
{
std::cout << "> Connect initialization error: " << ec.message() << std::endl;
return -1;
}
int new_id = m_next_id++;
CWebSocketConnectionMetaData::ptr metadata_ptr(new CWebSocketConnectionMetaData(new_id, con->get_handle(), uri, muopCWebSocketPlusPlus));
m_connection_list[new_id] = metadata_ptr;
con->set_open_handler(websocketpp::lib::bind(
&CWebSocketConnectionMetaData::on_open,
metadata_ptr,
&m_EndPoint,
websocketpp::lib::placeholders::_1
));
con->set_fail_handler(websocketpp::lib::bind(
&CWebSocketConnectionMetaData::on_fail,
metadata_ptr,
&m_EndPoint,
websocketpp::lib::placeholders::_1
));
con->set_close_handler(websocketpp::lib::bind(
&CWebSocketConnectionMetaData::on_close,
metadata_ptr,
&m_EndPoint,
websocketpp::lib::placeholders::_1
));
con->set_message_handler(websocketpp::lib::bind(
&CWebSocketConnectionMetaData::on_message,
metadata_ptr,
websocketpp::lib::placeholders::_1,
websocketpp::lib::placeholders::_2
));
con = m_EndPoint.connect(con);
return new_id;
}
}
Related
I am trying to download my hex file of size 1500KB via UDS with CAPL test module,
p2 timer = 50ms
p2* timer = 5000ms
Here is snippet of my code for data transfer :
void TS_transferData()
{
byte transferData_serviceid = 0x36;
byte blockSequenceCounter = 0x1;
byte buffer[4093];
byte binarydata[4095];
long i,ret1,ret2,ret3,temp,timeout = 0,Counter = 0;
char filename[30] = "xxx.bin";
dword readaccess_handle;
diagrequest ECU_QUALIFIER.* request;
long valueleft;
readaccess_handle = OpenFileRead(filename,1);
if (readaccess_handle != 0 )
{
while( (valueleft = fileGetBinaryBlock(buffer,elcount(buffer),readaccess_handle))==4093 )
{
binarydata[0] = transferData_serviceid;
binarydata[1] = blockSequenceCounter;
for(i=0;i<elcount(buffer);i++)
{
binarydata[i+2] = buffer[i];
}
diagResize(request, elCount(binarydata));
DiagSetPrimitiveData(request,binarydata,elcount(binarydata));
DiagSendRequest(request);
write("length of binarydata %d ",elcount(binarydata));
// Wait until the request has been completely sent
ret1 = TestWaitForDiagRequestSent(request, 20000);
if(ret1 == 1) // Request sent
{
ret2=TestWaitForDiagResponse(request,50);
if(ret2==1) // Response received
{
ret3=DiagGetLastResponseCode(request); // Get the code of the response
if(ret3==-1) // Is it a positive response?
{
;
}
else
{
testStepFail(0, "4.0","Binary Datatransfer on server Failed");
break;
}
}
else if(ret2 == timeout)
{
testStepFail(0, "4.0","Binary Datatransfer on server Failed");
write("timeout occured while TestWaitForDiagResponse with block %d ",blockSequenceCounter);
}
}
else if(ret1 == timeout)
{
testStepFail(0, "4.0","Binary Datatransfer on server Failed");
write("timeout occured while TestWaitForDiagRequestSent %d ",blockSequenceCounter);
}
if(blockSequenceCounter == 255)
blockSequenceCounter = 0;
else
++blockSequenceCounter;
}
}
//handle the rest of the bytes to be transmitted
fileClose (readaccess_handle);
}
The software downloading is happening but it is taking a long.... time for download.
For TestWaitForDiagRequestSent() function any value for timeout less than 20000 is giving me timeout error.
Is there any other way I can reduce the software transfer time or where am I going wrong with calculation?
Is there any example I can refer to see How to transmit such a long data using CAPL ?
Sorry, I am a beginner to CAPL and UDS protocol.
I'm trying to post a (large) json file that's stored in the SPIFFS (LittleFS- of an arduino.
I first of all, fetch the JSON from a local IP address, which can change or is dynamic. Then, that json file gets stored in the internal flash of my ESP32 and sent over to our servers.
The goal is to have an ESP32 act as a bridge for local addresses.
For example:
Locally, there is a server running, that server is never accessible from outside of a network, but it is from within. We place an ESP32 in that network, to be able to read out data from the internal device and pass them through to our servers, for further processing.
The current code I have is the following;
HTTPClient http;
if (LittleFS.exists("/downloadedFile.json")) {
LittleFS.remove("/downloadedFile.json");
}
File f = LittleFS.open("/downloadedFile.json", "w", true);
if (f) {
String url;
// Build the URL...
if (username != "" && password != "") {
url = "http://" + username + ":" + password + "#" + ip + ":" + String(port) + "/data/file.json";
} else {
url = "http://" + ip + ":" + String(port) + "/data/file.json";
}
http.begin(url);
int httpCode = http.GET();
if (httpCode > 0) {
if (httpCode == HTTP_CODE_OK) {
http.writeToStream(&f);
}
} else {
// failed.
}
f.close();
} else {
// Unable to open file
}
http.end();
if (LittleFS.exists("/downloadedFile.json")) {
File f = LittleFS.open("/downloadedFile.json", "r");
if (f) {
WiFiClienSecure client;
client.setInsecure(); // test
if (!client.connect("my.somain.com", 443)) {
// failed to connect. (never gets in here)
return;
}
client.println("POST /path/to/my/endpoint HTTP/1.1");
client.println("Host: my.somain.com");
client.println("Content-Type: application/json");
client.println("Connection: keep-alive");
client.println("Content-Length: " + String(f.size()));
client.println();
// Build the body
int len = 0;
int total = 0;
uint8_t buf[1025] = {};
size_t _len = 256;
do {
len = f.read(buf, _len);
client.write(buf, len);
total += len;
} while (len > 0);
client.println(); // end line
client.stop();
f.close();
}
}
Downloading the file always works, except for sometimes random crashing the whole system. But that's the least of my concerns.
Locally I got it working to download and re-upload the file. Struggle is when I try to achieve the same result on the server (which uses https)
Only difference is this;
Server
Local
WiFiClienSecure client;
WiFiClient client;
client.setInsecure();
client.connect("my.somain.com", 443)
client.connect("IP", 80)
The main problem is that it doesn't work on the server and that it works really unstable. the file size (when I do f.size()) is 310831.
Any help and thoughts on getting this stable is appriciated!
I have been modifying AODV protocol to add extra information in the data packet.GPSR option is a straightforward example so I created a class inherits from TlvOptionBase , set my variables & inserted it in the ipv4 header as mentioned in GPSR protocol.
Why can TlvOption cannot be found in the datagram
Here's a part of my code:
void Aodv::setCBFOptionOnNetworkDatagram(Packet *packet, const Ptr<const NetworkHeaderBase>& networkHeader, CBFOption *cbfOption)
{
packet->trimFront();
if (dynamicPtrCast<const Ipv4Header>(networkHeader)) {
auto ipv4Header = removeNetworkProtocolHeader<Ipv4Header>(packet);
cbfOption->setType(IPOPTION_TLV_GPSR);
B oldHlen = ipv4Header->calculateHeaderByteLength();
EV_INFO << "old header length "<<oldHlen << endl;
ASSERT(ipv4Header->getHeaderLength() == oldHlen);
ipv4Header->addOption(cbfOption);
B newHlen = ipv4Header->calculateHeaderByteLength();
EV_INFO << "new header length "<<newHlen << endl;
ipv4Header->setHeaderLength(newHlen);
ipv4Header->addChunkLength(newHlen - oldHlen);
ipv4Header->setTotalLengthField(ipv4Header->getTotalLengthField() + newHlen - oldHlen);
insertNetworkProtocolHeader(packet, Protocol::ipv4, ipv4Header);
}
}
const CBFOption *Aodv::getCBFOptionFromNetworkDatagram(const Ptr<const NetworkHeaderBase>& networkHeader) const
{
const CBFOption *cbfOption = findCBFOptionInNetworkDatagram(networkHeader);
if (cbfOption == nullptr)
throw cRuntimeError("Tlvoption not found in datagram");
return cbfOption;
}
CBFOption *Aodv::findCBfOptionInNetworkDatagramForUpdate(const Ptr<NetworkHeaderBase>& networkHeader)
{
CBFOption *cbfOption = nullptr;
if (auto ipv4Header = dynamicPtrCast<Ipv4Header>(networkHeader)) {
cbfOption = check_and_cast_nullable<CBFOption *>(ipv4Header->findMutableOptionByType(IPOPTION_TLV_GPSR));
}
return cbfOption;
}
INetfilter::IHook::Result Aodv::ensureRouteForDatagram(Packet *datagram)
{........
CBFOption *cbfOption = createCBFOption(networkHeader->getDestinationAddress());
setCBFOptionOnNetworkDatagram(datagram, networkHeader, cbfOption);
auto cOption = const_cast<CBFOption *>(getCBFOptionFromNetworkDatagram(networkHeader));
I am using INET veins-5.1-i2
I'm trying to get ESP8266 position by unwiredlabs. I followed with this introduction.
This is my arduino code:
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include "ESP8266WiFi.h"
char myssid[] = "Your wifi/hotspot name";
char mypass[] = "Your password";
const char* Host = "www.unwiredlabs.com";
String endpoint = "/v2/process.php";
String token = "d99cccda52ec0b";
String jsonString = "{\n";
double latitude = 0.0;
double longitude = 0.0;
double accuracy = 0.0;
void setup(){
Serial.begin(115200);
// Set WiFi to station mode and disconnect from an AP if it was previously connected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
Serial.println("Setup done");
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(myssid);
WiFi.begin(myssid, mypass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(".");
}
void loop() {
char bssid[6];
DynamicJsonBuffer jsonBuffer;
// WiFi.scanNetworks will return the number of networks found
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n == 0 ) {
Serial.println("No networks available");
} else {
Serial.print(n);
Serial.println(" networks found");
}
// now build the jsonString...
jsonString = "{\n";
jsonString += "\"token\" : \"";
jsonString += token;
jsonString += "\",\n";
jsonString += "\"id\" : \"saikirandevice01\",\n";
jsonString += "\"wifi\": [\n";
for (int j = 0; j < n; ++j) {
jsonString += "{\n";
jsonString += "\"bssid\" : \"";
jsonString += (WiFi.BSSIDstr(j));
jsonString += "\",\n";
jsonString += "\"signal\": ";
jsonString += WiFi.RSSI(j);
jsonString += "\n";
if (j < n - 1) {
jsonString += "},\n";
} else {
jsonString += "}\n";
}
}
jsonString += ("]\n");
jsonString += ("}\n");
Serial.println(jsonString);
WiFiClientSecure client;
//Connect to the client and make the api call
Serial.println("Requesting URL: https://" + (String)Host + endpoint);
if (client.connect(Host, 443)) {
Serial.println("Connected");
client.println("POST " + endpoint + " HTTP/1.1");
client.println("Host: " + (String)Host);
client.println("Connection: close");
client.println("Content-Type: application/json");
client.println("User-Agent: Arduino/1.0");
client.print("Content-Length: ");
client.println(jsonString.length());
client.println();
client.print(jsonString);
delay(500);
}
//Read and parse all the lines of the reply from server
while (client.available()) {
String line = client.readStringUntil('\r');
JsonObject& root = jsonBuffer.parseObject(line);
if (root.success()) {
latitude = root["lat"];
longitude = root["lon"];
accuracy = root["accuracy"];
Serial.println();
Serial.print("Latitude = ");
Serial.println(latitude, 6);
Serial.print("Longitude = ");
Serial.println(longitude, 6);
Serial.print("Accuracy = ");
Serial.println(accuracy);
}
}
Serial.println("closing connection");
Serial.println();
client.stop();
delay(5000);
}
When code had been flashed to esp8266, it showed that could not to connect to https://www.instructables.com/v2/process.php.
ESP serial output:
... // some initial setup string
Requesting URL: https://unwiredlabs.com/v2/process.php
// if connected, "connected" was printed here, but not
closing connection
Then, I tried to use url https://unwiredlabs.com/v2/process.php on chrome browser. This is message:
{
status: "error",
message: "Invalid request",
balance: 0,
help: "Check for misplaced commas and use double quotes to encapsulate strings"
}
This proved that this url existed, but when i tried on Postman, it showed:
Then, I turned off SSL certificate verifycation on Postman. It responsed with a 403 Forbidden error.
So i think the reason caused problem is SSL certificate verifycation of WifiClientSecure.
Anyone can help?
SSL - at least the way this code is trying to use it - requires the Fingerprint of the site you're trying to connect to. The code needs to tell its client object the fingerprint of the site before trying to connect to that site.
Step 1: manually retrieve the fingerprint from the site:
I browsed to https://www.unwiredlabs.com in Chrome and copied the site cerficate, then used openSSL in git bash on Windows to extract the fingerprint:
openssl x509 -noout -fingerprint -sha1 -inform pem -in certificate-file.cer > fingerprint.txt
I then edited the resulting fingerprint.txt file, replacing each ':' with a space.
Search the net for details on how to copy the cite certificate using Chrome, or whatever browser you're using.
Step 2: edit the code to add the fingerprint to the Sketch:
I added the constant 'sslFingerprint', and added a call to client.setFingerprint() just before calling client.connect().
I then removed the code unrelated to connecting to the site, creating an example Sketch that illustrates a successful connection to unwiredlabs.com:
#include <ESP8266HTTPClient.h>
#include "ESP8266WiFi.h"
// The SSL Fingerprint of https://www.unwiredlabs.com
// Certificate expires October 9, 2020
const char *sslFingerprint
= "C3 3E 2F 21 CB 15 4E 02 D5 27 E5 F6 EF FB 31 AE 91 51 A3 5D";
char myssid[] = "yourWiFiSSID";
char mypass[] = "yourWiFiPassword";
const char* Host = "www.unwiredlabs.com";
String endpoint = "/v2/process.php";
void setup() {
Serial.begin(9600);
// Set WiFi to station mode and disconnect from an AP if it was previously connected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
Serial.println("Setup done");
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(myssid);
WiFi.begin(myssid, mypass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(".");
}
void loop() {
WiFiClientSecure client;
//Connect to the client and make the api call
Serial.println("Requesting URL: https://" + (String)Host + endpoint);
client.setFingerprint(sslFingerprint);
if (client.connect(Host, 443)) {
Serial.println("Connected");
}
Serial.println("closing connection");
Serial.println();
client.stop();
delay(5000);
}
When run on a Sparkfun ESP8266 Thing Dev board, the Sketch produces this output:
......
Requesting URL: https://www.unwiredlabs.com/v2/process.php
Connected
closing connection
Im using a Teensy 3.2 microcontroller paired with an ESP8266. Right now im just trying to serve a simple HTML web page that is updated with ajax. I can connect to the ESP and serve a page but im having trouble updating the page with XML data. The problem is somewhere in the loop function. Im not sure how to get the ESP to properly send XML data, or maybe im missing a critical function. Help greatly appreciated!
#define LED1 11
#define LED2 12
#define BUFFER_SIZE 4096
#define SSID "xxxx" // change this to match your WiFi SSID
#define PASS "xxxx" // change this to match your WiFi password
#define PORT "8080" // Port 8080 is default webserver port
char buffer[BUFFER_SIZE];
int n = 0;
String webSite, javaScript, XML, header, content;
void buildWebsite() {
header = "HTTP/1.1 200 OK\r\n";
header += "Content-Type: text/html\r\n";
header += "Connection: close\r\n";
//header += "Refresh: 5\r\n";
buildJavascript();
content = "<!DOCTYPE HTML>\n";
content += javaScript;
content += "<BODY onload='process()'>\n";
content += "<BR>This is the ESP website.<BR>\n";
content += "Runtime = <A id='runtime'></A>\n";
content += "</BODY>\n";
content += "</HTML>\n";
header += "Content-Length:";
header += (int)(content.length());
header += "\r\n\r\n";
webSite = header + content;
}
void buildJavascript() {
javaScript = "<SCRIPT>\n";
javaScript += "var xmlHttp = createXmlHttpObject();\n";
javaScript += "function createXmlHttpObject() {\n";
javaScript += " if(window.XMLHttpRequest) {\n";
javaScript += " xmlHttp = new XMLHttpRequest();\n";
javaScript += " } else {\n";
javaScript += " xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');\n";
javaScript += " }\n";
javaScript += " return xmlHttp;\n";
javaScript += "}\n";
javaScript += "function process(){\n";
javaScript += " if(xmlHttp.readyState == 0 || xmlHttp.readyState == 4){\n";
javaScript += " xmlHttp.open('GET','xml',true);\n";
javaScript += " xmlHttp.onreadystatechange = handleServerResponse();\n"; // no brackets?????
javaScript += " xmlHttp.send();\n";
javaScript += " }\n";
javaScript += " setTimeout('process()',1000);\n";
javaScript += "}\n";
javaScript += "function handleServerResponse(){\n";
javaScript += " if(xmlHttp.readyState == 4 && xmlHttp.status == 200){\n";
javaScript += " xmlResponse = xmlHttp.responseXML;\n";
javaScript += " xmldoc = xmlResponse.getElementsByTagName('response');\n";
javaScript += " message = xmldoc[0].firstChild.nodeValue;\n";
javaScript += " document.getElementById('runtime').innerHTML = message;\n";
javaScript += " }\n";
javaScript += "}\n";
javaScript += "</SCRIPT>\n";
}
void buildXML() {
XML = "<?xml version='1.0' encoding='UTF-8'?>\n";
XML += "<response>\n";
XML += millis2time();
XML += "</response>\n";
}
String millis2time() {
String Time = "";
unsigned long ss;
byte mm, hh;
ss = millis() / 1000;
hh = ss / 3600;
mm = (ss - hh * 3600) / 60;
ss = (ss - hh * 3600) - mm * 60;
if (hh < 10)Time += "0";
Time += (String)hh + ":";
if (mm < 10)Time += "0";
Time += (String)mm + ":";
if (ss < 10)Time += "0";
Time += (String)ss;
return Time;
}
/*******************************************************************
* PROGRAM SETUP
********************************************************************/
void setup() {
delay(1000);
Serial1.begin(115200); // Teensy to ESP8266
Serial.begin(115200); // Teensy to USB Serial
Serial.println("Begin program.");
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
// Initialize ESP8266.
setupWiFi();
}
/*******************************************************************
* DEVICE FUNCTIONS
********************************************************************/
// Read line responses from ESP8266.
bool read_till_eol() {
static int i = 0;
if (Serial1.available()) {
buffer[i++] = Serial1.read();
if (i == BUFFER_SIZE) i = 0;
if (i > 1 && buffer[i - 2] == 13 && buffer[i - 1] == 10) {
buffer[i] = 0;
i = 0;
Serial.print(buffer);
return true;
}
}
return false;
}
// Listen for ESP8266 response. By default we are looking for OK\r\n
char OK[] = "OK\r\n";
byte wait_for_esp_response(int timeout, char* term = OK) {
unsigned long t = millis();
bool found = false;
int i = 0;
int len = strlen(term); // compute length of (string)
// wait for at most timeout milliseconds, or if OK\r\n is found
while (millis() < t + timeout) {
if (Serial1.available()) {
digitalWrite(LED2, HIGH);
buffer[i++] = Serial1.read();
if (i >= len) {
if (strncmp(buffer + i - len, term, len) == 0) {
found = true;
break;
}
}
digitalWrite(LED2, LOW);
}
}
buffer[i] = 0;
Serial.print(buffer);
return found;
}
/*******************************************************************
* LOOP
********************************************************************/
void loop() {
int ch_id, packet_len;
char *pb;
// Look for received IDP (unsolicited data packet) from browser refresh.
if (read_till_eol()) {
if (strncmp(buffer, "+IPD,", 5) == 0) // If strings match...
{
// Request: (+IPD, connection channel, data length)
sscanf(buffer + 5, "%d,%d", &ch_id, &packet_len);
if (packet_len > 0) {
// Read serial until packet_len character received
// start from :
pb = buffer + 5;
while (*pb != ':') pb++;
pb++;
if (strncmp(pb, "GET / HTTP", 10) == 0)
{
// Send HTML data.
wait_for_esp_response(2000);
Serial.println("Serving HTML ->");
buildWebsite();
serve(webSite, ch_id);
}
else if (strncmp(pb, "GET /xml", 8) == 0)
{
// Send XML data.
wait_for_esp_response(2000);
Serial.println("Serving XML ->");
buildXML();
serve(XML, ch_id);
Serial.println(millis2time());
}
}
}
}
}
/*******************************************************************
* SEND DATA
********************************************************************/
// Send the data to the ESP8266.
void serve(String data, int ch_id)
{
Serial1.print("AT+CIPSEND=");
Serial1.print(ch_id);
Serial1.print(",");
Serial1.println(data.length());
if (wait_for_esp_response(1000)) {
Serial1.print(data);
}
else {
Serial1.print("AT+CIPCLOSE=");
Serial1.println(ch_id);
}
}
/*******************************************************************
* SETUP WIFI
********************************************************************/
void setupWiFi() {
// Turn on echo.
Serial1.println("ATE1");
wait_for_esp_response(1000);
// Set mode 3 (client + AP).
Serial1.println("AT+CWMODE=3");
wait_for_esp_response(1000);
// Reset WiFi module.
Serial1.print("AT+RST\r\n");
wait_for_esp_response(1500);
// Join AP.
Serial1.print("AT+CWJAP=\"");
Serial1.print(SSID);
Serial1.print("\",\"");
Serial1.print(PASS);
Serial1.println("\"");
wait_for_esp_response(5000);
// Start server.
Serial1.println("AT+CIPMUX=1");
wait_for_esp_response(1000);
// Create TCP Server.
Serial1.print("AT+CIPSERVER=1,");
Serial1.println(PORT);
wait_for_esp_response(1000);
// Set the automatic socket client disconnection timeout from 1 to 28800 seconds.
Serial1.println("AT+CIPSTO=6000");
wait_for_esp_response(1000);
Serial1.println("AT+GMR");
wait_for_esp_response(1000);
Serial1.println("AT+CWJAP?");
wait_for_esp_response(1000);
Serial1.println("AT+CIPSTA?");
wait_for_esp_response(1000);
Serial1.println("AT+CWMODE?");
wait_for_esp_response(1000);
Serial1.println("AT+CIFSR");
wait_for_esp_response(5000);
Serial1.println("AT+CWLAP");
wait_for_esp_response(5000);
Serial1.println("AT+CIPSTATUS");
wait_for_esp_response(5000);
Serial.println("------------------------------------");
}
One of the problems is here:
xmlHttp.onreadystatechange = handleServerResponse();
You are not binding your handler to the readystatechange event. You are just setting what handleServerResponse() returns to xmlHttp.onreadystatechange.
It should be:
xmlHttp.onreadystatechange = handleServerResponse;
The second problem is that you aren't sending your XML as a proper HTTP response.
You should send it with HTTP headers like you are sending your HTML. And your XML response should set the Content-Type header as text/xml.
There are other possible problems/improvements like:
You are putting your <script> tag directly into the <html> tag. It should be under the <head> tag.
You are using Refresh: 5 header, but you are already refreshing your page with AJAX.
You don't need so send XML, if you just want a single value. You could just send what millis2time() returns and use document.getElementById('runtime').innerHTML = xmlHttp.responseText; in your JavaScript.
Your HTML code is static and doesn't change between requests. It doesn't make sense to build the whole request on every HTTP request.
You could just put the whole request string into a const variable and then serve that.