Spidey Wall is the name for a physical wall lit up by multiple addressable LED strips. This program is an LPC1768 web server to control the wall from a browser.
Dependencies: EthernetInterfacePlusHostname RdWebServer mbed-rtos mbed
This project is part of a Light-Wall using addressable LED strips (WS2801). I have published a few posts on my blog about the construction of the wall and building a game to play on it (PacMan). I have also had a guest post from a friend who has set his children the task of producing some interesting animations. The original post is http://robdobson.com/2015/07/spidey-wall/
So far, however, I hadn't fully connected the physical (and electronic) wall with the web-browser creations to drive it. This project is hopefully the final link. A fast and reliable web server using REST commands to drive the 1686 LEDs in the Spidey Wall from code running in a browser (say on an iPad while you are playing a game).
The approach taken here results in the ability to control the RGB values of all 1686 LEDs at a rate of 20 frames per second.
A blog post describing the whole thing is here:
http://robdobson.com/2015/08/a-reliable-mbed-webserver/
Revision 3:e5ea80fae61d, committed 2015-08-31
- Comitter:
- Bobty
- Date:
- Mon Aug 31 09:03:15 2015 +0000
- Parent:
- 2:99eb4c6e9ea4
- Child:
- 4:b521815f2657
- Commit message:
- Made ShowLeds an explicit command rather than automatic; Allow hostname to be set; Made sure HTTP response has keep-alive in it; Moved http_server into main loop (was in a separate thread); Added HSV fill command
Changed in this revision
--- a/DrawingManager.cpp Sat Aug 29 05:33:30 2015 +0000 +++ b/DrawingManager.cpp Mon Aug 31 09:03:15 2015 +0000 @@ -29,7 +29,6 @@ return "BUSY"; isBusy = true; char* respStr = cmdmsg::Interpret(cmdBuf, cmdLen, pLedStrip); - pLedStrip->ShowLeds(); isBusy = false; return respStr; }
--- a/EthernetInterface.lib Sat Aug 29 05:33:30 2015 +0000 +++ b/EthernetInterface.lib Mon Aug 31 09:03:15 2015 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/mbed_official/code/EthernetInterface/#2fc406e2553f +http://mbed.org/users/mbed_official/code/EthernetInterface/#8e692841213c
--- a/RdWebServer.lib Sat Aug 29 05:33:30 2015 +0000 +++ b/RdWebServer.lib Mon Aug 31 09:03:15 2015 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/Bobty/code/RdWebServer/#2dfb56648b93 +http://mbed.org/users/Bobty/code/RdWebServer/#0fc3d7b5e596
--- a/cmdmsg.cpp Sat Aug 29 05:33:30 2015 +0000 +++ b/cmdmsg.cpp Mon Aug 31 09:03:15 2015 +0000 @@ -32,6 +32,7 @@ (COD & 0x7f) .... 0x01: Clear the entire strip + There are no parameters 0x02: Fill a series of leds with RGB values <PARAMS> := <START><NUM><RGB1><RGB2> @@ -85,6 +86,21 @@ <LINKNUMLEDS> is the number of LEDs to control (1 byte) <LINKRGB> is the colour to make the link LEDs (3 bytes) + 0x08: + Raw HSV values for a series of leds + <PARAMS> := <START><NUM><HSV1><HSV2><HSV3>....<HSVn> + Where: + <START> is a two byte value (bigendian) for led to start series + <NUM> is a two byte (bigendian) number of leds to set + <HSV1> is the RGB value for 1st LED (16bits) - first 8 bits = H, upper 4 (bigendian) = S / 16, lower 4 = V / 16 + <HSV2> is the RGB value for 2nd LED + <HSVn> is the RGB value for nth LED + + 0x09: + ShowLeds + Until this command is sent all other commands simply "paint" into the buffer and don't actually change the LEDs + There are no parameters + */ #ifdef USE_SPIDEY_GEOM @@ -301,6 +317,34 @@ #endif break; } + case 0x08: // Raw HSV + { + if (paramsLen > 4) + { + int startLed = (msg[0] * 256) + msg[1]; + int numLeds = (msg[2] * 256) + msg[3]; + if (paramsLen >= numLeds * 2 + 4) + { + pLedStrip->HsvFill(startLed, numLeds, msg+4); +// printf("R Q%04x S%d N%d Pa%d\r\n", seqCount, startLed, numLeds, paramsLen); + } + else + { + sprintf(responseStr, "%04x HSV FILL - BufLengthMismatch %d", seqCount, paramsLen); + } + } + else + { + sprintf(responseStr, "%04x HSV FILL - ParamsLen Error %d", seqCount, paramsLen); + } + break; + } + case 0x09: // ShowLeds + { +// printf("ShowLeds - nextbyte %d\r\n", msg[0]); + pLedStrip->ShowLeds(); + break; + } default: { sprintf(responseStr, "%04x CMD %02x UNKNOWN", seqCount, cmdCode);
--- a/ledstrip.cpp Sat Aug 29 05:33:30 2015 +0000 +++ b/ledstrip.cpp Mon Aug 31 09:03:15 2015 +0000 @@ -146,6 +146,28 @@ unsigned char* pBuf = GetBuffer() + pos; memcpy(pBuf, pLedVals, numLeds * mColoursPerLed); } + +void ledstrip::HsvFill(int startLed, int numLeds, const unsigned char* pLedVals) +{ + if ((startLed < 0) || (startLed >= mLedsInStrip)) + return; + if (numLeds >= mLedsInStrip - startLed) + numLeds = mLedsInStrip - startLed; + int pos = startLed * mColoursPerLed; + unsigned char* pBuf = GetBuffer() + pos; + + // Copy over the values converting each to RGB + for (int i = 0; i < numLeds; i++) + { + RgbColor colrVal = HsvToRgb(HsvColor(pLedVals[0],pLedVals[1] & 0xf0, (pLedVals[1] << 4) & 0xf0)); + pBuf[pos] = colrVal.r; + pBuf[pos+1] = colrVal.g; + pBuf[pos+2] = colrVal.b; +// printf("HSV %d %d %d RGB %d %d %d\r\n", pLedVals[0],pLedVals[1] & 0xf0, (pLedVals[1] << 4) & 0xf0, colrVal.r, colrVal.g, colrVal.b); + pos += mColoursPerLed; + pLedVals += 2; + } +} // Fill - solid colour void ledstrip::Fill(int startLed, int numLeds, @@ -236,7 +258,7 @@ // Check if busy while (isr0Busy || isr1Busy) wait_us(2000); - wait_us(2000); +// wait_us(2000); // Set up start points mCurPos0 = 0;
--- a/ledstrip.h Sat Aug 29 05:33:30 2015 +0000 +++ b/ledstrip.h Mon Aug 31 09:03:15 2015 +0000 @@ -31,6 +31,7 @@ } void Clear(); void RawFill(int startLed, int numLeds, const unsigned char* pLedVals); + void HsvFill(int startLed, int numLeds, const unsigned char* pLedVals); void Fill(int startLed, int numLeds, int r1, int g1, int b1, int r2, int g2, int b2);
--- a/main.cpp Sat Aug 29 05:33:30 2015 +0000 +++ b/main.cpp Mon Aug 31 09:03:15 2015 +0000 @@ -180,11 +180,15 @@ // Get message payload int cmdLen = RdWebServer::getPayloadLengthFromMsg(msgBuf); unsigned char* cmdBuf = RdWebServer::getPayloadDataFromMsg(msgBuf); - pc.printf("Command Payload Len %d\r\n", cmdLen); - - // Process command - char* respStr = ""; - respStr = drawingManager.start(cmdBuf, cmdLen); + + // Check if the command length is 0 - in this case respond ok as it might be a + // pre-flight check on a Cross Domain request - https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS + char* respStr = "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: POST, GET, OPTIONS\r\nAccess-Control-Allow-Headers:accept, content-type\r\nContent-Length: 0\r\nContent-Type: application/octet-stream\r\n\r\n"; + if (cmdLen != 0) + { + // Process command + drawingManager.start(cmdBuf, cmdLen); + } return respStr; } @@ -220,7 +224,6 @@ { printf("LightWall - Configured for "); // Check for a config file on the local file system - strcpy(systemName, "Spidey"); LocalFileSystem local("local"); FILE* fp = fopen("/local/lights.txt", "r"); if (fp != NULL) @@ -257,17 +260,27 @@ mbed_mac_address(macAddr); pc.printf("Ethernet MAC address: %02x:%02x:%02x:%02x:%02x:%02x\r\n", macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]); pc.printf("Connecting to ethernet ...\r\n"); + + // Init ethernet EthernetInterface::init(); - EthernetInterface::connect(); - pc.printf("IP Address: %s\r\n", EthernetInterface::getIPAddress()); - // Web Server - Thread httpServer(&http_thread, NULL, osPriorityNormal, (DEFAULT_STACK_SIZE * 3)); + // Using code described here https://developer.mbed.org/questions/1602/How-to-set-the-TCPIP-stack-s-hostname-pr/ + // to setName on the ethernet interface + EthernetInterface::setName(systemName); - // Forever + // Connect ethernet + EthernetInterface::connect(); + pc.printf("IP Address: %s HostName %s\r\n", EthernetInterface::getIPAddress(), EthernetInterface::getName()); + + // Web Server used to run in a thread - but I've found this slows performance a lot as described here: + // http://robdobson.com/2015/08/a-reliable-mbed-webserver/ + // Fortunately it doesn't matter as the LED code is all interrupt driven + // This is the previous code... + // Thread httpServer(&http_thread, NULL, osPriorityNormal, (DEFAULT_STACK_SIZE * 3)); + http_thread(""); + + // Forever - actually it won't even get here as the server has a forever loop in it too while(true) { - // Service drawing manager - drawingManager.service(); } }