Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: CMDB EthernetInterface HTTPD dconfig mbed-rpc mbed-rtos mbed storage_on_flash
Revision 0:2ffd10976643, committed 2015-03-25
- 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
--- /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); + +