Web server based weather station using Sparkfun Weather Meters.

Dependencies:   FatFileSystem mbed WeatherMeters SDFileSystem

Revision:
0:616601bde9fb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Thu Feb 23 21:38:39 2012 +0000
@@ -0,0 +1,518 @@
+/* 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;
+        }
+    }
+}
+
+