Webserver controller with url trimming for value controls

Dependencies:   LCD_DISCO_F746NG BSP_DISCO_F746NG

main.cpp

Committer:
jonasnimb7992
Date:
2019-08-26
Revision:
10:f941e0750773
Parent:
9:161bed13b17e

File content as of revision 10:f941e0750773:

#include "mbed.h"
#include "EthernetInterface.h"
#include "TCPSocket.h"
#include "LCD_DISCO_F746NG.h"
#include <stdio.h>
#include <vector>
#include <sstream>
#include <iostream>       // std::cout
#include <string>         // std::string

using namespace std;

#define IP      "192.168.137.200"
#define GATEWAY "192.168.137.1"
#define NETMASK "255.255.255.0"
#define PORT    80

Serial              pc(USBTX, USBRX);
EthernetInterface*  net;
TCPSocket           server;
TCPSocket*          clientSocket;
SocketAddress       clientAddress;
LCD_DISCO_F746NG    displayLCD;

Thread              tempUpdater;            // Multithreaded temp updater
Thread              urlManager;             // Thread for url management

char                receiveBuf[1024];
const int           OFF = 0;                // Switch off
const int           ON = 1;                 // Switch on
DigitalOut          sw(LED1);               // A digital output to be switched on/off
PwmOut              PwrLED(D3);             // Simulation of motor control
AnalogIn            tempSensor(A0);         // Temperature sensor
float               roomTemp = 0.0;         // A temperature sensor output
float               units;                  // Final variable to contain temp value
unsigned int        a, beta = 3975;         // Unsigned int's to use for temperature calculations
float               resistance;             // LE PIECE OF RESISTAAAANCE
float               temperature;            // Temperature variable to use for calculations
int                 sliderVal               // Value of the slider
const string        PASSWORD = "daddyshark";    // Change as you like
const string        HTTP_OK = "HTTP/1.0 200 OK";
const string        MOVED_PERM = "HTTP/1.0 301 Moved Permanently\r\nLocation: ";
const string        UNAUTHORIZED = "HTTP/1.0 401 Unauthorized";
string              httpHeader;             // HTTP header
string              httpContent;            // HTTP content

/**
 * @brief   Analyses the received URL
 * @note    The string passed to this function will look like this:
 *          GET /HTTP/1.....
 *          GET /password HTTP/1.....
 *          GET /password/ HTTP/1.....
 *          GET /password/?sw=1 HTTP/1.....
 *          GET /password/?sw=0 HTTP/1.....
 * @param   url URL string
 * @retval -3 just refresh page
 *         -2 no command given but password valid
 *         -1 invalid password
 *          0 switch off
 *          1 switch on
 */
int8_t analyseURL(string& url)
{
    if (url.length() < 5 + PASSWORD.size() + 1)
        return(-1);

    if (url.substr(5, PASSWORD.size()) != PASSWORD)
        return(-1);

    uint8_t pos = 5 + PASSWORD.size();

    if (url.substr(pos, 1) != "/")
        return(-1);

    if (url.substr(pos++, 1) == " ")
        return(-2);

    string  cmd(url.substr(pos, 5));

    if (cmd == "?sw=0")
        return(0);

    if (cmd == "?sw=1")
        return(1);
    return(-3);
}
/**
 * @brief
 * @note
 * @param
 * @retval
 */
string& movedPermanently(uint8_t flag)
{
    if (flag == 1)
        httpContent = "/" + PASSWORD + "/";
    else
        httpContent = "";

    httpContent += "<h1>301 Moved Permanently</h1>\r\n";

    return(httpContent);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
string& showWebPage(int status)
{
    char    roomTempStr[5];

    //roomTemp = ds1820.read();
    sprintf(roomTempStr, "%3.1f", roomTemp);

    /*$off*/
    httpContent  =
        "<head>"
        "<meta charset=\"utf-8\">"
        "<meta name=\"viewport\" content=\" initial-scale=1.0; maximum-scale=1.0; minimum-scale=1.0; user-scalable=0;\"/>"
        "<title>Smart Home</title>"
        "<link href='http://fonts.googleapis.com/css?family=Droid+Sans&v1' rel='stylesheet' type='text/css'>"
        "<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>"
        "<style>"
        ".switch {"
            "position: relative;"
            "display: inline-block;"
            "width: 60px;"
            "height: 34px;"
        "}"
        ".switch input {display:none;}"
        ".slider {"
            "position: absolute;"
            "cursor: pointer;"
            "top: 0;"
            "left: 0;"
            "right: 0;"
            "bottom: 0;"
            "border-radius: 34px;"
            "background-color: #ccc;"
            "-webkit-transition: .4s;"
            "transition: .4s;"
        "}"
        ".slider:before {"
            "position: absolute;"
            "content: \"\";"
            "height: 26px;"
            "width: 26px;"
            "left: 4px;"
            "bottom: 4px;"
            "border-radius: 50%;"
            "background-color: white;"
            "-webkit-transition: .4s;"
            "transition: .4s;"
        "}"
        "input:checked + .slider {"
            "background-color: #8ce196;"
        "}"
        "input:focus + .slider {"
            "box-shadow: 0 0 1px #8ce196;"
        "}"
        "input:checked + .slider:before {"
            "-webkit-transform: translateX(26px);"
            "-ms-transform: translateX(26px);"
            "transform: translateX(26px);"
        "}"
        "</style>"
        "<script>"
        "$(document).ready(function(){"
                "setInterval(function() {"
                    "$(\"#readTemp\").load(\" #readTemp\");"
                "}, 10000);"
            "$(\"#click\").click(function(){"
                    "$(\"#readTemp\").load(\" #readTemp\");"
            "});"
        "});"
        "</script>"
        "</head>"
        "<body>"
        "<h2><a href=\".\" title=\"Click to refresh the page\">Smart Home</a></h2>"
        "<button id=\"click\">Click on this.</button>"
        "<div id=\"readTemp\">"
        "<pre>Temperature:\t" + string(roomTempStr) + "&deg;C</pre>"
        "<pre>Temperature:\t" + string(roomTempStr) + "&deg;C</pre>"
        "</div>"
        "<pre>Heating:\t";

    if(status == ON) {
        httpContent +=
            "<a href=\"./?sw=0\" class=\"switch\"> "
            "<input type=\"checkbox\" checked>";
    }
    else {
        httpContent +=
            "<a href=\"./?sw=1\" class=\"switch\"> "
            "<input type=\"checkbox\">";
    }

    httpContent +=
        "<div class=\"slider\"></div>"
        "</body>";

    return httpContent;
    /*$on*/
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void sendHTTP(TCPSocket& client, string& header, string& content)
{
    /*$off*/
    char    content_length[5] = { };

    header +=
        "\r\nContent-Type: text/html\r\n"
        "Content-Length: ";
    sprintf(content_length, "%d", content.length());
    header +=
        string(content_length) + "\r\n"
        "Pragma: no-cache\r\n"
        "Connection: About to close\r\n\r\n";

    string  webpage = header + content;
    client.send((char*)webpage.c_str(), webpage.length());
    pc.printf("HTTP message sent to client.\n\r");
    /*$on*/
}

void clearLineLCD(int line) {
    displayLCD.DisplayStringAt(0, LINE(line),(uint8_t *)("                                           "), CENTER_MODE);
}

int findURL(string mainString,string stringToFind) {
    int foundAt;
    int number;
    
    //printf("String to search in: %s\r\n", mainString);
    //printf("String to search for: %s\r\n", stringToFind);
    
    std::size_t found = mainString.find(stringToFind); 
    if (found != string::npos) {
        foundAt = found;
        //printf("Found at %i\r\n", foundAt);
        string getString = mainString.substr(foundAt, 7);
        string trimString = getString.substr(stringToFind.length(), 3);
        std::istringstream iss (trimString);
        iss >> number;
        
        //printf("%s found: %s\r\n", stringToFind, getString);
        
        //printf("Converting: %s\r\n", trimString);
        
        //printf("Final Number = %i\r\n", number);
    }
    else {
        number = -1;    
    }
    return number;
}

void tempReader() {
    while (true) {
        a = tempSensor.read_u16(); /* Read analog value */
    
        // Calculate the resistance of the thermistor from analog voltage read.
        resistance= (float) 10000.0 * ((65536.0 / a) - 1.0);
            
        // Convert the resistance to temperature using Steinhart's Hart equation
        temperature=(1/((log(resistance/10000.0)/beta) + (1.0/298.15)))-273.15;
            
        units = (float) temperature;
                    
        roomTemp = units;
        
        if (sw == ON) {
            if (roomTemp >= 30) {
                PwrLED = 0.80;
            }
            else {
                PwrLED = 0.20;
            }
        }
        if (sw == OFF) {
            PwrLED = 0;   
        }
        char    tempStr[20];
        sprintf(tempStr, "Temperature is: %3.1f C", roomTemp);

        displayLCD.DisplayStringAt(0, LINE(6),(uint8_t *)(tempStr), CENTER_MODE);
        Thread::wait(1000);
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int main(void)
{
    displayLCD.Clear(LCD_COLOR_BLACK);
    displayLCD.SetBackColor(LCD_COLOR_BLACK);
    displayLCD.SetTextColor(LCD_COLOR_WHITE);
    
    pc.printf("Starting.. \r\n\r\n");

    net = new EthernetInterface();

    if (!net) {
        pc.printf("Error! No network inteface found.\n");
        return 0;
    }

    net->set_network (IP, NETMASK, GATEWAY);  // include this for using static IP address
    nsapi_size_or_error_t   r = net->connect();

    if (r != 0) {
        pc.printf("Error! net->connect() returned: %d\n", r);
        return r;
    }

    // Show the network address
    const char*     ip = net->get_ip_address();
    const char*     netmask = net->get_netmask();
    const char*     gateway = net->get_gateway();
    
    displayLCD.DisplayStringAt(0, LINE(2),(uint8_t *)("IP Address"), CENTER_MODE);
    displayLCD.DisplayStringAt(0, LINE(4),(uint8_t *)( ip ? ip : "None"), CENTER_MODE);
    pc.printf("IP address: %s\r\n", ip ? ip : "None");
    pc.printf("Netmask: %s\r\n", netmask ? netmask : "None");
    pc.printf("Gateway: %s\r\n\r\n", gateway ? gateway : "None");
    pc.printf("Usage: Type %s/%s/ into your web browser and hit ENTER\r\n\r\n", ip, PASSWORD.c_str());
    
    tempUpdater.start(tempReader); // Starting the multithreaded workload so it won't affect main program

    /* Open the server on ethernet stack */
    server.open(net);

    /* Bind the HTTP port (TCP 80) to the server */
    server.bind(ip, PORT);

    /* Can handle 5 simultaneous connections */
    server.listen(5);

    //listening for http GET request
    while (true) {
        pc.printf("=========================================\r\n");

        nsapi_error_t   error;

        clientSocket = server.accept(&error);
        if (error != 0) {
            pc.printf("Connection failed!\r\n");
            clientSocket->close();
            continue;
        }

        clientSocket->getpeername(&clientAddress);
        pc.printf("Client with IP address %s connected.\r\n\r\n", clientAddress.get_ip_address());
        clientSocket->recv(receiveBuf, 1023);
        pc.printf("Data received:\r\n%s\n\r", receiveBuf);

        string  received(receiveBuf);

        if (received.substr(0, 3) != "GET") {
            httpHeader = HTTP_OK;
            httpContent = "<h1>200 OK</h1>";
            sendHTTP(*clientSocket, httpHeader, httpContent);
            clientSocket->close();
            continue;
        }

        if (received.substr(0, 6) == "GET / ") {
            httpHeader = HTTP_OK;
            httpContent = "<p>Usage: Type http://ip_address/password/ into your web browser and hit ENTER</p>\r\n";
            sendHTTP(*clientSocket, httpHeader, httpContent);
            clientSocket->close();
            continue;
        }

        int cmd = analyseURL(received);

        int testFind = findURL(received, "val=");
        
        if (testFind == -1) {
            printf("--------------Couldn't find 'val='\r\n");
        }
        if (testFind >= 0) {
            printf("--------------Found 'val=': %i\r\n", testFind);   
        }

        switch (cmd) {
            case -3:
                // update webpage
                httpHeader = HTTP_OK;
                sendHTTP(*clientSocket, httpHeader, showWebPage(sw));
                break;

            case -2:
                // redirect to the right base url
                httpHeader = MOVED_PERM;
                sendHTTP(*clientSocket, httpHeader, movedPermanently(1));
                break;

            case -1:
                httpHeader = UNAUTHORIZED;
                httpContent = "<h1>401 Unauthorized</h1>";
                sendHTTP(*clientSocket, httpHeader, httpContent);
                break;

            case 0:
                sw = OFF;   // turn the switch off
                PwrLED = 0; // turn the "motor" led off
                clearLineLCD(8);
                displayLCD.DisplayStringAt(0, LINE(8),(uint8_t *)("Heating OFF"), CENTER_MODE);
                httpHeader = HTTP_OK;
                sendHTTP(*clientSocket, httpHeader, showWebPage(sw));
                break;

            case 1:
                sw = ON;    // turn the switch on
                clearLineLCD(8);
                displayLCD.DisplayStringAt(0, LINE(8),(uint8_t *)("Heating ON"), CENTER_MODE);
                httpHeader = HTTP_OK;
                sendHTTP(*clientSocket, httpHeader, showWebPage(sw));
                break;
        }
        clientSocket->close();
    }
}