Chat freely about anything...

User avatar
By AdrianM
#39901 It doesn't look like anyone here makes use of Server-Sent Events to dynamically update content in web pages so I'm wondering if there's a reason this won't work.

So as I understand it, on registering the event in javascript on the webpage e.g.
Code: Select allvar eventSource = new EventSource("server");

...a single GET request opens a connection to the server which then asynchronously streams data back to the page with the following sort of flow:
Code: Select all=> GET server
- HEADERS -
Accept: text/event-stream

<= HTTP/1.1 200 OK
-RESPONSE HEADERS -
Content-Type: text/event-stream

- RESPONSE BODY -
data: {"time": 1234556677880, "temperature": "24.2"}

:colon precedes comments, used to keep the connection alive if necessary

data: {"time": 1234556679000, "temperature": "24.3"}


The data values then get passed to the "eventSource.onmessage" event where they can be placed into the .innerHTML of the required display element(s).

Obviously the connection must be left open for this to work but it seems like a neat way to dynamically alter content on a page that's been served up by the ESP8266 whenever fresh values need to be pushed to the page in the browser.

I haven't tried it yet because I'm not sure how the URL in the registration phase should be formed. All the examples I've seen for this refer to a .PHP script running on the server - yet our servers are capable of generating their own content. Now I type these words I suppose it means the URL can be anything and on getting and recognising the request, just responding with the data in a "send"?
User avatar
By AdrianM
#39949 It does work after all. In case anyone wants to try it here's some bare bones in LUA...

index.htm
Code: Select all<!DOCTYPE html>
<html>
   <head>
      <title>STREAM</title>
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta charset="utf-8" />
   </head>
<body>

<br><br>
<div id="serverData">Here is where the server sent data will appear</div>
<br><br>

<script type="text/javascript">
   //check for browser support
   if(typeof(EventSource)!=="undefined") {
      //create an object, passing it the name and location of the server side script
      var eSource = new EventSource("stream.htm");
      //detect message receipt
      eSource.onmessage = function(event) {
         //write the received data to the page
         document.getElementById("serverData").innerHTML = event.data;
      };
   }
   else {
      document.getElementById("serverData").innerHTML="Whoops! Your browser doesn't receive server-sent events.";
   }
   
</script>
</body>
</html>


init.lua
Code: Select allwifi.setmode(wifi.STATION)
wifi.sta.config("ssid","password")
wifi.sta.connect()
tries=0
tmr.alarm(0, 1000, 1, function(Q) 
    if wifi.sta.getip() == nil then
        tries=tries+1
        if tries>10 then
            tmr.stop(0)
            print("unable to get onto LAN after 10 seconds")
        end
    else
        tmr.stop(0)
        print("On LAN @ "..wifi.sta.getip(),"after "..tries.." seconds")
       
        local httpRequest={}
        httpRequest["/"]="index.htm";
        httpRequest["/index.htm"]="index.htm";
       
        local getContentType={};
        getContentType["/"]="text/html";
        getContentType["/index.htm"]="text/html";
        getContentType["/stream.htm"]="text/event-stream";
       
        if srv then srv:close() srv=nil end
        srv=net.createServer(net.TCP)
        srv:listen(80,function(conn)
            conn:on("receive", function(conn,payload)
                print("Request:"..payload.."<<<");
                local _, _, method, path, vars = string.find(payload, "([A-Z]+) (.+)?(.+) HTTP");
                if(method == nil)then
                 _, _, method, path = string.find(payload, "([A-Z]+) (.+) HTTP");
                end
       
                if path~=nil then
                    print("PATH="..path)
                    if getContentType[path] then
                       if(path=="/stream.htm") then
                            requestFile=nil
                            count=0
                            tmr.alarm(1, 1000, 1, function(Q)
                                count=count+1
                                local post =  "Content-Type: text/event-stream\r\n"
                                            .."Cache-Control: no-cache\r\n"
                                            .."data:"..count.."\n\n"
                                print (post)
                                conn:send(post)
                            end)
                        else
                            requestFile=httpRequest[path];
                            print("Sending file "..requestFile)
                            filePos=0;
                        end
                        conn:send("HTTP/1.1 200 OK\r\nContent-Type: "..getContentType[path].."\r\n\r\n");
                    else
                        requestFile=nil
                        print("File "..path.." not found");
                        conn:send("HTTP/1.1 404 Not Found\r\n\r\n")
                        conn:close();
                        collectgarbage();
                    end
                else
                    print("nil path in payload:",payload);
                end
            end)
           
            conn:on("sent",function(conn)
                if requestFile then
                    if file.open(requestFile,r) then
                        conn:send(file.read());
                        file.close();         
                    else
                        print("Error opening file"..requestFile);
                    end
                    print("File sent. Connection closed");
                    conn:close();
                    collectgarbage();
               end
            end)
        end)
    end
end)


Just edit the WiFi credentials, Upload both files in ESPlorer, hit reset and browse to the IP that's reported in the console. The webpage should show a count that increments once a second.
User avatar
By AdrianM
#72483 It annoys me when I find code snippets that don't work on previous SDKs... so for SDK 2.1.0 change:

Code: Select allwifi.sta.config("ssid","password")
wifi.sta.connect()

to:
Code: Select allstation_cfg={}
station_cfg.ssid="ssid"
station_cfg.pwd="password"
station_cfg.save=false
wifi.sta.config(station_cfg)
station_cfg=nil


and everything is OK again - although I just spotted a spurious
Code: Select all conn:close();
in the "File not found" part of the handler. Might be worth removing.

Oh, and I was wondering, how can it be discovered if the connection drops because as the demo is at the moment it throws an error "not connected" on conn:send and while it can be trapped it woudl be better to check in advance.
User avatar
By rudy
#72484
AdrianM wrote:It doesn't look like anyone here makes use of Server-Sent Events to dynamically update content in web pages

I thought it was interesting, a possible option, but from what I recall using websockets was not much harder and I often want bidirectional communications. But maybe it is time for me to look at this again.