Chat freely about anything...

User avatar
By Java-Jim
#41893 Hey,
i am trying to sent server events from esp8266WebServer to a connected browser. The javascript code registers to the eventsource, receives the data and shows it in the corresponding div-tag, but it seems like a connection drop after that, so that the browser repeatly reconnects to the source and throws an event error at onmessage(). It seems, that the server code is not able to hold a persistent connection over which the event could be sent. if i call the function that sends the event message nothing happens on the browser. i examined the esp8266WebServer.sent() function and found out, that it always sends a Connection: close header, so i build the keep-alive header and content myself. Browser now reflects that responseheader. i removed the Content.length as read, that could be a problem to firefox. I also tried ios safari browser as firefox could cause the error, but the result is the same.

i´m trying for hours to get SSEs to work - maybe some of you have a good advice for me?

I am using arduino ide 1.61 with esp8266 core sdk version 1.3.0.

parts of the used code:
Code: Select all
  ESP8266WebServer webserver(80);
  webserver.on("/rfmonitor", rfmonitor);
  webserver.on("/rfevent", rfevent);
  webserver.on("/eventcheck.js", [](){if(debug) Serial.println(F("eventcheck.js")); webserver.send(200, "text/plain", eventcheck_js);});

//////////////////////////////////////////////////////////////////////////////////////////////////
// send monitoring page with ServerSentEvent support
void rfmonitor(){
  if (debug) Serial.println(__FUNCTION__);
  String Header, result;
  result =  "<head> <meta content='text/html'; charset='utf-8'/>";
  result += "<meta name='viewport' content='width=device-width, initial-scale=1'/>";
  result += "<title>RF-Monitor</title></head><h1>RF-Monitor</h1>";
  result += "<body><div id='rfoutput'></div>";
  result += "<script src='/eventcheck.js'></script></body>";
  Header = "HTTP/1.1 200 OK\r\n";
  Header += "Content-Type: text/html;charset=UTF-8\r\n";
  Header += "Connection: keep-alive\r\n"; 
  Header += "Cache-Control: no-cache\r\n";
//  Header += "Content-Length: " + result.length() + "\r\n";
  Header += "Access-Control-Allow-Origin: *\r\n";
  Header += "\r\n";
 
  webserver.sendContent(Header);   
  webserver.sendContent(result);
//  webserver.send(200, "text/html", result);
}

//////////////////////////////////////////////////////////////////////////////////////////////////
// send event data
void rfevent(){
  if (debug) Serial.println(__FUNCTION__);
  String Header, result;
//  result = "data: {'eventName': 'rf_received', ";
  result =  "data: {uptime: " + String(uptime()) + "}\n\n";
//  result += "event: message \n";
  result += "retry: 10000\n";
 
  Header = "HTTP/1.1 200 OK\r\n";
  Header += "Content-Type: text/event-stream;charset=UTF-8\r\n";
  Header += "Connection: keep-alive\r\n";
  Header += "Cache-Control: no-cache\r\n";
//  Header += "Content-Length: " + result.length() + "\r\n";
  Header += "Access-Control-Allow-Origin: *\r\n";
  Header += "\r\n";
 
  webserver.sendContent(Header);   
  webserver.sendContent(result);
 }

const char eventcheck_js[] PROGMEM = R"=====(
  if (typeof(EventSource) != "undefined"){
    var source = new EventSource('/rfevent');
    var outputElement = document.getElementById('rfoutput');
    var eventCounter = 0;
    source.onmessage = function(e) {
      eventCounter++;
      outputElement.innerHTML = "data:" + e.data + " <br> " + eventCounter + " Events";
      console.log(e.data);
    };
    source.addEventListener('open', function(e) {
     console.log("connected");
    }, false);
   source.addEventListener('error', function(e) {
     console.error(e);
    }, false);
  }
  else{
  document.write("This Browser does not support EventSource.")
  }
)=====";


this is the console.log from firefox
Code: Select allconnected eventcheck.js:12:4
{uptime: 0 days - 00:16:09} eventcheck.js:9:7
error { target: EventSource, isTrusted: true, currentTarget: EventSource, eventPhase: 2, bubbles: false, cancelable: false, defaultPrevented: false, timeStamp: 1456434943116000, originalTarget: EventSource, explicitOriginalTarget: EventSource, NONE: 0 } eventcheck.js:15:4

connected eventcheck.js:12:4
{uptime: 0 days - 00:16:22} eventcheck.js:9:7
error { target: EventSource, isTrusted: true, currentTarget: EventSource, eventPhase: 2, bubbles: false, cancelable: false, defaultPrevented: false, timeStamp: 1456434955663000, originalTarget: EventSource, explicitOriginalTarget: EventSource, NONE: 0 } eventcheck.js:15:4


this is the serial log
Code: Select allrfmonitor
eventcheck.js
rfevent
rfevent
rfevent
rfevent
...
User avatar
By Java-Jim
#45051 Update - it´s working!

Here are the needed parts. In my case it shows a jquery mobile website with a codegenerated svg graphic of received rf datastreams.
It is incredible fast on my iphone - ughh. The svg image updates happen extremely fast and gives a scope or logicanalyser view of the live data.
Sadly my firefox on a fast worstation pc is times slower than the phone. Don´t know why this is so.
Currently the routine sends 100 updates (on iphone less than a second) and checking in between if new data is there or if the browser call another url. After that the browser reconnects after 1000 miliseconds and it begins again. You could make the loop infenite, so that no reconnects needed or edit the retry after connection break interval.
SSE can be started or stopped by button click events.

Code: Select all//////////////////////////////////////////////////////////////////////////////////////////////////
// register webserver callbacks
void initwebserver(void){
  ESP8266WebServer webserver(80);
  webserver.on("/rfmonitor", rfmonitor);
  webserver.on("/rfevent", rfevent);
  webserver.on("/eventcheck.js", [](){if(debug) Serial.println(F("eventcheck.js")); webserver.send(200, "text/plain", eventcheck_js);});
 webserver.begin();
}


Code: Select all//////////////////////////////////////////////////////////////////////////////////////////////////
// the sse js code
const char eventcheck_js[] PROGMEM = R"=====(
var source;
  function ssestart(){
    if (typeof(EventSource) != 'undefined'){
      source = new EventSource('/rfevent');
      var outE = document.getElementById('out');
      source.addEventListener('rfdata', function(e) {
        outE.innerHTML = e.data;
      });
    }
    else{
      document.write('This Browser does not support EventSource.')
    }
  };
  function ssestop(){source.close();};
)=====";


Code: Select all//////////////////////////////////////////////////////////////////////////////////////////////////
// show monitoring page with ServerSendEvent support
void rfmonitor(){
  if (debug) Serial.println(__FUNCTION__);
  String Header, result;

  Header = "HTTP/1.1 200 OK\r\n";
  Header += "Content-Type: text/html\r\n";
  Header += "Connection: close\r\n\r\n"; 
  Header += "Cache-Control: no-cache\r\n";
  Header += "Access-Control-Allow-Origin: *\r\n";
  Header += "\r\n";
 
  if (debug) { Serial.print(__FUNCTION__); Serial.println(" sending header"); }
  webserver.sendContent(Header);   

  result = jqHeader;
  result += "<div data-role='page' id='rfmonitor'>";
  result += "<div data-role='header'>";
  result +=  "<h1>RF-Monitor</h1>";
  result += "</div>";
  result += "<div data-role='content' class='test' style='width:90%; margin:0 auto;'>";
  result += "<div id='out'>RF-Received: wait...</div>";
  result += "<div data-role='controlgroup' data-type='horizontal' align='center'>";
  result += "<button class='ui-btn ui-icon-check ui-btn-icon-left' onclick='ssestart()'>Start</button>"; 
  result += "<button class='ui-btn ui-icon-delete ui-btn-icon-left' onclick='ssestop()'>Stop</button></div>"; 
  result += "<script src='/eventcheck.js'></script>";
  result += jqFooter(2);

  if (debug) {Serial.print(__FUNCTION__); Serial.println(" sending content"); }
  webserver.sendContent(result);
}


Code: Select all//////////////////////////////////////////////////////////////////////////////////////////////////
// send event data
void rfevent(){
  if (debug) Serial.println(__FUNCTION__);
  String Header, result;
 
  Header = "HTTP/1.1 200 OK\r\n";
  Header += "Content-Type: text/event-stream;\r\n";
  Header += "Cache-Control: no-cache\r\n";
  Header += "\r\n"; 
  if (debug) Serial.print(__FUNCTION__); Serial.println(" sending header");
  webserver.sendContent(Header);   
  int j=100;
  for (int i=1; i<=j; i++){
    handleReceiver();
    webserver.handleClient();
      result = "event:rfdata\n";
      result += "data:";
      result += makesvg();
    result += "\n\n";
    result += "retry: 1000\n"; 
    webserver.sendContent(result);
    delay(20);
    if (debug) {Serial.print(i); Serial.print(" of "); Serial.print(j); Serial.println(" events send");}
  }
 }