Webserver only w/o any other functions, single thread. Running on STM32F013+W5500
Dependencies: NTPClient W5500Interface Watchdog device_configuration eeprom_flash mbed-rpc-nucleo mbed-rtos mbed
Fork of F103-Serial-to-Ethernet by
Revision 40:c966abbe2d62, committed 2016-06-16
- Comitter:
- olympux
- Date:
- Thu Jun 16 08:38:31 2016 +0000
- Parent:
- 39:083cf93121a9
- Child:
- 41:a50a534a2fbb
- Commit message:
- Working with a few issues:; - Added RPC AnalogIn, wdt reset when creating new object on PC_0; - Added creating RPC string but not tested yet
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Formatter.cpp Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,187 @@ +#include "Formatter.h" +#include "mbed.h" +#include "RPCObjectManager.h" +#include "EthernetInterface.h" + +const char *SIMPLE_HTML_CODE = "\ +<!DOCTYPE html>\ +<html>\ +<head>\ +<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\ +<title>TCP Server</title>\ +</head>\ + <body>"; + + +const char* INTERACTIVE_HTML_CODE_1 = "\ +<!DOCTYPE html> \ +<html>\ +<head>\ +<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\ +<title>TCP Server</title>\ +<script type=\"text/javascript\">\ +var ip = \"%s\";\ +function submitCreateForm()\ +{\ +var list = document.getElementById(\"type\");\ +var type = list.options[list.selectedIndex].value;\ +var name = document.getElementById(\"name\").value;\ +if(name === \"\") \ +return;\ +var arg = document.getElementById(\"arg\").value;\ +var url;\ +if(arg === \"\") url = \"http://\" + ip + type + \"new?name=\" + name;\ +else url = \"http://\" + ip + type + \"new?arg=\" + arg + \"&name=\" + name;\ +location.href= url;\ +}\ +function submitCallFuncForm()\ +{\ +var command = document.getElementById(\"command\").value;\ +if(command === \"\") \ +return; \ +var tmp = command.split(\' \');\ +var url = tmp[0];\ +if(tmp.length > 1)\ +url += \"?\";\ +for(var i = 1; i < tmp.length; ++i)\ +{\ +url += \"arg\" + i + \"=\" + tmp[i];\ +if(i+1 < tmp.length)\ +url += \"&\";\ +}\ +location.href = url;\ +}\ +</script>\ +</head> \ +<body>"; + +const char* INTERACTIVE_HTML_CODE_2 = "<h3>Create Object :</h3>\ +<form>\ +Type: <select id=\"type\">\ +<option value=\"/DigitalOut/\">DigitalOut</option>\ +<option value=\"/DigitalIn/\">DigitalIn</option>\ +<option value=\"/DigitalInOut/\">DigitalInOut</option>\ +<option value=\"/AnalogIn/\">AnalogIn</option>\ +<option value=\"/PwmOut/\">PwmOut</option>\ +<option value=\"/Timer/\">Timer</option>\ +</select><br>\ +name: <input type=\"text\" id=\"name\"><br>\ +arg(optional): <input type=\"text\" id=\"arg\">\ +<p><input type=\"button\" value=\"Create\" onclick=\"javascript:submitCreateForm();\"></p>\ +</form> \ + \ +<h3>Call a function :</h3>\ +<p>Enter an RPC command.</p>\ +<form>\ +Command: <input type= \"text\" id=\"command\" maxlength=127><br>\ +<p><input type=\"button\" value=\"Send\" onclick=\"javascript:submitCallFuncForm();\"></p><br>\ +</form>\ +</body> \ +</html>"; + +static char chunk[1024]; + +Formatter::Formatter(int nb): +currentChunk(0), +nbChunk(nb) +{ +} + +char* Formatter::get_page(char *reply) +{ + chunk[0] = '\0'; + + if(currentChunk < nbChunk) + { + get_chunk(currentChunk, reply); + currentChunk++; + } + else + currentChunk = 0; + + return chunk; +} + +void Formatter::get_chunk(const int c, char *reply) +{ + strcat(chunk, reply); +} + +SimpleHTMLFormatter::SimpleHTMLFormatter(): +Formatter() +{ +} + +void SimpleHTMLFormatter::get_chunk(const int c, char* reply) +{ + strcat(chunk, SIMPLE_HTML_CODE); + + if(reply != NULL && strlen(reply) != 0) + { + strcat(chunk, "RPC reply : "); + strcat(chunk, reply); + } + + if(!RPCObjectManager::instance().is_empty()) + { + strcat(chunk, "<ul>"); + for(std::list<char*>::iterator itor = RPCObjectManager::instance().begin(); + itor != RPCObjectManager::instance().end(); + ++itor) + { + strcat(chunk, "<li>"); + strcat(chunk, *itor); + strcat(chunk, "</li>"); + } + strcat(chunk, "</ul>"); + } + + strcat(chunk, "</body></html>"); +} + +InteractiveHTMLFormatter::InteractiveHTMLFormatter(): +Formatter(3) +{ +} + +void InteractiveHTMLFormatter::get_chunk(const int c, char *reply) +{ + if(c == 0) + //sprintf(chunk, INTERACTIVE_HTML_CODE_1, EthernetInterface::getIPAddress()); + sprintf(chunk, INTERACTIVE_HTML_CODE_1, eth.getIPAddress()); + + else if(c == 1) + { + if(reply != NULL && strlen(reply) != 0) + { + strcat(chunk, "RPC reply : "); + strcat(chunk, reply); + } + if(!RPCObjectManager::instance().is_empty()) + { + strcat(chunk, "<p>Objects created :</p>"); + + strcat(chunk, "<ul>"); + for(std::list<char*>::iterator itor = RPCObjectManager::instance().begin(); + itor != RPCObjectManager::instance().end(); + ++itor) + { + strcat(chunk, "<li>"); + strcat(chunk, *itor); + strcat(chunk, " (<a href=\"http://"); + //strcat(chunk, EthernetInterface::getIPAddress()); + strcat(chunk, eth.getIPAddress()); + strcat(chunk, "/"); + strcat(chunk, *itor); + strcat(chunk, "/delete\">delete</a>)"); + strcat(chunk, "</li>"); + } + strcat(chunk, "</ul>"); + } + strcat(chunk, " "); + } + else if(c == 2) + strcat(chunk, INTERACTIVE_HTML_CODE_2); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Formatter.h Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,51 @@ +#ifndef FORMATTER +#define FORMATTER + +#include "EthernetInterface.h" + +extern EthernetInterface eth; + +class Formatter +{ + public : + + Formatter(int nbChunk = 1); + + char* get_page(char *reply); + + protected : + + virtual void get_chunk(const int c, char *reply); + + private : + + int currentChunk; + int nbChunk; +}; + +class SimpleHTMLFormatter : public Formatter +{ + public : + + SimpleHTMLFormatter(); + + protected : + + virtual void get_chunk(const int c, char *reply); + +}; + +class InteractiveHTMLFormatter : public Formatter +{ + public : + + InteractiveHTMLFormatter(); + + protected : + + virtual void get_chunk(const int c, char *reply); +}; + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPServer.cpp Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,120 @@ +#include "HTTPServer.h" + +#define INVALID_FORMATTER "No valid formatter specified" + +bool cmp(char* a, char* b) +{ + return strcmp(a,b) < 0; +} + +HTTPServer::HTTPServer(Formatter *f): +socket(), +handlers(&cmp), +formatter(f), +reply(), +command() +{ +} + +HTTPServer::~HTTPServer() +{ + for(std::map<char*, RequestHandler*>::iterator itor = handlers.begin(); + itor != handlers.end(); + ++itor) + delete itor->second; + + if(formatter) + delete formatter; +} + +bool HTTPServer::init(int port) +{ + socket.set_blocking(true); + if(socket.bind(port)) + { + printf("Could not bind on port %d.\n", port); + return false; + } + + if(socket.listen()) + { + printf("Could not listen %d\n", port); + return false; + } + + return true; +} + +void HTTPServer::run() +{ + TCPSocketConnection c; + while(true) + { + while(socket.accept(c)); + c.set_blocking(false, 1000); + while(c.is_connected()) + { + char buffer[512]; + int n = c.receive_all(buffer, sizeof(buffer)-1); + if(n == 0) + { + c.close(); + break; + } + else if(n != -1) + { + printf("Received data\n"); + buffer[n] = '\0'; + handle_request(buffer); + if(formatter != NULL) + { + printf("Sending data..."); + char *page = formatter->get_page(reply); + do + { + c.send(page, strlen(page)+1); + page = formatter->get_page(reply); + }while(strlen(page)>0); + printf("done\n"); + } + else + c.send(INVALID_FORMATTER, strlen(INVALID_FORMATTER)+1); + } + else + printf("Error while receiving data\n"); + } + } +} + +void HTTPServer::handle_request(char *buffer) +{ + char *request_type = strtok(buffer, " "); + char *request = strtok(NULL, " "); + + reply[0] = '\0'; + if(!strcmp(request, "/")) + return; + + if(!command.decode(request)) + { + strcat(reply, "Malformed request"); + return; + } + + std::map<char*, RequestHandler*>::iterator itor = handlers.find(request_type); + if(itor == handlers.end()) + { + strcat(reply, "No request handler found for this type of request."); + return; + } + if(itor->second != NULL) + itor->second->handle(command, reply); + else + strcat(reply, "Invalid request handler"); +} + +void HTTPServer::add_request_handler(char *name, RequestHandler* handler) +{ + handlers[name] = handler; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPServer.h Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,39 @@ +#ifndef HTTP_SERVER +#define HTTP_SERVER + +#include <map> + +#include "mbed.h" +#include "mbed_rpc.h" +#include "RequestHandler.h" +#include "Formatter.h" +#include "EthernetInterface.h" +#include "RPCCommand.h" + + +class HTTPServer +{ + public : + + HTTPServer(Formatter *f = new Formatter()); + virtual ~HTTPServer(); + + bool init(int port); + + void run(); + + void add_request_handler(char *name, RequestHandler* handler); + + private : + + void handle_request(char *buffer); + + TCPSocketServer socket; + std::map<char*, RequestHandler*, bool(*)(char*, char*)> handlers; + Formatter *formatter; + char reply[RPC_MAX_STRING]; + RPCCommand command; +}; + +#endif +
--- a/README.md Tue Jun 14 21:25:04 2016 +0000 +++ b/README.md Thu Jun 16 08:38:31 2016 +0000 @@ -1,102 +1,12 @@ -# Features - -Forked of F103_NNIO_RPC rev36:3055e4 -Firmware for NNIO modules based on STM32F103RBT6 and W5500. - -- TCP/UDP server for controlling and monitoring using NNIO v2.0 and RPC protocols. -- UDP server for discovering and configuring. - -# Usage - -## Set RTC for timed Digital Ouputs - -Use TCP client or UDP to send configuration to the module: - -Set the current_time variable - -``` -/Time/write abcdef -``` - -where abcdef is time from 00:00am in seconds. - -Run RPC SetTime() function to execute set_current_time() to set current time to the current_time variable +Forked of RPC over HTTP server -``` -/SetTime/run x -``` - -## Set On/Off time for Digital Outputs - -``` -/do0OnTime/write abcdef -/do0OffTime/write abcdef -``` - -where abcdef is time in seconds, start from 00:00am. Check it by sending to read - -``` -/do0OnTime/read x -/do0OffTime/read x -``` - - -# Releases - -## v2.0.0 (04/06/2016) - -New features +------------------ +Modified but not commit yet: -- RPCVariable -- RPCFunction - -Improvements - -- Removed private mbed-rpc -- Updated to latest mbed-rtos, NTPClient, Watchdog, W5500Interface and mbed. - -Bug fixes - -- Had to compile from source as eeprom library failed when using the compiled mbed library - -## v1.1.1 (07/02/2015) - -Improvements - -- Updated RPC names - -## v1.1 (24/01/2015) - -New features - -- Process RPC-style command in tcp client, similar to TCP/UDP server. - -Improvements - -- Control command is able to be processed by both TCP and UDP. -- Use only one network output buffer for both RPC-style and NNIO protocols. -- Automatically reset after setting network configuration or setting TCP server for auto update mode. -- RPC object name is 16 chars max. - - -## v1.0 (06/01/2014) - -New features - -- RPC command replies as following object_name:reply_value - -Improvements - -- Modified: clean code in my_eeprom_funcs and main.cpp - -## v0.1 (29/12/2014) - -Initial - -- Imported F103_NNIO rev27:22f289beceb8 -- process_control_command() with return value. 0 if NNIO protocol or RPC protocol without reply; length of RPC outbut buffer; or -1 if RPC failed. -- TCP server now checks to return data to client. -- use device description instead of device config code in Discovery command. -- working with ConfigurationTool v2.0 and AlarmMonitoring v1.1. - - +1. Updated Formatter.c to use eth.getIPAddress() +2. Added AnalogIn + - to HTML code in Formatter.c + - to RPCType.c + +Tested working but creating RPC object AnalogIn at PC_0 caused watchdog resets +------------------- \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RPCCommand.cpp Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,130 @@ +#include "RPCCommand.h" +#include "mbed.h" +#include "RPCType.h" + + +RPCCommand::RPCCommand(): +cmd(), +obj_name(NULL), +func_name(NULL) +{ + +} + +bool RPCCommand::decode(char *buffer) +{ + if(buffer == NULL) + return false; + if(buffer[0] != '/') + return false; + + ++buffer; + char *tmp = strchr(buffer ,'/'); + + if(tmp == NULL) + return false; + if(tmp == buffer) + return false; + + tmp[0] = '\0'; + obj_name = buffer; + + buffer = tmp+1; + + if(buffer[0] == '\0' || buffer[0] == '?') + return false; + + func_name = buffer; + + tmp = strchr(buffer, '?'); + if(tmp != NULL) + { + if(tmp[1] == '\0') + return false; + tmp[0] = '\0'; + } + + cmd[0] = '\0'; + strcat(cmd, "/"); + strcat(cmd, obj_name); + strcat(cmd, "/"); + strcat(cmd, func_name); + + if(tmp == NULL) + return true; + + buffer = tmp+1; + do + { + tmp = strchr(buffer, '&'); + + if(tmp != NULL) + { + if(tmp[1] == '\0' || buffer == tmp) + return false; + tmp[0] = '\0'; + } + + char *sep = strchr(buffer, '='); + if(sep == NULL) + return false; + if(sep == buffer) + return false; + if(sep[1] == '\0' || sep[1] == '&') + return false; + + strcat(cmd, " "); + strcat(cmd, sep+1); + + if(tmp != NULL) + buffer = tmp+1; + else + buffer = NULL; + }while(buffer); + + return true; +} + + + +char* RPCCommand::get_cmd() const +{ + return (char*)cmd; +} + +RPC_COMMAND_TYPE RPCCommand::get_type() const +{ + if(!strcmp(func_name, "new") && RPCType::instance().is_supported_type(obj_name)) + return CREATE; + + RPC* r = RPC::lookup(obj_name); + if(r == NULL) + return INVALID; + + if(!strcmp(func_name, "delete")) + return DELETE; + + const struct rpc_method *methods = r->get_rpc_methods(); + int i = 0; + while(methods[i].name != NULL) + { + if(!strcmp(func_name, methods[i].name)) + { + return FUNCTION_CALL; + } + ++i; + } + + return INVALID; +} + +char* RPCCommand::get_obj_name() const +{ + return obj_name; +} + +char* RPCCommand::get_func_name() const +{ + return func_name; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RPCCommand.h Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,36 @@ +#ifndef RPCCOMMAND +#define RPCCOMMAND + +#include <list> +#include "mbed_rpc.h" + +enum RPC_COMMAND_TYPE { INVALID, CREATE, DELETE, FUNCTION_CALL }; + +struct rpc_arg +{ + char *name; + char *val; +}; + +class RPCCommand +{ + public : + + RPCCommand(); + + bool decode(char *buffer); + + char* get_cmd() const; + RPC_COMMAND_TYPE get_type() const; + char* get_obj_name() const; + char* get_func_name() const; + + private : + + char cmd[RPC_MAX_STRING]; + char* obj_name; + char* func_name; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RPCObjectManager.cpp Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,68 @@ +#include "RPCObjectManager.h" +#include "mbed.h" + +RPCObjectManager& RPCObjectManager::instance() +{ + static RPCObjectManager om; + return om; +} + +RPCObjectManager::RPCObjectManager(): +objects() +{ +} + +RPCObjectManager::~RPCObjectManager() +{ + for(std::list<char*>::iterator itor = objects.begin(); + itor != objects.end(); + ++itor) + delete *itor; +} + +void RPCObjectManager::store_object(char *obj_name) +{ + char *obj = new char[strlen(obj_name)+1]; + strcpy(obj, obj_name); + obj[strlen(obj_name)] = '\0'; + objects.push_back(obj); +} + +void RPCObjectManager::remove_object(char *obj_name) +{ + for(std::list<char*>::iterator itor = objects.begin(); + itor != objects.end(); + ++itor) + if(!strcmp(obj_name, *itor)) + { + delete *itor; + objects.erase(itor); + break; + } +} + +bool RPCObjectManager::lookup_object(char *obj_name) +{ + for(std::list<char*>::iterator itor = objects.begin(); + itor != objects.end(); + ++itor) + if(!strcmp(obj_name, *itor)) + return true; + return false; +} + +bool RPCObjectManager::is_empty() +{ + return objects.empty(); +} + +std::list<char*>::iterator RPCObjectManager::begin() +{ + return objects.begin(); +} + +std::list<char*>::iterator RPCObjectManager::end() +{ + return objects.end(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RPCObjectManager.h Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,29 @@ +#ifndef RPCOBJECTMANAGER +#define RPCOBJECTMANAGER + +#include <list> + +class RPCObjectManager +{ + public : + + static RPCObjectManager& instance(); + + void store_object(char *obj_name); + void remove_object(char *obj_name); + bool lookup_object(char *obj_name); + + std::list<char*>::iterator begin(); + std::list<char*>::iterator end(); + + bool is_empty(); + + private : + + RPCObjectManager(); + ~RPCObjectManager(); + + std::list<char*> objects; +}; +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RPCType.cpp Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,59 @@ +#include "mbed.h" +#include "mbed_rpc.h" +#include "RPCType.h" + + +RPCType::RPCType(): +supported_types() +{ +} + +RPCType& RPCType::instance() +{ + static RPCType t; + return t; +} + +void RPCType::register_types() +{ + RPCType &t = instance(); + + RPC::add_rpc_class<RpcDigitalOut>(); + t.supported_types.push_back("DigitalOut"); + RPC::add_rpc_class<RpcDigitalIn>(); + t.supported_types.push_back("DigitalIn"); + RPC::add_rpc_class<RpcDigitalInOut>(); + t.supported_types.push_back("DigitalInOut"); + + #if DEVICE_ANALOGIN + RPC::add_rpc_class<RpcAnalogIn>(); + t.supported_types.push_back("AnalogIn"); + #endif + #if DEVICE_PWMOUT + RPC::add_rpc_class<RpcPwmOut>(); + t.supported_types.push_back("PwmOut"); + #endif + #if DEVICE_SPI + t.supported_types.push_back("SPI"); + RPC::add_rpc_class<RpcSPI>(); + #endif + #if DEVICE_SERIAL + t.supported_types.push_back("Serial"); + RPC::add_rpc_class<RpcSerial>(); + #endif + RPC::add_rpc_class<RpcTimer>(); + t.supported_types.push_back("Timer"); +} + +bool RPCType::is_supported_type(char *type) +{ + for(std::list<char*>::iterator itor = instance().supported_types.begin(); + itor != instance().supported_types.end(); + ++itor) + if(!strcmp(*itor,type)) + return true; + + return false; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RPCType.h Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,23 @@ +#ifndef RPCTYPE_H +#define RPCTYPE_H + +#include <list> + +class RPCType +{ + public : + + static RPCType& instance(); + + void register_types(); + + bool is_supported_type(char *type); + + private : + + RPCType(); + std::list<char*> supported_types; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RequestHandler.cpp Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,109 @@ +#include "RequestHandler.h" +#include "mbed_rpc.h" +#include "RPCObjectManager.h" +#include "RPCCommand.h" + +const char* INVALID_CMD = "Invalid RPC command"; +const char* DELETE_ERROR = "You must send a DELETE request to remove an object "; +const char* CREATE_ERROR = "You must send a PUT request to create an object"; +const char* FUNC_CALL_ERROR = "You must send a GET request to call a function"; + +void GetRequestHandler::handle(const RPCCommand& cmd, char *reply) +{ + switch(cmd.get_type()) + { + case DELETE: + printf("Error: %s\n", DELETE_ERROR); + strcat(reply, DELETE_ERROR); + break; + case FUNCTION_CALL: + RPC::call(cmd.get_cmd(), reply); + break; + case CREATE: + printf("Error: %s\n", CREATE_ERROR); + strcat(reply, CREATE_ERROR); + break; + default: + printf("Error: %s\n", INVALID_CMD); + strcat(reply, INVALID_CMD); + break; + } +} + +void PutRequestHandler::handle(const RPCCommand& cmd, char *reply) +{ + switch(cmd.get_type()) + { + case DELETE: + printf("Error: %s\n", DELETE_ERROR); + strcat(reply, DELETE_ERROR); + break; + case FUNCTION_CALL: + printf("Error: %s\n", FUNC_CALL_ERROR); + strcat(reply, FUNC_CALL_ERROR); + break; + case CREATE: + RPC::call(cmd.get_cmd(), reply); + if(strlen(reply) > 0) + { + RPCObjectManager::instance().store_object(reply); + strcat(reply, " has been created"); + } + else + { + printf("Error while creating object\n"); + strcat(reply, "Error while creating object."); + } + break; + default: + printf("Error: %s\n", INVALID_CMD); + strcat(reply, INVALID_CMD); + break; + } +} + +void DeleteRequestHandler::handle(const RPCCommand& cmd, char *reply) +{ + switch(cmd.get_type()) + { + case CREATE: + printf("Error: %s\n", CREATE_ERROR); + strcat(reply, CREATE_ERROR); + break; + case FUNCTION_CALL: + printf("Error: %s\n", FUNC_CALL_ERROR); + strcat(reply, FUNC_CALL_ERROR); + break; + case DELETE: + RPC::call(cmd.get_cmd(), reply); + RPCObjectManager::instance().remove_object(cmd.get_obj_name()); + strcat(reply, "Deleted object "); + strcat(reply, cmd.get_obj_name()); + break; + default: + printf("Error: %s\n", INVALID_CMD); + strcat(reply, INVALID_CMD); + break; + } +} + +void ComplexRequestHandler::handle(const RPCCommand& cmd, char *reply) +{ + switch(cmd.get_type()) + { + case CREATE : + putHandler.handle(cmd, reply); + break; + case DELETE : + deleteHandler.handle(cmd, reply); + break; + case FUNCTION_CALL : + getHandler.handle(cmd, reply); + break; + default : + printf("Error: %s\n", INVALID_CMD); + strcat(reply, INVALID_CMD); + break; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RequestHandler.h Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,51 @@ +#ifndef REQUEST_HANDLER +#define REQUEST_HANDLER + +#include "RPCCommand.h" + +class RequestHandler +{ + public : + + virtual void handle(const RPCCommand& cmd, char* reply) = 0; +}; + +class GetRequestHandler : public RequestHandler +{ + public : + + virtual void handle(const RPCCommand& cmd, char* reply); +}; + +class PutRequestHandler : public RequestHandler +{ + public : + + virtual void handle(const RPCCommand& cmd, char* reply); + +}; + + +class DeleteRequestHandler : public RequestHandler +{ + public : + + virtual void handle(const RPCCommand& cmd, char* reply); + +}; + +class ComplexRequestHandler : public RequestHandler +{ + public : + + virtual void handle(const RPCCommand& cmd, char* reply); + + private : + + GetRequestHandler getHandler; + PutRequestHandler putHandler; + DeleteRequestHandler deleteHandler; +}; + +#endif +
--- a/main.cpp Tue Jun 14 21:25:04 2016 +0000 +++ b/main.cpp Thu Jun 16 08:38:31 2016 +0000 @@ -13,6 +13,14 @@ #include "my_eeprom_funcs.h" #include "Watchdog.h" +#include "RPCCommand.h" +#include "HTTPServer.h" +#include "Formatter.h" +#include "RequestHandler.h" +#include "RPCType.h" + +#define SERVER_PORT 80 + /** Debug option * @@ -37,6 +45,7 @@ // Ethernet SPI spi(PA_7, PA_6, PA_5); // mosi, miso, sclk EthernetInterface eth(&spi, PA_4, PC_9); // spi, cs, reset +int ethernet_init(void); /* * EEPROM section @@ -63,33 +72,6 @@ /* -* Network configuration -*/ -#define TCP_SERVER -#define TCP_CLIENT -#define UDP_SERVER -//#define UDP_CLIENT -#define NTP - -#define TCP_SERVER_WAIT_CLIENT_TIMEOUT 200 // timeout for local tcp server wait for a remote client -#define TCP_SERVER_RECEIVE_TIMEOUT 2000 // timeout for local tcp server wait to receive from remote client -#define TCP_CLIENT_RECEIVE_TIMEOUT 200 // timeout for local tcp client try to connect remote server -#define UDP_SERVER_RECEIVE_TIMEOUT 100 // timeout for checking config command - - -// TCP server function -TCPSocketServer tcp_server; -TCPSocketConnection tcp_client; -// TCP client function -TCPSocketConnection tcp_sock; -// UDP server -UDPSocket udp_server; -Endpoint ep_udp_client; -// NTP -NTPClient ntp; - - -/* * Variables for network configuration, TCP server */ uint8_t u8mac[6], u8ip_addr[4];// keep mac and ip address in 8-bits @@ -109,191 +91,21 @@ uint16_t u16tcp_server_port; // directly loaded from eeprom uint16_t u16enable_tcp_client, u16enable_tcp_server;// flags for enabling TCP client or TCP server -#define NET_BUF_LEN 256 -char tcp_receiving_buffer[NET_BUF_LEN]; -char udp_receiving_buffer[NET_BUF_LEN]; -char network_output_buffer[NET_BUF_LEN]; // output buffer for TCP/UDP control command - -/* - * RPC Protocol - * Use the RPC enabled wrapped class - see RpcClasses.h for more info - */ -// DigitalIn -RpcDigitalIn di0(PB_14, "di0"); -RpcDigitalIn di1(PB_12, "di1"); -RpcDigitalIn di2(PB_10, "di2"); -RpcDigitalIn di3(PB_1, "di3"); -RpcDigitalIn di4(PB_15, "di4"); -RpcDigitalIn di5(PB_13, "di5"); -RpcDigitalIn di6(PB_11, "di6"); -RpcDigitalIn di7(PB_2, "di7"); -DigitalIn din0(PB_14); -DigitalIn din1(PB_12); -DigitalIn din2(PB_10); -DigitalIn din3(PB_1); -DigitalIn din4(PB_15); -DigitalIn din5(PB_13); -DigitalIn din6(PB_11); -DigitalIn din7(PB_2); -// DigitalOut -RpcDigitalOut do0(PB_3, "do0"); -RpcDigitalOut do1(PB_5, "do1"); -RpcDigitalOut do2(PB_7, "do2"); -RpcDigitalOut do3(PB_9, "do3"); -RpcDigitalOut do4(PD_2, "do4"); -RpcDigitalOut do5(PB_4, "do5"); -RpcDigitalOut do6(PB_6, "do6"); -RpcDigitalOut do7(PB_8, "do7"); -DigitalOut dout0(PB_3); -DigitalOut dout1(PB_5); -DigitalOut dout2(PB_7); -DigitalOut dout3(PB_9); -DigitalOut dout4(PD_2); -DigitalOut dout5(PB_4); -DigitalOut dout6(PB_6); -DigitalOut dout7(PB_8); -// AnalogIn -RpcAnalogIn adc10(PC_0, "ai0"); // adc10 -RpcAnalogIn adc11(PC_1, "ai1"); // adc11 -AnalogIn ain0(PC_0); -AnalogIn ain1(PC_1); -// AnalogOut, PWM -RpcPwmOut pwm11(PA_8, "pwm0"); // pwm11 -RpcPwmOut pwm21(PA_15, "pwm1"); // pwm21 // Serial -RpcSerial usart2(USBTX, USBRX, "uart"); // usart2 Serial uart(USBTX,USBRX); -// Timer -RpcTimer timer1("tmr1"); // Watchdog Watchdog wdt; - -// Some variable types that can be modified through RPC. -//int wheelsOn; -//char lcdBannerMessage; -//float speed; -int current_time; -int do0OnTime, do0OffTime; -int do1OnTime, do1OffTime; - -//RPCVariable<int> rpcLights(&wheelsOn, "wheels"); -//RPCVariable<char> rpcBanner(&lcdBannerMessage, "banner"); -//RPCVariable<float> rpcSpeed(&speed, "speed"); -RPCVariable<int> rpcCurrentTime(¤t_time, "Time"); -RPCVariable<int> rpcdo0OnTime(&do0OnTime, "do0OnTime"); -RPCVariable<int> rpcdo0OffTime(&do0OffTime, "do0OffTime"); -RPCVariable<int> rpcdo1OnTime(&do0OnTime, "do1OnTime"); -RPCVariable<int> rpcdo1OffTime(&do0OffTime, "do1OffTime"); - -// RPC function definitions -// Create a function of the required format -void set_current_time(Arguments* args, Reply* rep); -void set_current_time(Arguments* args, Reply* rep){ - time_t ct = (time_t)current_time; // convert - struct tm *st = localtime(&ct); - - set_time(ct); // set time - - DBG("Set current time to: %s", ctime(&ct)); - DBG("Time only: %d:%d:%d", st->tm_hour, st->tm_min, st->tm_sec); -} -// Attach it to an RPC object -RPCFunction rpcSetCurrentTime(&set_current_time, "SetTime"); - -/* - * NNIO Protocol - */ -// Commands -#define DEVICE_DESCRIPTION "NNIO" -#define DEVICE_CONFIG_CODE "NNCF" -#define DEVICE_CONTROL_CODE "NNIO" - -#define RECEIVING_PROTOCOL_LENGTH 58 -#define SENDING_PROTOCOL_LENGTH 39 -#define QUERY_NETWORK_CONFIG_CMD_LENGTH 6 -#define SET_NETWORK_CONFIG_CMD_LENGTH 19 -#define UPDATE_TCP_SERVER_INFO_COMMAND_LENGTH 12 - -#define QUERY_DISCOVERY_CMD "NNCFDS" -#define QUERY_IP_CMD "NNCFIP" -#define QUERY_SUBNET_CMD "NNCFSN" -#define QUERY_GATEWAY_CMD "NNCFGW" -#define QUERY_MAC_CMD "NNCFMC" -#define QUERY_UDP_PORT_CMD "NNCFUP" -#define QUERY_TCP_PORT_CMD "NNCFTP" -#define QUERY_UPDATE_TIME_CMD "NNCFTM" -#define RECEIVING_PROTOCOL_ENABLE_OUTPUT 'O' -#define QUERY_STATUS_COMMAND 'Q' -#define DIGITAL_HIGH 'H' -#define DIGITAL_LOW 'L' - - -// Positions -#define RECEIVING_PROTOCOL_ID_POS 0 -#define RECEIVING_PROTOCOL_OP_POS 4 -#define RECEIVING_PROTOCOL_EN_DO_POS RECEIVING_PROTOCOL_OP_POS + 0 -#define RECEIVING_PROTOCOL_EN_A0O_POS RECEIVING_PROTOCOL_OP_POS + 1 -#define RECEIVING_PROTOCOL_EN_A1O_POS RECEIVING_PROTOCOL_OP_POS + 2 -#define RECEIVING_PROTOCOL_EN_UART_POS RECEIVING_PROTOCOL_OP_POS + 3 -#define RECEIVING_PROTOCOL_COMMAND_POS RECEIVING_PROTOCOL_OP_POS + 4 - -#define RECEIVING_PROTOCOL_IP_POS 9 -#define RECEIVING_PROTOCOL_DO_POS 13 -#define RECEIVING_PROTOCOL_A0O_POS 21 -#define RECEIVING_PROTOCOL_A01_POS 23 -#define RECEIVING_PROTOCOL_UART_POS 25 - - -#define SENDING_PROTOCOL_ID_POS 0 -#define SENDING_PROTOCOL_MAC_POS 4 -#define SENDING_PROTOCOL_IP_POS 10 -#define SENDING_PROTOCOL_DI_POS 14 -#define SENDING_PROTOCOL_DO_POS 22 -#define SENDING_PROTOCOL_AI0_POS 30 -#define SENDING_PROTOCOL_AI1_POS 32 -#define SENDING_PROTOCOL_AO0_POS 34 -#define SENDING_PROTOCOL_AO1_POS 36 -#define SENDING_PROTOCOL_CR_POS 38 - - -/* -* RTOS -*/ -struct message_t { - int len; - char *msg; -}; -Queue<message_t, 16> uart_queue; -Queue<bool, 1> auto_update_queue; - - - -// Prototypes -int ethernet_init(void); -int process_control_command(char* received_buffer, int len); -void process_config_command(char* received_buffer, int len); -void update_digital_outputs(char* buf); -void update_sending_frame(char* buf); +float speed; +RPCVariable<float> rpcSpeed(&speed, "speed"); +char stripaddr[20]; +RPCVariable<char*> rpcIPAddress(stripaddr, "ipaddr"); /* * Threads */ -// Timer thread for auto update in TCP client function -void auto_update_timer_thread(void const* args) -{ - bool update_flag = true; - - Thread::wait(500); - while(true) { - auto_update_queue.put(&update_flag); - Thread::wait(1000*transmit_time_period); // Thread::wait() in ms - } -} - - // WDT reset void wdt_reset_thread(void const* args) { @@ -301,104 +113,21 @@ wdt.Service(); } -// Timer thread to check on/off time -void digital_outputs_timer_thread(void const* args) + +HTTPServer create_simple_server() +{ + HTTPServer srv; + srv.add_request_handler("DELETE", new DeleteRequestHandler()); + srv.add_request_handler("GET", new GetRequestHandler()); + srv.add_request_handler("PUT", new PutRequestHandler()); + return srv; +} + +HTTPServer create_interactive_server() { - Thread::wait(700); - - while(true) { - // read current time - time_t seconds = time(NULL); - struct tm *st = localtime(&seconds); - int current_time_in_seconds = 3600*(st->tm_hour) + 60*(st->tm_min) + st->tm_sec; - DBG("Current time: %d:%d:%d", st->tm_hour, st->tm_min, st->tm_sec); - - // check do0 - if (do0OnTime < do0OffTime) { - if ((current_time_in_seconds >= do0OnTime) && (current_time_in_seconds < do0OffTime)){ - if (dout0 == 0) { - dout0 = 1; - DBG("do0 ON"); - } - else { - DBG("do0 ON'ed"); - } - } - else { - if (dout0 == 1) { - dout0 = 0; - DBG("do0 OFF'ed"); - } - else { - DBG("do0 OFF"); - } - } - } - else { - if ((current_time_in_seconds >= do0OffTime) && ((current_time_in_seconds < do0OnTime))) { - if (dout0 == 1) { - dout0 = 0; - DBG("do0 OFF"); - } - else { - DBG("do0 OFF'ed"); - } - } - else { - if (dout0 == 0) { - dout0 = 1; - DBG("do0 ON"); - } - else { - DBG("do0 ON'ed"); - } - } - } - - // check do1 - if (do1OnTime < do1OffTime) { - if ((current_time_in_seconds >= do1OnTime) && (current_time_in_seconds < do1OffTime)){ - if (dout1 == 0) { - dout1 = 1; - DBG("do1 ON"); - } - else { - DBG("do1 ON'ed"); - } - } - else { - if (dout1 == 1) { - dout1 = 0; - DBG("do1 OFF"); - } - else { - DBG("do1 OFF'ed"); - } - } - } - else { - if ((current_time_in_seconds >= do1OffTime) && ((current_time_in_seconds < do1OnTime))) { - if (dout1 == 1) { - dout1 = 0; - DBG("do1 OFF"); - } - else { - DBG("do1 OFF'ed"); - } - } - else { - if (dout1 == 0) { - dout1 = 1; - DBG("do1 ON"); - } - else { - DBG("do1 ON'ed"); - } - } - } - // wait 1s - Thread::wait(10000); // Thread::wait() in ms - } + HTTPServer srv(new InteractiveHTMLFormatter()); + srv.add_request_handler("GET", new ComplexRequestHandler()); + return srv; } // Main code @@ -428,9 +157,10 @@ /* * UI threads */ - Thread t2(auto_update_timer_thread); Thread t3(wdt_reset_thread); - Thread t4(digital_outputs_timer_thread); + + // rpc + RPCType::instance().register_types(); /* * Ethernet @@ -442,400 +172,23 @@ } Thread::wait(2000); // TCP/UDP stack delay - - /* - * UDP server - * TCP server/client - */ -#ifdef UDP_SERVER - ret = udp_server.bind(udp_server_local_port); - DBG("UDP server started (sock.bind = %d)...", ret); - udp_server.set_blocking(false, UDP_SERVER_RECEIVE_TIMEOUT); -#endif - -#ifdef TCP_SERVER - tcp_server.bind(tcp_server_local_port); - tcp_server.listen(); - DBG("TCP server started..."); - tcp_server.set_blocking(false, TCP_SERVER_WAIT_CLIENT_TIMEOUT); -#endif - -#ifdef TCP_CLIENT - -#endif - - /* - * Network loop processor - */ - while (true) { -#ifdef TCP_CLIENT // auto update device status to a remote TCP server - if (auto_transmit_flag == DEFAULT_ENABLE_FLAG_VALUE) { - // connect to TCP server if required - if (!tcp_sock.is_connected()) { - ret = tcp_sock.connect(str_server_ip_addr, u16tcp_server_port); // timeout is default in connect() in W5500.h - if (ret > -1) { - DBG("Successfully connected to %s on port %d", str_server_ip_addr, u16tcp_server_port); - } else { - ERR("Unable to connect to %s on port %d", str_server_ip_addr, u16tcp_server_port); - } - } - - // transmit data if connected - if (tcp_sock.is_connected()) { - osEvent evt = auto_update_queue.get(1); // timeout after 1ms - if (evt.status == osEventMessage) { - DBG("Updating..."); - update_sending_frame(network_output_buffer); - tcp_sock.send_all(network_output_buffer, SENDING_PROTOCOL_LENGTH); - } - - // check to receive or timeout - tcp_sock.set_blocking(false, TCP_CLIENT_RECEIVE_TIMEOUT); - n = tcp_sock.receive(tcp_receiving_buffer, sizeof(tcp_receiving_buffer)); - if (n > 0) { - // got some data, test it - DBG("TCP client received %d bytes: %s", n, tcp_receiving_buffer); - n = process_control_command(tcp_receiving_buffer, n); - // send reply back to client, NNIO protocol always returns 0 - // RPC-style protocol - if (n > 0) { - network_output_buffer[n] = '\0'; - tcp_sock.send_all(network_output_buffer, strlen(network_output_buffer)); - } // RPC-style protocol - else if (n == 0) { - // then, check query status command and sending protocol if required - if (tcp_receiving_buffer[RECEIVING_PROTOCOL_COMMAND_POS] == QUERY_STATUS_COMMAND) { - DBG("Requested to send device status through TCP"); - // sending protocol - update_sending_frame(network_output_buffer); - tcp_sock.send_all(network_output_buffer, SENDING_PROTOCOL_LENGTH); - DBG("Sent"); - } - } // NNIO protocol - } - } - } // if tcp client enabled && auto transmit -#endif - - -#ifdef TCP_SERVER // control and monitor from a remote TCP client, both NNIO and RPC-style - // no tcp client connected{ - if (1) { - // wait for client within timeout - ret = tcp_server.accept(tcp_client); - - // tcp client connected - if (ret > -1) { - DBG("Connection from: %s", tcp_client.get_address()); - - // loop waiting and receiving data within timeout - tcp_client.set_blocking(false, TCP_SERVER_RECEIVE_TIMEOUT); // Timeout after x seconds - while (tcp_client.is_connected()) { - n = tcp_client.receive(tcp_receiving_buffer, sizeof(tcp_receiving_buffer)); - if (n <= 0) break; - - // got some data, process it - tcp_receiving_buffer[n] = '\0'; // for debugging purpose - DBG("TCP server received: %s", tcp_receiving_buffer); - n = process_control_command(tcp_receiving_buffer, n); - // send reply back to client, NNIO protocol always returns 0 - // RPC-style protocol - if (n > 0) { - network_output_buffer[n] = '\0'; - tcp_client.send_all(network_output_buffer, strlen(network_output_buffer)); - } // RPC-style protocol - else if (n == 0) { - // then, check query status command and sending protocol if required - if (tcp_receiving_buffer[RECEIVING_PROTOCOL_COMMAND_POS] == QUERY_STATUS_COMMAND) { - DBG("Requested to send device status through TCP"); - // sending protocol - update_sending_frame(network_output_buffer); - tcp_client.send_all(network_output_buffer, SENDING_PROTOCOL_LENGTH); - DBG("Sent"); - } - } // NNIO protocol - } // end loop if no data received within timeout - } // if client connected - tcp_client.close(); - } // if tcp server enabled && no client connected -#endif - -#ifdef UDP_SERVER // configuration and control, both NNIO and RPC-style - bool discovery_mode_flag, config_mode_flag; - - n = udp_server.receiveFrom(ep_udp_client, udp_receiving_buffer, sizeof(udp_receiving_buffer)); - - // check to see if it is a query command - // if yes, is it a discovery command or an ip query to enter config mode - discovery_mode_flag = false; - config_mode_flag = false; - if ((n == QUERY_NETWORK_CONFIG_CMD_LENGTH) && (strstr(udp_receiving_buffer, QUERY_DISCOVERY_CMD) != NULL)) { - discovery_mode_flag = true; - DBG("Received discovery command"); - char str[50]; - sprintf(str, "%s%s%s", DEVICE_DESCRIPTION, eth.getMACAddress(), eth.getIPAddress()); - udp_server.sendTo(ep_udp_client, str, strlen(str)); - } // NNCFDS - else if ((n == QUERY_NETWORK_CONFIG_CMD_LENGTH) && (strstr(udp_receiving_buffer, QUERY_IP_CMD) != NULL)) { - config_mode_flag = true; - DBG("Entered configuration mode..."); - DBG("!!! RESET when finished"); - } // NNCFIP + + // Test parsing ip address string + char* ipaddr = "192.168.0.121"; + int b[4]; + sscanf(ipaddr, "%d.%d.%d.%d", &b[0], &b[1], &b[2], &b[3]); + DBG("%d.%d.%d.%d",b[0],b[1],b[2],b[3]); - // if received NNCFIP, enter config mode - if (config_mode_flag) { - while (n > 0) { - // got some data, test it - DBG("UDP received (%s) from (%s:%d)", udp_receiving_buffer, ep_udp_client.get_address(), ep_udp_client.get_port()); - process_config_command(udp_receiving_buffer, n); - // wait to receive new config command - udp_server.set_blocking(true); - n = udp_server.receiveFrom(ep_udp_client, udp_receiving_buffer, sizeof(udp_receiving_buffer)); - } // while (n > 0), config loop - } // if (config_mode_flag) - // process control packages sent using UDP - else if ((n > 0) && (!discovery_mode_flag)) { - n = process_control_command(udp_receiving_buffer, n); - // send rpc reply back to client, NNIO protocol always returns 0 - // RPC-style protocol - if (n > 0) { - network_output_buffer[n] = '\0'; - udp_server.sendTo(ep_udp_client, network_output_buffer, strlen(network_output_buffer)); - } // RPC-style protocol - else if (n == 0) { - // then, check query status command and sending protocol if required - if (udp_receiving_buffer[RECEIVING_PROTOCOL_COMMAND_POS] == QUERY_STATUS_COMMAND) { - DBG("Requested to send device status through UDP"); - // sending protocol - update_sending_frame(network_output_buffer); - udp_server.sendTo(ep_udp_client, network_output_buffer, SENDING_PROTOCOL_LENGTH); - DBG("Sent"); - } - } // NNIO protocol - } -#endif - } // network processor -} - - -/* - * Process NNCF commands - */ -void process_config_command(char* received_buffer, int len) -{ - DBG("Processing configuration command"); - - // a configuration command always starts with NN - if ((received_buffer[0] == 'N') && (received_buffer[1] == 'N') && - (received_buffer[2] == 'C') && (received_buffer[3] == 'F')) { - switch (len) { - // length = 6, a QUERY command (discovery command, TCP port, or UDP port) - // Format: NNCFDS, NNCFTP, NNCFUP, NNCFTM - case QUERY_NETWORK_CONFIG_CMD_LENGTH: { - if (strstr(received_buffer, QUERY_IP_CMD) != NULL) { - udp_server.sendTo(ep_udp_client, eth.getIPAddress(), strlen(eth.getIPAddress())); - } // NNCFIP - else if (strstr(received_buffer, QUERY_SUBNET_CMD) != NULL) { - udp_server.sendTo(ep_udp_client, eth.getNetworkMask(), strlen(eth.getNetworkMask())); - } // NNCFSN - else if (strstr(received_buffer, QUERY_GATEWAY_CMD) != NULL) { - udp_server.sendTo(ep_udp_client, eth.getGateway(), strlen(eth.getGateway())); - } // NNCFGW - else if (strstr(received_buffer, QUERY_MAC_CMD) != NULL) { - udp_server.sendTo(ep_udp_client, eth.getMACAddress(), strlen(eth.getMACAddress())); - } // NNCFMC - // ask for TCP server port - else if (strstr(received_buffer, QUERY_TCP_PORT_CMD) != NULL) { - char port[5]; - sprintf(port, "%5d", tcp_server_local_port); - udp_server.sendTo(ep_udp_client, port, strlen(port)); - } // NNCFTP - // ask for UDP server port - else if (strstr(received_buffer, QUERY_UDP_PORT_CMD) != NULL) { - char port[5]; - sprintf(port, "%5d", udp_server_local_port); - udp_server.sendTo(ep_udp_client, port, strlen(port)); - } // NNCFUP - else if (strstr(received_buffer, QUERY_UPDATE_TIME_CMD) != NULL) { -#ifdef NTP - char str_time[50]; - - DBG("Trying to update time..."); - if (ntp.setTime("0.pool.ntp.org") == 0) { - DBG("Set time successfully"); - time_t ctTime; - ctTime = time(NULL); + // create rpc http server + HTTPServer srv = create_interactive_server(); - DBG("Time is set to (UTC): %s", ctime(&ctTime)); - sprintf(str_time, "%s", ctime(&ctTime)); - udp_server.sendTo(ep_udp_client, str_time, strlen(str_time)); - } else { - WARN("Error"); - sprintf(str_time, "ERR"); - udp_server.sendTo(ep_udp_client, str_time, strlen(str_time)); - } -#else - WARN("NTP disabled"); - sprintf(str_time, "DIS"); - udp_server.sendTo(ep_udp_client, str_time, strlen(str_time)); -#endif - } // NNCFTM - break; - } - // length = 19, SET NETWORK CONFIGURATION - // Format: 4E 4E 43 46 C0 A8 00 78 FF FF FF 00 C0 A8 00 01 00 00 01 - // (NNCF; IP: 192.168.0.120; Subnet: 255.255.255.0; GW: 192.168.0.1; MAC: 0 0 1) - case SET_NETWORK_CONFIG_CMD_LENGTH: { - // check device id - char* id = strstr(received_buffer, DEVICE_CONFIG_CODE); - if (id == NULL) - break; - else if ((id - received_buffer) > 0) - break; - - DBG("Received user configuration"); - write_eeprom_network(&received_buffer[strlen(DEVICE_CONFIG_CODE)]); // parameters from 5th char, 15-bytes - NVIC_SystemReset(); - break; - } - // length = 12, SET TCP SERVER CONFIGURATION - // auto update & its time period, TCP server configuration (IP & port) - // Format: 4E 4E 43 46 'Y' 01 C0 A8 00 09 E0 2E (LSB MSB) - // NNCF Auto 1s 192.168.0.9 12000 - case UPDATE_TCP_SERVER_INFO_COMMAND_LENGTH: { - char* id = strstr(received_buffer, DEVICE_CONFIG_CODE); - if (id == NULL) - break; - else if ((id - received_buffer) > 0) - break; - - DBG("Received TCP server configuration"); - write_eeprom_tcpserver(&received_buffer[strlen(DEVICE_CONFIG_CODE)]); // parameters from 5th char - NVIC_SystemReset(); - break; - } - default: - break; - } // switch (n), check configuration command length - } // if starts with NNCF, a config command - else { // if not a configuration command - } -} - -/* - * Procedure to process receiving protocol, which includes command to control outputs. - * Support both NNIO and RPC-style commands. - * RPC always starts with '/'. - * Return: - * 0 if NNIO protocol; - * length of rpc output buffer if rpc; - * -1 if rpc failed - */ -int process_control_command(char* received_buffer, int len) -{ - char* received_frame; - bool rpc_style; - int pos; - char inbuf[NET_BUF_LEN]; - - DBG("Processing control command"); - - /* - * This section is for RPC-style command - */ - // check if it is a RPC-style command - rpc_style = false; - strncpy(inbuf, received_buffer, len); - inbuf[len] = '\r'; // use inbuf for RPC protocol - inbuf[len+1] = '\n'; - inbuf[len+2] = '\0'; // add CR-LF - if ((len > 0) && (inbuf[0] == '/')) { - char obj_name[16]; - bool ok; - - rpc_style = true; - // find RPC object name - for (int i = 1; i < strlen(inbuf); i++) { - if (inbuf[i] != '/') { - obj_name[i-1] = inbuf[i]; - } else { - obj_name[i-1] = '\0'; - break; - } - } - DBG("Rpc command = %s", inbuf); - /* - * execute RPC command, return reply length and reply in rpc_outbuf - */ - ok = RPC::call(inbuf, network_output_buffer); - if (ok) { - // re-arrange output buffer as following: object_name:output_value - strcpy(inbuf, network_output_buffer); // use inbuf as temp - strcpy(network_output_buffer, obj_name); // rpc object name - strcat(network_output_buffer, ":"); - strcat(network_output_buffer, inbuf); // concat rpc reply - strcat(network_output_buffer, "\r\n"); // CR-LF - int j = strlen(network_output_buffer); - DBG("Reply of rpc command on \"%s\" (%d bytes): %s", obj_name, j, network_output_buffer); - return j; // return length of rpc_outbuf - } else { - ERR("Failed: %s", inbuf); - return -1; - } + if(!srv.init(SERVER_PORT)) + { + eth.disconnect(); + return -1; } - /* - * This section below is for NNIO protocol - */ - while ((!rpc_style) && (len >= RECEIVING_PROTOCOL_LENGTH)) { - // find device ID - DBG("Checking device ID..."); - char* id = strstr(received_buffer, DEVICE_CONTROL_CODE); - if (id == NULL) { - DBG("No device ID found"); - break; - } - pos = id - received_buffer; - DBG("Found a frame at %d", pos); - - // extract this frame - received_frame = &received_buffer[pos]; - // calculate the rest - received_buffer = &received_buffer[pos + RECEIVING_PROTOCOL_LENGTH]; - len -= RECEIVING_PROTOCOL_LENGTH; - - // process this received frame - // firstly, update outputs if required - // digital outputs - if (received_frame[RECEIVING_PROTOCOL_EN_DO_POS] == RECEIVING_PROTOCOL_ENABLE_OUTPUT) { - DBG("Update digital outputs"); - char str_dout[9]; - memcpy(str_dout, &received_frame[RECEIVING_PROTOCOL_DO_POS], 8); - str_dout[8] = '\0'; - update_digital_outputs(str_dout); - } - // analog output 0 - if (received_frame[RECEIVING_PROTOCOL_EN_A0O_POS] == RECEIVING_PROTOCOL_ENABLE_OUTPUT) { - DBG("Update analog output 0"); - //TODO Update analog output - } - // analog output 1 - if (received_buffer[RECEIVING_PROTOCOL_EN_A1O_POS] == RECEIVING_PROTOCOL_ENABLE_OUTPUT) { - DBG("Update analog output 1"); - //TODO Update analog output - } - // UART - if (received_frame[RECEIVING_PROTOCOL_EN_UART_POS] == RECEIVING_PROTOCOL_ENABLE_OUTPUT) { - DBG("UART data: "); - char str_uart[33]; - memcpy(str_uart, &received_frame[RECEIVING_PROTOCOL_UART_POS], 32); - str_uart[32] = '\0'; - uart.printf("%s\r\n", str_uart); - } - } - - DBG("Successfully processed."); - return 0; + srv.run(); } @@ -885,59 +238,3 @@ return 0; } - -/* -* Update digital outputs according to receiving protocol -*/ -void update_digital_outputs(char* buf) -{ - DBG("Digital outputs: %s", buf); - - dout0 = (buf[0] == DIGITAL_HIGH)? 1 : 0; - dout1 = (buf[1] == DIGITAL_HIGH)? 1 : 0; - dout2 = (buf[2] == DIGITAL_HIGH)? 1 : 0; - dout3 = (buf[3] == DIGITAL_HIGH)? 1 : 0; - dout4 = (buf[4] == DIGITAL_HIGH)? 1 : 0; - dout5 = (buf[5] == DIGITAL_HIGH)? 1 : 0; - dout6 = (buf[6] == DIGITAL_HIGH)? 1 : 0; - dout7 = (buf[7] == DIGITAL_HIGH)? 1 : 0; -} - -/* -* Prepare a frame for sending protocol, which includes status of I/Os -*/ -void update_sending_frame(char* buf) -{ - memcpy(&buf[SENDING_PROTOCOL_ID_POS], DEVICE_CONTROL_CODE, 4); // device id - memcpy(&buf[SENDING_PROTOCOL_MAC_POS], &u8mac, 6); - memcpy(&buf[SENDING_PROTOCOL_IP_POS], &u8ip_addr, 4); - - buf[SENDING_PROTOCOL_DI_POS+0] = (din0 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DI_POS+1] = (din1 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DI_POS+2] = (din2 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DI_POS+3] = (din3 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DI_POS+4] = (din4 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DI_POS+5] = (din5 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DI_POS+6] = (din6 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DI_POS+7] = (din7 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - - buf[SENDING_PROTOCOL_DO_POS+0] = (dout0 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DO_POS+1] = (dout1 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DO_POS+2] = (dout2 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DO_POS+3] = (dout3 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DO_POS+4] = (dout4 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DO_POS+5] = (dout5 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DO_POS+6] = (dout6 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - buf[SENDING_PROTOCOL_DO_POS+7] = (dout7 == 1) ? DIGITAL_HIGH : DIGITAL_LOW; - - uint16_t val = ain0.read_u16(); // 16-bits normalised - memcpy(&buf[SENDING_PROTOCOL_AI0_POS], &val, 2); // LSB MSB - val = ain1.read_u16(); // 16-bits normalised - memcpy(&buf[SENDING_PROTOCOL_AI1_POS], &val, 2); // LSB MSB - val = 0x1234; - memcpy(&buf[SENDING_PROTOCOL_AO0_POS], &val, 2); // LSB MSB - val = 0x5678; - memcpy(&buf[SENDING_PROTOCOL_AO1_POS], &val, 2); // LSB MSB - buf[SENDING_PROTOCOL_CR_POS] = 0x0D; - buf[SENDING_PROTOCOL_CR_POS+1] = '\0'; -} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-rpc-nucleo.lib Thu Jun 16 08:38:31 2016 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/olympux/code/mbed-rpc-nucleo/#e65ed29fd4d1
--- a/mbed-rpc.lib Tue Jun 14 21:25:04 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/users/olympux/code/mbed-rpc-nucleo/#e6a835c40639
--- a/protocol_rpc.txt Tue Jun 14 21:25:04 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -NNIO module can be controlled through TCP/UDP using RPC commands. - - -1. Connections - + UDP port: 11000 - + TCP port: 10000 (NNIO module is TCP server) - -2. Digital inputs - + Digital inputs: di0 - di7 - + Commands: /di0/read x - -3. Digital outputs - + Digital outputs: do0 - do7 - + Commands: - /do0/read x - /do0/write 1 - -3. Analog inputs - + Analog inputs: ai0 - ai1 - + Commands: - /ai0/read x: returns a value between 0 to 1 - /ai0/read_u16 x: returns a value between 0 to 0xFFFF - -4. PWM outputs: - + PWM outputs: pwm0 - pwm1 - + Commands: - /pwm/period 0.5: Set the PWM period, specified in seconds (float), keeping the duty cycle the same. - /pwm/period_ms 500: Set the PWM period, specified in milli-seconds (int), keeping the duty cycle the same. - /pwm/pulsewidth 1: Set the PWM pulsewidth, specified in seconds (float), keeping the period the same. - /pwm/pulsewidth_ms 1000: Set the PWM pulsewidth, specified in milli-seconds (int), keeping the period the same. - /pwm/read x: Return the current output duty-cycle setting, measured as a percentage (float). - /pwm/write 0.6: Set the ouput duty-cycle, specified as a percentage (float). - -5. Timers: - + Timer: timer1 - + Commands: - /tmr1/start x - /tmr1/stop x - /tmr1/reset x - /tmr1/read x: returns float, between 0 to 1. - /tmr1/read_ms - /tmr1/read_us - -6. UARTs: - + Uart: uart - + Commands: - /uart/baud 9600 - /uart/putc 60: send ASCII code, '<' - /uart/getc x: return ASCII code - /uart/puts helloworld: print helloworld to serial port - -7. RPCVariable - + Declare normal variables and associate them with RPCVariable, e.g. int wheels, float speed, char banner - + Commands: - /speed/write 34.5 - /speed/read x - /wheels/write 64 - /wheels/read x - /banner/write c - /banner/read x - -8. RPCFunction - + Declare a function and associate it with RPCFunction - + Commands: - /func_name/run x \ No newline at end of file
--- a/protocol_v2.0.txt Tue Jun 14 21:25:04 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -CONFIGURATION SECTION (UDP) -A configuration command always starts with "NNCF" -1. DISCOVERY Command - + UDP broadcast: 192.168.0.255 to port 11000 - + Send: NNCFDS - + Receive: NNCF (4-bytes) MAC-address (6-bytes) IP-address -1a. Query Command: IP, subnet, gateway, mac - + Send: NNCFIP (enter configuration mode), NNCFSN, NNCFGW, NNCFMC - -2. TCP SERVER PORT Command - + Send: NNCFTP - + Receive: 10000 - -3. UDP SERVER PORT Command - + Send: NNCFUP - + Receive: 11000 - -4. Set time using NTP Command - + Send NNCFTM - + Receive: - DIS: if NTP is disabled - ERR: if cannot update time - Successful: Fri Sep 26 20:28:01 2014 {0A} - -5. Set new network configuration - + Send: 19 bytes in total, NNCF + 15-byte - NNCF 4-byte IP address 4-byte subnet 4-byte gateway 3-byte MAC - 4E 4E 43 46 C0 A8 00 78 FF FF FF 00 C0 A8 00 01 00 00 01 - -6. Set TCP server info (only when the device is as a TCP client) - + Send: 12 bytes in total, NNIO + 8 bytes - NNCF 1-byte auto flag 1-byte time period (s) 4-byte IP 2-byte port (LSB MSB) - 4E 4E 43 46 'Y' or others 05 (5s) C0 A8 00 09 E0 2E (0x2EE0 = 12000) - -NOTE: -1. Both TCP client and server are working. Can be enabled/disabled using u16enable_tcp_client/server flags in eeprom (not in use now). -2. UDP server is always working. - - -INTERFACING SECTION (TCP) -4. Receiving Protocol: 58-bytes in total -Ex in hex -ID: 4E 4E 49 4F -OP: 4F 4F 4F 4F 51 -IP: C0 A8 00 78 -DO: 48 48 48 48 48 48 48 48 -AO: 80 00 80 00 (no DAC) -UART: 32-byte -CR: 0D - - + Field ID (4-bytes) = NNIO - + Field OP (5-bytes): output control enable flag - Byte 1: Digital output - Byte 2: Analog output 0 - Byte 3: Analog output 1 - Byte 4: UART output - Byte 5: Command (Q: query status) - 'O': enable controlling output - Others: disable controlling output - If Command is 'Q', device will update its outputs if needed and send its status including new outputs - + Field IP (4-bytes): device IP address - + Field DO[n] (8-bytes): digital output values - 'H': output set to high - 'L': output set to low - + Field AO_0 (2-bytes): 16-bit analog output value, channel 0 - no DAC - + Field AO_1 (2-bytes): 16-bit analog output value, channel 1 - no DAC - + UART output (32-bytes): max 32 bytes, stop string by NULL - + End char: CR - -5. Sending Protocol: 39-bytes in total - + Field ID (4-bytes) = NNIO - + Field MAC (6-bytes) - + Field IP (4-bytes) - + Field DI[n]: 8-bit digital input values - 'H' if input is HIGH - 'L' if input is LOW - + Field DO[n]: 8-bit digital output values - 'H' if output is HIGH - 'L' if output is LOW - + Field AI_0 (2-bytes): analog 16-bit input value, normalised, channel 0 - 1st byte = LSB, 2nd byte = MSB - + Field AI_1 (2-bytes): analog 16-bit input value, normalised, channel 1 - 1st byte = LSB, 2nd byte = MSB - + Field AO_0 (2-bytes): 16-bit analog output value, channel 0 - no DAC, fixed - + Field AO_1 (2-bytes): 16-bit analog output value, channel 1 - no DAC, fixed - + End char: CR - -6 Commands: - + QUERY STATUS ('Q') \ No newline at end of file