Typical controller demo program based on Seeed Arch Max. Features: - Multi-thread architecture - Inter-thread message communication - Independent command shell using thread - HTTPD with CGI, WS, RPC - Key & value pair configuration load/save

Dependencies:   CMDB EthernetInterface HTTPD dconfig mbed-rpc mbed-rtos mbed storage_on_flash

Files at this revision

API Documentation at this revision

Comitter:
hillkim7
Date:
Wed Mar 25 21:56:51 2015 +0000
Child:
1:a20044f85ee6
Commit message:
Typical controller demo program based on Seeed Arch Max.; Features:; - Multi-thread architecture; - Inter-thread message communication; - Independent command shell using thread; - HTTPD with CGI, WS, RPC; - Key & value pair configuration load/save

Changed in this revision

CMDB.lib Show annotated file Show diff for this revision Revisions of this file
EthernetInterface.lib Show annotated file Show diff for this revision Revisions of this file
HTTPD.lib Show annotated file Show diff for this revision Revisions of this file
MainConfig.cpp Show annotated file Show diff for this revision Revisions of this file
MainConfig.h Show annotated file Show diff for this revision Revisions of this file
console.cpp Show annotated file Show diff for this revision Revisions of this file
console.h Show annotated file Show diff for this revision Revisions of this file
dconfig.lib Show annotated file Show diff for this revision Revisions of this file
httpd_service.cpp Show annotated file Show diff for this revision Revisions of this file
httpd_service.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
main.h Show annotated file Show diff for this revision Revisions of this file
mbed-rpc.lib Show annotated file Show diff for this revision Revisions of this file
mbed-rtos.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
storage_on_flash.lib Show annotated file Show diff for this revision Revisions of this file
util.cpp Show annotated file Show diff for this revision Revisions of this file
util.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CMDB.lib	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/teams/mbed_controller/code/CMDB/#ed36b4c73d4c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EthernetInterface.lib	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/users/yihui/code/EthernetInterface-arch-max-dev/#34f536b71858
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPD.lib	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/okini3939/code/HTTPD/#d18dff347122
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MainConfig.cpp	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,62 @@
+/**
+ * @file MainConfig.cpp
+ *
+ * @brief main configuration
+ *
+ */
+
+#include "MainConfig.h"
+#include "SOFBlock.h"
+
+// main configuration sector index of flash memory.
+const uint8_t config_sector_index = 7;
+
+void MainConfig::reset_default(void)
+{
+    (*this)["eth"] = "dhcp";
+    (*this)["ip"] = "";
+    (*this)["mask"] = "255.255.255.0";
+    (*this)["gw"] = "";
+}
+
+bool MainConfig::load_config()
+{
+    SOFReader reader;
+
+    if (reader.open(config_sector_index) != kSOF_ErrNone) {
+        return false;
+    }
+
+    return load_from((char *)reader.get_physical_data_addr(), reader.get_data_size());
+}
+
+static bool save_func(void *user_data, char c)
+{
+    SOFWriter *writer = (SOFWriter*)user_data;
+
+    return writer->write_byte_data((uint8_t)c);
+}
+
+bool MainConfig::save_config()
+{
+    size_t need_bytes = estimate_save();
+    SOFWriter writer;
+
+    if (writer.open(config_sector_index) != kSOF_ErrNone) {
+        printf("open(%d) fail: format\r\n", config_sector_index);
+        SOFBlock::format(config_sector_index);
+        writer.open(config_sector_index);
+    } else {
+        if (need_bytes > writer.get_free_size()) {
+            printf("too small free size(%u/%u): format\r\n", need_bytes, writer.get_free_size());
+            writer.close();
+            SOFBlock::format(config_sector_index);
+            writer.open(config_sector_index);
+        }
+    }
+
+    return save_to(save_func, &writer);
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MainConfig.h	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,36 @@
+/**
+ * @file MainConfig.h
+ *
+ * @brief main configuration
+ *
+ */
+#pragma once
+
+#include "dconfig.h"
+#include "mbed.h"
+#include "rtos.h"
+
+
+class MainConfig : public DConfig
+{
+public:
+    virtual void reset_default(void);
+
+    bool load_config();
+    bool save_config();
+
+    void lock_config() {
+        mutex_.lock(osWaitForever);
+    }
+
+    void unlock_config() {
+        mutex_.unlock();
+    }
+
+protected:
+    Mutex mutex_;
+};
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console.cpp	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,139 @@
+/**
+ * @file console.cpp
+ *
+ * @brief console implementation using CMDB library.
+ *
+ * After boot, it prompts "CMD>" in console. Type "help" command to get help.
+ */
+
+#include <vector>
+#include "mbed.h"
+#include "cmdb.h"
+#include "util.h"
+#include "main.h"
+
+#define CID_TEST		1
+#define CID_FREE		2
+#define CID_CFG_SET		3
+#define CID_CFG_SAVE	4
+#define CID_CFG_PRINT	5
+#define CID_IF_UP		6
+#define CID_IF_DOWN		7
+#define CID_IF_STAT		8
+
+/** Sample User Command Dispatcher.
+ *
+ * @parm cmdb the command interpreter object.
+ * @parm cid the command id.
+ */
+void my_dispatcher(Cmdb& cmdb, int cid)
+{
+    //cmdb.printf("my_dispatcher: cid=%d\r\n", cid);
+
+    switch (cid) {
+        case CID_FREE :
+            //cmdb.printf("my_dispatcher: parm 0=%d\r\n",cmdb.INTPARM(0));
+            print_memstat();
+            break;
+        case CID_CFG_SET :
+            _config.lock_config();
+            if (!_config.value_replace(cmdb.STRINGPARM(0), cmdb.STRINGPARM(1))) {
+                cmdb.printf("invalid key='%s'\r\n", cmdb.STRINGPARM(0));
+            }
+            _config.unlock_config();
+            break;
+        case CID_CFG_SAVE :
+            _config.lock_config();
+            if (!_config.save_config()) {
+                cmdb.printf("save fail\r\n");
+            }
+            _config.unlock_config();
+            break;
+        case CID_CFG_PRINT :
+            _config.lock_config();
+            _config.print_all();
+            _config.unlock_config();
+            break;
+        case CID_IF_UP :
+            send_main_message(MSG_IFUP, 0, 0);
+            break;
+        case CID_IF_DOWN:
+            send_main_message(MSG_IFDOWN, 0, 0);
+            break;
+        case CID_IF_STAT :
+            send_main_message(MSG_IFSTAT, 0, 0);
+            break;
+        default:
+            printf("unknown CID=%u\r\n", cid);
+            break;
+    }
+}
+
+static const cmd user_cmd[] = {
+    {"Test",SUBSYSTEM,CID_TEST,""  ,"* Test Subsystem"},
+    {"free",CID_TEST,CID_FREE,""	,"show amount of free memory", ""},
+    {"cfgset",CID_TEST,CID_CFG_SET,"%s %s"  ,"config set", "config_key value"},
+    {"cfgsave",CID_TEST,CID_CFG_SAVE,""  ,"config save to flash"},
+    {"cfgprint",CID_TEST,CID_CFG_PRINT,""  ,"print all config"},
+    {"ifup",CID_TEST,CID_IF_UP,""  ,"bring a network interface up"},
+    {"ifdown",CID_TEST,CID_IF_DOWN,""  ,"bring a network interface down"},
+    {"ifstat",CID_TEST,CID_IF_STAT,""  ,"print network info"},
+};
+
+
+void console_thread(void const *args)
+{
+    Serial &serial = *((Serial *)args);
+
+    // Test the serial connection by
+    serial.printf("\r\n\r\nCmdb Command Interpreter Demo Version %0.2f.\r\n\r\n", Cmdb::version());
+
+    //Create a Command Table Vector.
+    std::vector<cmd> cmds(&user_cmd[0], &user_cmd[sizeof(user_cmd)/sizeof(user_cmd[0])]);
+
+    //Add some of our own first...
+    //Add some predefined...
+    cmds.push_back(COMMANDS); //Handled by Cmdb internally.
+    cmds.push_back(BOOT); //Handled by Cmdb internally.
+
+    cmds.push_back(ECHO); //Handled by Cmdb internally.
+    cmds.push_back(BOLD); //Handled by Cmdb internally.
+    cmds.push_back(CLS);  //Handled by Cmdb internally.
+
+    cmds.push_back(MACRO);  //Handled by Cmdb internally.
+    cmds.push_back(RUN);    //Handled by Cmdb internally.
+    cmds.push_back(MACROS); //Handled by Cmdb internally.
+
+    //Add some predefined and mandatory...
+    cmds.push_back(IDLE); //Handled by Cmdb internally.
+    cmds.push_back(HELP); //Handled by Cmdb internally.
+
+    //Create and initialize the Command Interpreter.
+    Cmdb cmdb(serial, cmds, &my_dispatcher);
+
+    //cmdb.printf("%d=%d\r\n",cmds[0].subs,cmds[0].cid);
+    //cmdb.printf("%d=%d\r\n",cmds[1].subs,cmds[1].cid);
+
+    while (1) {
+        //Check for input...
+        if (cmdb.hasnext()==true) {
+
+            //Supply input to Command Interpreter
+            if (cmdb.scan(cmdb.next())) {
+            }
+        }
+
+        //For Macro Support we basically do the same but take characters from the macro buffer.
+        //Example Macro: Test|Int_42|Idle
+        while (cmdb.macro_hasnext()) {
+            //Get and process next character.
+            cmdb.scan(cmdb.macro_next());
+
+            //After the last character we need to add a cr to force execution.
+            if (!cmdb.macro_peek()) {
+                cmdb.scan(cr);
+            }
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console.h	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,12 @@
+/*
+* @file console.h
+*
+* @brief console implementation
+*
+*/
+
+#define CONSOLE_STACK_SIZE (4*1024)
+
+void console_thread(void const *args);
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dconfig.lib	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/teams/mbed_controller/code/dconfig/#989178d925e9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/httpd_service.cpp	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,82 @@
+/*
+* @file httpd_task.h
+*
+* @brief HTTPD service task
+*
+*/
+
+#include "mbed.h"
+#include "rtos.h"
+#include "EthernetInterface.h"
+#include "mbed_rpc.h"
+#include "HTTPD.h"
+#include "httpd_service.h"
+
+HTTPD *httpd;
+
+//LocalFileSystem local("local");
+DigitalOut led1(LED1), led2(LED2), led3(LED3), led4(LED4);
+
+void callback_cgi (int id)
+{
+    int i, n;
+    char buf[256];
+
+    strcpy(buf, httpd->getFilename(id));
+    strcat(buf, "\r\n");
+    strcat(buf, httpd->getQueryString(id));
+    strcat(buf, "\r\n");
+    n = strlen(buf);
+
+    i = httpd->receive(id, &buf[n], sizeof(buf) - n);
+    if (i < 0) return;
+    i += n;
+    buf[i] = 0;
+
+    printf("CGI %d %s\r\n", id, buf);
+    httpd->send(id, buf, i, "Content-Type: text/plain\r\n");
+}
+
+void callback_ws (int id)
+{
+    int i;
+    char buf[256];
+
+    i = httpd->receive(id, buf, sizeof(buf));
+    if (i < 0) return;
+    buf[i] = 0;
+
+    printf("WS %d %s\r\n", id, buf);
+    httpd->sendWebsocket(id, buf, i);
+}
+
+void callback_rpc (int id)
+{
+    char buf[40], outbuf[40];
+
+    strcpy(buf, "/");
+    httpd->urldecode(httpd->getFilename(id), &buf[1], sizeof(buf) - 2);
+    RPC::call(buf, outbuf);
+
+    printf("RPC id %d '%s' '%s'\r\n", id, buf, outbuf);
+    httpd->send(id, outbuf, strlen(outbuf), "Content-Type: text/plain\r\n");
+}
+
+void httpd_start(int port)
+{
+//    RPC::add_rpc_class<RpcAnalogIn>();
+//    RPC::add_rpc_class<RpcAnalogOut>();
+    RPC::add_rpc_class<RpcDigitalIn>();
+    RPC::add_rpc_class<RpcDigitalOut>();
+    RPC::add_rpc_class<RpcDigitalInOut>();
+    RPC::add_rpc_class<RpcPwmOut>();
+
+    httpd = new HTTPD;
+    httpd->attach("/cgi-bin/", &callback_cgi);
+    httpd->attach("/ws/", &callback_ws);
+    httpd->attach("/rpc/", &callback_rpc);
+    httpd->attach("/", "/local/");
+    httpd->start(port);
+    printf("httpd ready\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/httpd_service.h	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,10 @@
+/*
+* @file httpd_task.h
+*
+* @brief HTTPD service task
+*
+*/
+
+
+void httpd_start(int port);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,155 @@
+/**
+Typical controller demo program based on Seeed Arch Max.
+The mbed has potential power by combining variable library, this program unveils that kind of thing.
+Features:
+- Multi-thread architecture
+- Inter-thread message communication
+- Independent command shell using thread
+- HTTPD with CGI, WS, RPC
+- Key & value pair configuration load/save
+*/
+
+#include "mbed.h"
+#include "rtos.h"
+#include "console.h"
+#include "httpd_service.h"
+#include "util.h"
+#include "EthernetInterface.h"
+#include "main.h"
+
+#define SUPPORT_CONSOLE			1
+#define SUPPORT_ETH				1
+#define SUPPORT_HTTPD			1
+
+const unsigned connect_timeout_ms = 2000;
+
+static Mail<MainMessage_t, 5> _mainq;
+
+static DigitalOut _alive_led(LED1);
+
+EthernetInterface _eth;
+MainConfig _config;
+
+static void eth_up_dhcp()
+{
+
+    printf("eth_up_dhcp: init()\r\n");
+    _eth.init();
+    _eth.connect(connect_timeout_ms);
+}
+
+static void eth_up_static(const char *ip, const char *netmask, const char *gateway)
+{
+    const unsigned static_connect_timeout_ms = 3;
+
+    printf("eth_up_static: init('%s', '%s', '%s')\r\n", ip, netmask, gateway);
+    _eth.init(ip, netmask, gateway);
+    _eth.connect(static_connect_timeout_ms);
+}
+
+static void eth_up()
+{
+    _config.lock_config();
+    if (_config.lookup("eth") == "dhcp")
+        eth_up_dhcp();
+    else
+        eth_up_static(_config.lookup_as_cstr("ip", ""), _config.lookup_as_cstr("mask", ""), _config.lookup_as_cstr("gw", ""));
+    _config.unlock_config();
+}
+
+static void eth_stat()
+{
+    printf("eth '%s', '%s', '%s'\r\n", _eth.getIPAddress(), _eth.getNetworkMask(), _eth.getGateway());
+}
+
+
+bool send_main_message(MainMessageId msg_id, uint32_t msg_p1, uint32_t msg_p2)
+{
+    MainMessage_t *msg = (MainMessage_t*)_mainq.alloc();
+
+    if (msg) {
+        msg->msg_id = msg_id;
+        msg->msg_p1 = msg_p1;
+        msg->msg_p2 = msg_p2;
+        _mainq.put(msg);
+        return true;
+    } else {
+        printf("_mainq.alloc fail\r\n");
+        return false;
+    }
+}
+
+static void on_main_message(MainMessageId msg_id, uint32_t msg_p1, uint32_t msg_p2)
+{
+    switch (msg_id) {
+        case MSG_IFUP:
+            printf("eth connect: %d\r\n", _eth.connect(1000));
+            break;
+        case MSG_IFDOWN:
+            printf("eth disconnect\r\n");
+            _eth.disconnect();
+            break;
+        case MSG_IFSTAT:
+            eth_stat();
+            break;
+    }
+}
+
+int main()
+{
+    Serial pc(USBTX, USBRX);
+
+    pc.baud(115200);
+    pc.printf("built at " __DATE__ " " __TIME__ "\r\n");
+    pc.printf("memory stat:\r\n");
+    print_memstat();
+
+    if (!_config.load_config()) {
+        printf("load_config fail: reset_default()\r\n");
+        _config.reset_default();
+    }
+
+#if SUPPORT_CONSOLE
+    printf("SUPPORT_CONSOLE\r\n");
+    Thread console(console_thread, &pc, osPriorityNormal, CONSOLE_STACK_SIZE);
+#endif
+
+#if SUPPORT_ETH
+    printf("SUPPORT_ETH\r\n");
+    eth_up();
+
+#if SUPPORT_HTTPD
+    httpd_start(80);
+#endif
+#endif
+
+    while (1) {
+        const uint32_t wait_ms = 100;
+        osEvent evt = _mainq.get(wait_ms);
+
+        if (evt.status == osEventMail) {
+            MainMessage_t *msg = (MainMessage_t*)evt.value.p;
+
+            on_main_message(msg->msg_id, msg->msg_p1, msg->msg_p2);
+            _mainq.free(msg);
+        }
+
+        _alive_led = !_alive_led;
+    }
+
+    return 0;
+}
+
+/**
+ * Overriding MAC address
+ */
+void mbed_mac_address(char *mac)
+{
+    // Change ethernet mac address of device.
+    mac[0] = 0x00;
+    mac[1] = 0x80;
+    mac[2] = 0xE1;
+    mac[3] = 0x00;
+    mac[4] = 0x02;
+    mac[5] = 0x01;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.h	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,38 @@
+/**
+ * @file main.h
+ *
+ * @brief message and interface for main task.
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+#include "MainConfig.h"
+
+/**
+ * Command Message ID of main task.
+ */
+typedef enum {
+    MSG_IFUP,		/// Start Ethernet
+    MSG_IFDOWN, 	/// Stop Ethernet
+    MSG_IFSTAT, 	/// Print Ethernet status
+} MainMessageId;
+
+typedef struct {
+    MainMessageId	msg_id;
+    uint32_t		msg_p1;
+    uint32_t		msg_p2;
+} MainMessage_t;
+
+
+/**
+ * Global configuration instance.
+ */
+extern MainConfig _config;
+
+/**
+ * Send message to main task.
+ */
+bool send_main_message(MainMessageId msg_id, uint32_t msg_p1, uint32_t msg_p2);
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rpc.lib	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/teams/mbed_controller/code/mbed-rpc/#75a49e903ad8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rtos.lib	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed-rtos/#d3d0e710b443
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/7e07b6fb45cf
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage_on_flash.lib	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/teams/mbed_controller/code/storage_on_flash/#2bb58064d0a2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util.cpp	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,70 @@
+/**
+ * @file util.cpp
+ *
+ * @brief system wide utility function
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#if defined(__CC_ARM) && !defined(__MICROLIB)
+// Keil MDK with microlib supports __heapstats();
+#define SUPPORT_HEAPSTATS
+#endif
+
+#if defined(SUPPORT_HEAPSTATS)
+
+static void heap_printf(void *dummy, const char *fmt, ...)
+{
+    va_list	arg_ptr;
+
+    if (strchr(fmt, '\n') != NULL) {
+        putchar('\r');
+    }
+    va_start(arg_ptr, fmt);
+    vprintf(fmt, arg_ptr);
+    va_end(arg_ptr);
+}
+
+void print_memstat(void)
+{
+    __heapstats((__heapprt)heap_printf, NULL);
+    printf("\r\n");
+}
+
+#else
+
+/**
+ * Compute max consecutive memory chunk, by trying to allocate it.
+ */
+static uint32_t comput_free_heap(uint32_t resolution, uint32_t maximum)
+{
+    int low = 0;
+    int high = maximum + 1;
+
+    while (high - low > resolution) {
+        int mid = (low + high) / 2;
+        void* p = malloc(mid);
+        if (p == NULL) {
+            high = mid;
+        } else {
+            free(p);
+            low = mid;
+        }
+    }
+
+    return low;
+}
+
+void print_memstat(void)
+{
+    printf("heap free %u\r\n", comput_free_heap(512, 192*1024));
+}
+
+#endif
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util.h	Wed Mar 25 21:56:51 2015 +0000
@@ -0,0 +1,11 @@
+/**
+ * @file util.h
+ *
+ * @brief system wide utility function
+ *
+ */
+#pragma once
+
+void print_memstat(void);
+
+