click for Part 3
click for Part 4
Introduction
In this post we will narrow things down to a simple project, which will form an important step towards our overall goal. We will make a web app that communicates with a stripped down C server on the BBB. This app will be able to request the temperature and display it, nothing more. The client will be coded using HTML and JavaScript, allowing the client side to be completely handled by a web-browser, regardless of which operating system is being used for the client and making it a true IoT device. I have only tested the front end on Firefox and Google Chrome.HTTP headers
When a web-browser makes a request to a server it sends and expects headers. We need to make our server deal with this. It is easiest if our web-page comes from the same server address as our data does; then we don't need to worry about cross-origin resource sharing in our server's header.A basic example of an HTTP header is:
HTTP/1.1 200 OK
Date: Mon 12 Nov 2017 05:23:02 GMT
Server: C_BBB
Content-Length: 1332
Content-type: text/html; charset=utf-8
Note that a blank line must follow this header. For us the most important lines in the header are the first one and the Content-Length. The first line tells the client that the server is happy and to expect its request to follow the header. If the server can't find the request then it should send "HTTP/1.1 404 Not Found", which I'm sure we've all experienced. The length gives the number of bytes we are going to send in the message which follows.
We'll use this header to send our web-page to the client (our web-browser). Our web-page is in the file "web_client.html" which is placed in the same directory as our server. When the server is ready to call the write() function to reply to the client the following C function is called which does all the work in sending the header and the html file
void file_serv(int sock, char fn[])
{
char buffer[512];
int n, c;
FILE *ifp;
printf("Hi from file_serv %s\n", fn);
ifp = fopen(fn, "r");
fseek(ifp, 0L, SEEK_END);
// get the size of the file in bytes, less the end of file character
n = ftell(ifp) - 1;
rewind(ifp);
// Simple HTTP headers
sprintf(buffer, "HTTP/1.1 200 OK\nServer: C_BBB\nContent-Length: %d", n);
n = strlen(buffer);
sprintf(buffer + n, "\nContent-Type: text/html; charset=utf-8\n\n");
n = write(sock, buffer, strlen(buffer)); // send header to client
if(n < 0) error("ERROR writing to socket");
while(1){ // loop which sends the html file to the client
n = 0;
while((c = getc(ifp)) != EOF){
buffer[n++] = c;
if(n == 511) break;
}
if(n == 0) break; // end of file reached
buffer[n] = '\0';
n = write(sock, buffer, strlen(buffer));
if (n < 0) error("ERROR writing to socket");
if(n == 0) break; // The browser ain't buying what we're selling
}
fclose(ifp);
printf("Bye from file_serv\n");
}
In this case we haven't put the date in the header, but it's not an issue because we won't be using this feature. This function is quite versatile, it will load any html file passed as an argument to it provided that it is compatible with the header. We will also use it to load the "favicon.ico" file, which is just a blank icon I downloaded and is used to keep the browser happy.
It is straightforward to make our server print the headers sent to it. When Firefox contacts our server, using the url localhost, it makes the request
GET / HTTP/1.1
Host: localhost:20006
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:63.0) Gecko/20100101 Firefox/63.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
This is followed up with the "favicon.ico" icon request with the GET line now "GET /favicon.ico HTTP/1.1" in the header.
If we append ?tt onto the URL (e.g. http://localhost:20006/?tt) then the GET line becomes: "GET /?tt HTTP/1.1". So this is what we will use to send a request to our server from the client side.
The client
The client code is small enough to reproduce here<!DOCTYPE html>
<html>
<body>
<h1>Temerature Reader</h1>
<p id="mydata"></p>
<button onclick="displayResult()">Update</button>
<script>
const url=document.location.origin + '?data';
displayResult();
function displayResult(){
fetch(url).then(function(response){
if(response.ok){
response.text().then(function(myText){
str = myText;
document.getElementById("mydata").innerHTML = myText;
});
}else{
console.log('Network request failed with response ' +
response.status + ': ' + response.statusText);
}
});
}
</script>
</body>
</html>
This code uses the JS fetch() function to contact the server whenever the "Update" button is pushed. A picture of the working client is shown directly below:
When the Update button is pressed the server is contacted, and the text line containing the time and temperature is returned and displayed as shown.
The Code
The code can be found at https://github.com/wilstep/IOT-temperature-logger-for-the-BeagleBone-Black/tree/1.1 The relevant code is located in the subdirectory "web-trial". Compile on the BBB by issuing the command "make" and then run with the command
wserver x
where x is the port number (20000 is not a bad choice). Once the server is running you can access it from a browser. In my case when running on port 20000 the URL "http://beaglebone.local:20009/" is all that's needed.
No comments:
Post a Comment