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();
}
}