Web server based weather station using Sparkfun Weather Meters.

Dependencies:   FatFileSystem mbed WeatherMeters SDFileSystem

main.cpp

Committer:
AdamGreen
Date:
2012-02-25
Revision:
1:c7958aa34fa1
Parent:
0:616601bde9fb

File content as of revision 1:c7958aa34fa1:

/* Copyright 2012 Adam Green (http://mbed.org/users/AdamGreen/)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
/* Provides a simple web interface to these weather meters from Sparkfun:
    http://www.sparkfun.com/products/8942
*/
#include <stdarg.h>
#include <mbed.h>
#include "SDFileSystem.h"
#include "network.h"
#include "HTTPServer.h"
#include "debug.h"
#include "homepage.h"
#include "WeatherMeters.h"


// Name of this sample
#define APPLICATION_NAME "WindMeter"

// Set to appropriate "#.#.#.#" string for static IP addresses or
// set to NULL for DHCP to be used instead.
#define HTTP_STATIC_IP_ADDRESS      "192.168.0.127"
#define HTTP_STATIC_SUBNET_MASK     "255.255.255.0"
#define HTTP_STATIC_GATEWAY_ADDRESS "192.168.0.1"

// Name of this device to be use with DHCP server.
#define HTTP_HOST_NAME   "WindMeter"

// Port to which the HTTP server is to be bound.  The well known port number
// for HTTP is 80.
#define HTTP_PORT       80

// String used to identify this sample HTTP server to the client.
#define HTTP_SERVER_NAME "lwIPHTTPServer/1.0"

// Root name of filesystem to be used for HTTP server sample.
#define ROOT_FILESYSTEM_NAME "SD/webfs"

// Where the SHttpServer structure containing the HTTP_BUFFER_POOL should be
// placed in the device's memory.
#define HTTP_MEM_POSITION __attribute((section("AHBSRAM0"),aligned))

// Utility macro to determine the element count of an array.
#define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))



// GET request handler context which bases HTML on printf() substituion.
class CPrintfGetRequestHandlerContext : public IHTTPRequestHandlerContext
{
public:
    // Constructors / Destructors
    CPrintfGetRequestHandlerContext(const char* pStatusLine,
                                    size_t      StatusLineSize,
                                    const char* pHeaders, 
                                    size_t      HeaderSize)
    {
        m_pContent = NULL;
        m_pCurrent = NULL;
        m_ContentSize = 0;
        m_pStatusLine = pStatusLine;
        m_StatusLineSize = StatusLineSize;
        m_pHeaders = pHeaders;
        m_HeaderSize = HeaderSize;
    }
    ~CPrintfGetRequestHandlerContext()
    {
        free(m_pContent);
        m_pContent = NULL;
        m_pCurrent = NULL;
        m_pHeaders = NULL;
        m_pStatusLine = NULL;
        m_ContentSize = 0;
        m_HeaderSize = 0;
        m_StatusLineSize = 0;
    }

    int printf(const char* pFormat, ...)
    {
        va_list ArgList;
        int     RequiredBufferSize;

        // Only expect this to be called once.
        assert ( NULL == m_pContent && 0 == m_ContentSize );
        
        // How big does the HTML content buffer need to be?
        va_start(ArgList, pFormat);
        RequiredBufferSize = vsnprintf(NULL, 0, pFormat, ArgList);
        va_end(ArgList);
        
        // Check for error.
        if (RequiredBufferSize < 0)
        {
            return RequiredBufferSize;
        }
        
        // Allow space for the NULL terminator.
        RequiredBufferSize++;
        
        // Attempt to allocate a buffer of adequate size.
        m_pContent = (char*)malloc(RequiredBufferSize);
        if (!m_pContent)
        {
            return -1;
        }
        m_ContentSize = RequiredBufferSize;
        m_pCurrent = m_pContent;
        
        // Place the HTML content into the newly allocated buffer.
        va_start(ArgList, pFormat);
        RequiredBufferSize = vsnprintf(m_pContent, m_ContentSize, pFormat, ArgList);
        va_end(ArgList);
        
        // Make sure that nothing has changed out from underneath us.
        assert ( (int)m_ContentSize == RequiredBufferSize+1 );
        
        return RequiredBufferSize + 1;
    }
    
    // IHTTPRequestHandlerContext interface methods.
    virtual int  BeginRequestHeaders()
    {
        // Ignore request headers.
        return 0;
    }
    virtual void WriteRequestHeader(const char* pHeaderName, 
                                    const char* pHeaderValue)
    {
        // Unused parameters.
        (void)pHeaderName;
        (void)pHeaderValue;
        
        assert ( FALSE );
    }
    
    virtual void EndRequestHeaders()
    {
        assert ( FALSE );
    }
    
    virtual int BeginRequestContent(size_t ContentSize)
    {
        // Ignore request content body.
        (void)ContentSize;
        return 0;
    }
    
    virtual void WriteRequestContentBlock(const void* pBlockBuffer, 
                                          size_t BlockBufferSize)
    {
        (void)pBlockBuffer;
        (void)BlockBufferSize;
        assert ( FALSE );
    }
    
    virtual void EndRequestContent()
    {
        assert ( FALSE );
    }
    
    virtual const char* GetStatusLine(size_t* pStatusLineLength)
    {
        *pStatusLineLength = m_StatusLineSize;
        return m_pStatusLine;
    }
    
    virtual const char* GetResponseHeaders(size_t* pResponseHeaderLength)
    {
        *pResponseHeaderLength = m_HeaderSize;
        return m_pHeaders;
    }
    
    virtual int BeginResponseContent()
    {
        assert (m_pContent);
        
        // We have data to be sent back to client.
        m_pCurrent = m_pContent;
        return 1;
    }
    
    virtual size_t ReadResponseContentBlock(char*  pBuffer,
                                            size_t BytesToRead)
    {
        size_t BytesAlreadyRead = m_pCurrent - m_pContent;
        size_t BytesLeft = m_ContentSize - BytesAlreadyRead;
        
        if (BytesToRead > BytesLeft)
        {
            BytesToRead = BytesLeft;
        }
        
        memcpy(pBuffer, m_pCurrent, BytesToRead);
        m_pCurrent += BytesToRead;
        
        return BytesToRead;
    }
    
    virtual void EndResponseContent()
    {
    }
    
    virtual void Release()
    {
        delete this;
    }

protected:
    char*       m_pContent;
    const char* m_pCurrent;
    const char* m_pStatusLine;
    const char* m_pHeaders;
    size_t      m_ContentSize;
    size_t      m_StatusLineSize;
    size_t      m_HeaderSize;
};


// Request handler context which pulls content from file handle.
class CFileRequestHandlerContext : public IHTTPRequestHandlerContext
{
public:
    // Constructors / Destructors
    CFileRequestHandlerContext(FILE*       pFile, 
                               const char* pStatusLine,
                               size_t      StatusLineSize,
                               const char* pHeaders, 
                               size_t      HeaderSize)
    {
        m_pFile = pFile;
        m_pStatusLine = pStatusLine;
        m_StatusLineSize = StatusLineSize;
        m_pHeaders = pHeaders;
        m_HeaderSize = HeaderSize;
    }
    ~CFileRequestHandlerContext()
    {
        // Make sure that file handle isn't being leaked.
        assert ( !m_pFile );
        m_pHeaders = NULL;
        m_pStatusLine = NULL;
        m_HeaderSize = 0;
        m_StatusLineSize = 0;
    }
    
    // IHTTPRequestHandlerContext interface methods.
    virtual int  BeginRequestHeaders()
    {
        // Ignore request headers.
        return 0;
    }
    virtual void WriteRequestHeader(const char* pHeaderName, 
                                    const char* pHeaderValue)
    {
        (void)pHeaderName;
        (void)pHeaderValue;
        assert ( FALSE );
    }
    
    virtual void EndRequestHeaders()
    {
        assert ( FALSE );
    }
    
    virtual int BeginRequestContent(size_t ContentSize)
    {
        // Ignore request content body.
        (void)ContentSize;
        return 0;
    }
    
    virtual void WriteRequestContentBlock(const void* pBlockBuffer, 
                                          size_t BlockBufferSize)
    {
        (void)pBlockBuffer;
        (void)BlockBufferSize;
        assert ( FALSE );
    }
    
    virtual void EndRequestContent()
    {
        assert ( FALSE );
    }
    
    virtual const char* GetStatusLine(size_t* pStatusLineLength)
    {
        *pStatusLineLength = m_StatusLineSize;
        return m_pStatusLine;
    }
    
    virtual const char* GetResponseHeaders(size_t* pResponseHeaderLength)
    {
        *pResponseHeaderLength = m_HeaderSize;
        return m_pHeaders;
    }
    
    virtual int BeginResponseContent()
    {
        assert (m_pFile);
        
        // We have data to be sent back to client.
        return 1;
    }
    
    virtual size_t ReadResponseContentBlock(char*  pBuffer,
                                            size_t BytesToRead)
    {
        return fread(pBuffer, 1, BytesToRead, m_pFile);
    }
    
    virtual void EndResponseContent()
    {
        if (m_pFile)
        {
            fclose(m_pFile);
            m_pFile = NULL;
        }
    }
    
    virtual void Release()
    {
        EndResponseContent();
        delete this;
    }

protected:
    FILE*       m_pFile;
    const char* m_pStatusLine;
    const char* m_pHeaders;
    size_t      m_StatusLineSize;
    size_t      m_HeaderSize;
};


// Class to test application request handler overrides.
class CWindMeterRequestHandler : public IHTTPRequestHandler
{
public:
    // Constructors/Destructors
    CWindMeterRequestHandler();
    
    // IHTTPRequestHandler interface methods.
    virtual IHTTPRequestHandlerContext* HandleGetRequest(const char* pURI);
    virtual IHTTPRequestHandlerContext* HandleHeadRequest(const char* pURI);
    virtual IHTTPRequestHandlerContext* HandlePostRequest(const char* pURI);
    virtual IHTTPRequestHandlerContext* HandleBadRequest(const char* pRequest);
    
protected:
    IHTTPRequestHandlerContext* HandleRequest(const char* pURI);
    void MinuteTickerISR();
    
    CWeatherMeters  m_WeatherMeters;
    Ticker          m_MinuteTicker;
};


CWindMeterRequestHandler::CWindMeterRequestHandler()
    : m_WeatherMeters(p9, p10, p15)
{
    m_MinuteTicker.attach_us<CWindMeterRequestHandler>(this, &CWindMeterRequestHandler::MinuteTickerISR, 60000000);
}


void CWindMeterRequestHandler::MinuteTickerISR()
{
    CWeatherMeters::SMeasurements   Measurements;
    
    m_WeatherMeters.GetMeasurements(&Measurements);
}


IHTTPRequestHandlerContext* CWindMeterRequestHandler::HandleGetRequest(const char* pURI)
{
    return HandleRequest(pURI);
}

IHTTPRequestHandlerContext* CWindMeterRequestHandler::HandleHeadRequest(const char* pURI)
{
    return HandleRequest(pURI);
}

IHTTPRequestHandlerContext* CWindMeterRequestHandler::HandleRequest(const char* pURI)
{
    // Generate content for root home page.
    if (0 == strcmp(pURI, "/"))
    {
        CWeatherMeters::SMeasurements Measurements;
        
        CPrintfGetRequestHandlerContext* pContext = new CPrintfGetRequestHandlerContext(g_OkStatusLine,
                                                                                        sizeof(g_OkStatusLine)-1,
                                                                                        g_HTMLHeaders,
                                                                                        sizeof(g_HTMLHeaders)-1);
        if (!pContext)
        {
            return NULL;
        }
        
        m_WeatherMeters.GetMeasurements(&Measurements);
        pContext->printf(g_HomePageHTML, 
                         Measurements.WindSpeed, 
                         Measurements.MaximumWindSpeed, 
                         Measurements.WindDirectionString,
                         Measurements.Rainfall,
                         "? minutes");
        
        return pContext;
    }
    
    return NULL;
}

IHTTPRequestHandlerContext* CWindMeterRequestHandler::HandlePostRequest(const char* pURI)
{
    if (0 == strcmp(pURI, "/reset.html"))
    {
        FILE* pFile;
        
        m_WeatherMeters.Reset();

        pFile = fopen("/" ROOT_FILESYSTEM_NAME "/reset.html", "r");
        if (pFile)
        {
            static const char StatusLine[] = "HTTP/1.0 200 OK\r\n";
            static const char Headers[] = "Content-type: text/html\r\n";
                                            
            CFileRequestHandlerContext* pRequestHandlerContext = new CFileRequestHandlerContext(pFile,
                                                                                                StatusLine,
                                                                                                sizeof(StatusLine)-1,
                                                                                                Headers,
                                                                                                sizeof(Headers)-1);
            if (!pRequestHandlerContext)
            {
                fclose(pFile);
            }
            
            return pRequestHandlerContext;
        }
    }
    
    return NULL;
}

IHTTPRequestHandlerContext* CWindMeterRequestHandler::HandleBadRequest(const char* pRequest)
{
    // Unused parameters.
    (void)pRequest;
    
    return NULL;
}



int main() 
{
    int                                     Result = 1;
    DigitalOut                              ProgressLED(LED1);
    Timer                                   BlinkTimer;
    static SNetwork                         Network;
    static HTTP_MEM_POSITION CHTTPServer    HTTPServer;
    static CWindMeterRequestHandler         WindMeterRequestHandler;
    
    printf("\r\n" APPLICATION_NAME "\r\n");
    
    // Create the SD based file system.
    static SDFileSystem sd(p5, p6, p7, p8, "SD");

    // Initialize the ethernet driver and lwIP network stack.
    Result = SNetwork_Init(&Network, 
                           HTTP_STATIC_IP_ADDRESS, 
                           HTTP_STATIC_SUBNET_MASK,
                           HTTP_STATIC_GATEWAY_ADDRESS,
                           HTTP_HOST_NAME);
    if (Result)
    {
        error("Failed to initialize network.\r\n");
    }
    
    // Register test request handler for handling HTTP requests.
    HTTPServer.AttachRequestHandler(&WindMeterRequestHandler);

    // Setup a HTTP server to listen on port 80.
    Result = HTTPServer.Bind(ROOT_FILESYSTEM_NAME, HTTP_SERVER_NAME, HTTP_PORT);
    if (Result)
    {
        error("Failed to initialize for receiving HTTP requests.\r\n");
    }
                
    // Enter the main application loop.
    BlinkTimer.start();
    while(1) 
    {
        // Allow the network stack to do its thing.  It will callback into the
        // application callbacks as necessary to complete the actual work
        // required for a simple HTTP server implementation.
        SNetwork_Poll(&Network);
        
        // Blink the progress LED once each half second to let user know that
        // we haven't hung.
        if (BlinkTimer.read_ms() > 500)
        {
            BlinkTimer.reset();
            ProgressLED = !ProgressLED;
        }
    }
}