Web server based weather station using Sparkfun Weather Meters.
Dependencies: FatFileSystem mbed WeatherMeters SDFileSystem
Diff: main.cpp
- 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; + } + } +} + +