moving my window shades control to MBED

I’ve been having trouble with my Netduino Plus 2 based window shade (blinds to us in the UK) controller. The main problems have been:

  1. Something seems to trash the firmware fairly often and the procedure for re-flashing is laborious … http://forums.netduino.com/index.php?/topic/8116-netduino-plus-2-firmware-v422-update-2/
  2. I couldn’t get a webserver (I’m using code based on http://www.schuurmans.cc/multi-threaded-web-server-for-netduino-plus) running reliably – seems to hang somewhere and not respond – maybe a threading problem when accessed from more than one place but I’ve tried to debug on several occasions and the problem seems to hang the debugger as well as the program
  3. Doesn’t seem to always start-up correctly on power on – this may be (2) crashing it before it has worked

So after a lot of consideration (due to the amount of time I’ve invested in the Netduino code and Arduino compatible hardware) I decided to try other platforms.

I’ve previously used Raspberry Pi, Arduino, FlyPort and various other older things but MBED @mbedmicro seems to be the one I go back to because it’s reasonably powerful for a microcontroller and tends to work more reliably than the Raspberry Pi for simple hardware projects in my experience (I’ll have to post about my son’s “spidey wall” sometime!).

Simple Control Scheme

In the past I’ve used relays to control the blinds which use a Sonesse DCT-30 dry-contact motor by Somfy @somfy (http://www.somfy.com/downloads/nam_pro/sonesse_30_databook_low.pdf) but I tried a simpler approach using a CMOS analog switch (74HCT4066) and it worked fine so that’s what I’m using on the new blinds for the games room. The schematic is simple.

image

Since the things I tend to make for the house are one-offs I tend to wire everything by hand as you would with a prototype and leave it at that. I have to confess that the Cool Components proto board for MBED that I used is a little frustrating when connecting to the MBED pins as the connectors are not through-hole so you have to solder onto the top of the board and then work out how to get connection to other components that would normally involved soldering on the other side.  Please @coolcomponents could we have a board with through-holes for the MBED pins?

imageimage

Web Server

I found a few web servers for the MBED’s new networking library but I didn’t really like any of them completely and I wanted something similar to Jasper Schuurmans (http://www.schuurmans.cc/multi-threaded-web-server-for-netduino-plus) web server for Netduino which allows simple REST commands to generate callbacks. So I set about modifying the MBED code at http://mbed.org/users/hsgw/code/HTTPServer_echoback/ and http://mbed.org/users/feb11/code/HTTP-Server/ to create something similar.

Due to the bad experiences with stability I’ve avoided using too much from the standard C++ library to avoid a lot of memory allocation/de-allocation. I use std::vector in the server to manage the commands and file cache but these won’t be de-allocated under normal circumstances.

Going back to doing all the string handling in C brought back some memories I can tell you – the thing I most remember is the nonsense of strncpy – which makes a copy of a C string ensuring it doesn’t overrun the destination buffer length – but doesn’t ensure the resultant string copy is null terminated! – doh!

int main (void)
{
    setConfig();

    // setup ethernet interface
    eth.init(); //Use DHCP
    eth.connect();
    printf("IP Address is %s\n\r", eth.getIPAddress());

    // setup web server
    webServer.addCommand("", RdWebServerCmdDef::CMD_LOCALFILE, NULL, "index.html", true);
    webServer.addCommand("gear-gr.png", RdWebServerCmdDef::CMD_LOCALFILE, NULL, NULL, true);
    webServer.addCommand("blind", RdWebServerCmdDef::CMD_CALLBACK, &handleCmd_blindControl);
    webServer.init(PORT, &led2);
    webServer.run();
}

And the callback for blinds control

void handleCmd_blindControl(char* cmdStr, char* argStr)
{
    printf("BLINDS COMMAND %s %s\n\r", cmdStr, argStr);
    if (argStr == NULL)
        return;
    char* pch = strtok (argStr,"/");
    if (pch == NULL)
        return;
    int blindNum = pch[0] - '0';
    if (blindNum < 1 || blindNum > MAX_WINDOW_SHADES)
        return;
    int blindIdx = blindNum-1;
    char* pDirn = strtok (NULL,"/");
    if (pDirn == NULL)
        return;
    char* pDuration = strtok (NULL,"/");
    if (pDuration == NULL)
        return;
    pWindowShades[blindIdx]->DoCommand(pDirn, pDuration);
}

Main source code for the server is here … http://mbed.org/users/Bobty/code/RdWebServer/

And for the whole project here … http://mbed.org/users/Bobty/code/RdBlindsServer/

I think I’ve more or less succeeded – it supports simple web pages delivered from Local (with caching) or SD file stores and has a callback mechanism that pleases me sufficiently.

Please note that the caching is only implemented for the local file system and is very rudimentary – it never clears the cache and only stops trying to cache new files when it can’t allocate sufficient memory.  In fact I only implemented the caching mechanism because MBED access to the local file system from program code disconnects the USB access and causes the attached PC to re-scan the files every time an access occurs – this doesn’t matter except when the PC is connected for debugging of course.

Stress Testing

Because of the problems encountered with the Netduino Plus 2 web server I decided I really ought to hit the MBED web server fairly hard to make sure it withstood simultaneous requests. I’m actually not that worried about it serving simultaneous requests successfully as, currently, I only intend it to be accessed by a human and, in the unlikely event of failure, it’s not unreasonably to think the human would try again. However, I really don’t want it to hang all future requests as that causes me to have to reset the device – a la Netduino.

from selenium import webdriver

driver = webdriver.Firefox()
successCount = 0
failCount = 0
for i in range(10000):
    driver.get("http://192.168.0.36")
    pagesource = driver.page_source
    if "blind" in pagesource:
        successCount += 1
    else:
        failCount += 1
    driver.get("http://192.168.0.36/blind/"+str((i%2)+1)+"/up/pulse")
    pagesource = driver.page_source
    if "ok" in pagesource:
        successCount += 1
    else:
        failCount += 1

print ("Failed", failCount, "of", failCount+successCount)
driver.close()

It works pretty well – no failures even when I hit it from two clients simultaneously – so I’m pretty happy with that.

Removal of JQuery for the Home Page

In the original version of the server I used JQuery Mobile to give a pleasing look and simplify the job of Ajax requests etc. And, following general advice I’d read on where to load JQuery from, I loaded the minified version from google.  I did’t really think what would happen if/when my somewhat unreliable Virgin Media broadband failed but the predictable answer is that the page looks terrible and the Ajax requests don’t work. So basically I lose all control of my window shades.

In the new version I’ve decided to code the home page with inline CSS and inline script so that if the network is working inside my house then I can still control the blinds – even if I can’t access the world outside.

The page looks a little simpler but at least the buttons are big so I can hit them fairly easily even on my phone.

image

I decided not to repeat a lot of the work I’d done on the commissioning page so that still uses JQuery …

image