Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Made serial command language a little more complete. Frames can now b…

…e sent.
  • Loading branch information...
commit bd53109a9824729c7e2eefd6df9ac7fb0687f9c4 1 parent e0bf693
Chris Hodapp authored
Showing with 268 additions and 57 deletions.
  1. +268 −57 shiftbrite_slave/shiftbrite_slave.pde
View
325 shiftbrite_slave/shiftbrite_slave.pde
@@ -5,12 +5,28 @@
//
// It is based off of Rainbow_Plasma.pde from the existing code in the repo.
// =============================================================================
-// As of now it also has a really rudimentary serial command language.
-// cC clears the screen (it's overwritten very quickly though)
-// cQ queries for screen size (it will reply with x and y)
-// (First byte is BLOCK_START, which is now "c" - it signifies the start of the
-// command. Second byte is the command (see macros starting with CMD_). C and Q
-// are the only functioning ones right now.
+// As of now it also has a rudimentary serial command language. See the
+// whole block of CMD_* and REPLY_* constants.
+// Commands so far:
+// cPe - Ping (should reply rAe for reply start, ACK, reply end)
+// cQe - Query display size
+// cCe - Clear the display
+// cF{rgbrgbrgbrgbrgb..}e - Send frame. Between the 'F' and the 'e, put
+// one scanline at a time with one byte for R, one for G, one for B, and so
+// on. If you send less than screenWidth*screenHeight*3 bytes in the body,
+// it will block any further command until you do!
+// cDe - Toggle demo modes (clearing and sending a frame override this mode)
+//
+// A reply starting with rE indicates an error (it will be followed with a
+// one-byte error code, which the ERROR_* constants comprise, then the 'e'
+// for ending the reply block)
+// A reply of rAe indicates an acknowledgement.
+
+// If DEBUG is on, verbose serial messages are printed, which is good
+// for diagnosing errors but bad for any programs that expect it to
+// speak a normal serial protocol.
+// (use the debugMsg(char*) function for this)
+#define DEBUG 0
#include <math.h>
#include <avr/pgmspace.h>
@@ -46,36 +62,86 @@ int SB_BlueCommand;
// Board mounted LED state
int boardLEDstate = 1;
-// Frame count
+// For demo modes mostly: Frame count
int frame = 0;
+// SLAVE mode: Only displaying via CMD_FRAME, CMD_CLEAR
+// DEMO_BLUE_HORIZ, DEMO_GREEN_VERT: Animating itself with some simple demos
+// until interrupted by CMD_FRAME, CMD_CLEAR
+enum { SLAVE, DEMO_BLUE_HORIZ, DEMO_GREEN_VERT } mode = DEMO_BLUE_HORIZ;
// The frame that SetPixel writes to, and that we send to the Shiftbrite via
// WriteLEDArray
int LEDArray[screenWidth][screenHeight][3] = {0};
+// ===============================
// For our serial command protocol
// ===============================
+// Commands we receive
+// ===================
+// General structure:
+// CMD_BLOCK_START
+// CMD_*
+// arguments
+// CMD_BLOCK_END
+
// c
-#define BLOCK_START 0x63
+#define CMD_BLOCK_START 0x63
-// P
+// P (Ping, just reply ACK)
#define CMD_PING 0x50
-// Q
+// Q (Query, return screen size)
#define CMD_QUERY 0x51
-// D
+// D (Demo mode - let the display animate itself. Note that CMD_CLEAR and
+// CMD_FRAME end this mode.)
#define CMD_DEMO 0x44
-// F
+// F (Frame, specify frame data)
#define CMD_FRAME 0x46
-// C
+// C (Clear screen)
#define CMD_CLEAR 0x43
+#define CMD_BLOCK_END 0x65
+
+
+// Our replies back to commands
+// ============================
+// General structure:
+// REPLY_BLOCK_START
+// REPLY_*
+// arguments
+// REPLY_BLOCK_END
+
+// r (start reply block)
+#define REPLY_BLOCK_START 0x72
+// A (ACK for any command that gives no other output)
+#define REPLY_ACK 0x41
+// E (Error, followed by error number)
+#define REPLY_ERROR 0x45
+// e (end reply block)
+#define REPLY_BLOCK_END 0x65
+
+// Possible error codes, following REPLY_ERROR
+// S (received something else where CMD_BLOCK_START was expected)
+#define ERROR_CMD_NO_START 0x53
+// U (unknown command)
+#define ERROR_CMD_UNKNOWN 0x55
+// P (received end block prematurely)
+#define ERROR_PREMATURE_END 0x50
+// R (received unexpected arguments where CMD_BLOCK_END expected)
+#define ERROR_ARG_UNEXPECTED 0x52
+// I (internal error, most likely not your fault)
+#define ERROR_INTERNAL 0x49
+
+
// =============================================================================
// Function prototypes
// =============================================================================
void SetPixel(int x, int y, int r, int g, int b);
void WriteLEDArray();
void checkSerial();
+void replyError(byte errorCode);
+void replyAck();
+void debugMsg(char * msg);
// =============================================================================
// Arduino entry points
@@ -134,39 +200,44 @@ void loop() {
if (boardLEDstate ^= 1) digitalWrite(13, HIGH);
else digitalWrite(13, LOW);
- // Compute a frame
- /*
- for(int x = 0; x < screenWidth; ++x) {
- for(int y = 0; y < screenHeight; ++y) {
- int r = (x * 255 / screenWidth + frame >> 9) & 0xFF;
- int g = (y * 255 / screenHeight + frame >> 10) & 0xFF;
- int b = (frame >> 11) & 0xFF;
- SetPixel(x, y, r, g, b);
+ // If we're in demo mode, happily animate
+ if (mode == DEMO_BLUE_HORIZ) {
+ // Shift everything by one column.
+ for(int x = screenWidth-1; x > 0; --x) {
+ for(int y = 0; y < screenHeight; ++y) {
+ LEDArray[x][y][0] = LEDArray[x-1][y][0];
+ LEDArray[x][y][1] = LEDArray[x-1][y][1];
+ LEDArray[x][y][2] = LEDArray[x-1][y][2];
+ }
}
- }
- */
-
- // Shift everything by one column.
- for(int x = screenWidth-1; x > 0; --x) {
+
+ // Add in a new column with random colors
for(int y = 0; y < screenHeight; ++y) {
- LEDArray[x][y][0] = LEDArray[x-1][y][0];
- LEDArray[x][y][1] = LEDArray[x-1][y][1];
- LEDArray[x][y][2] = LEDArray[x-1][y][2];
+ int sample = random(0,100);
+ LEDArray[0][y][0] = 255 * (sample > 97);
+ LEDArray[0][y][1] = 255 * (sample > 97);
+ LEDArray[0][y][2] = 127 * (sample > 60) + 127 * (sample > 80);
}
+ delay(200);
+ } else if (mode == DEMO_GREEN_VERT) {
+ // Shift everything by one column.
+ for(int y = screenHeight-1; y > 0; --y) {
+ for(int x = 0; x < screenWidth; ++x) {
+ LEDArray[x][y][0] = LEDArray[x][y-1][0];
+ LEDArray[x][y][1] = LEDArray[x][y-1][1];
+ LEDArray[x][y][2] = LEDArray[x][y-1][2];
+ }
+ }
+
+ // Add in a new column with random colors
+ for(int x = 0; x < screenWidth; ++x) {
+ int sample = random(0,100);
+ LEDArray[x][0][0] = 255 * (sample > 97);
+ LEDArray[x][0][1] = 127 * (sample > 60) + 127 * (sample > 80);;
+ LEDArray[x][0][2] = 255 * (sample > 97);
+ }
+ delay(200);
}
-
- // Add in a new column with random colors
- for(int y = 0; y < screenHeight; ++y) {
- LEDArray[0][y][0] = random(0,255);
- LEDArray[0][y][1] = random(0,255);
- LEDArray[0][y][2] = random(0,255);
- }
- int d = 250.0 * (sin(frame >> 4) + 1.0);
- /*char buf[100];
- snprintf(buf, 100, "%i", d);
- Serial.println(buf);*/
- delay(d);
- //delay(500);
// Actually send that frame
WriteLEDArray();
@@ -232,32 +303,98 @@ void WriteLEDArray() {
// Serial communication
// =============================================================================
void checkSerial() {
+ //Serial.println(" checkSerial ");
byte b;
long i = 0;
- enum { WAITING, INSIDE_COMMAND } state;
- state = WAITING;
+ // N.B. Next few variables are static because this function is left and
+ // entered multiple times in the course of a command.
+ // This may not be optimal.
+
+ // IDLE: We are awaiting the start of a command block.
+ // WAITING_COMMAND: We're inside a command block, awaiting a command.
+ // WAITING_ARGS: We're awaiting arguments to a command.
+ // WAITING_END: We have a command; we're awaiting end-of-block.
+ // FLUSHING: We're flushing input until the next command block starts, on
+ // account of receiving erroneous data.
+ static enum { IDLE, WAITING_COMMAND, WAITING_ARGS, WAITING_END, FLUSHING } state;
+ //state = IDLE;
+ // Set 'command' to the command we received (i.e. one of the CMD_* constants)
+ static int command;
+ // Offset to hold some additional state when in WAITING_ARGS state
+ // (e.g. current position for CMD_FRAME)
+ static int offset;
while (Serial.available() > 0) {
+ debugMsg(" byte ");
b = Serial.read();
switch(state) {
- case WAITING:
- if (b == BLOCK_START) {
- state = INSIDE_COMMAND;
+ case FLUSHING:
+ command = -1;
+ debugMsg(" flushing ");
+ if (b != CMD_BLOCK_START) {
+ break;
+ } else {
+ // Fall through to IDLE (no 'break' here)
+ }
+ case IDLE:
+ command = -1;
+ offset = 0;
+ debugMsg(" idle ");
+ if (b == CMD_BLOCK_START) {
+ state = WAITING_COMMAND;
} else {
- // Some sort of error here...
+ replyError(ERROR_CMD_NO_START);
+ state = FLUSHING;
}
break;
- case INSIDE_COMMAND:
+ case WAITING_COMMAND:
+ debugMsg(" waiting command ");
+ command = b;
switch(b) {
+ // Check that it's a valid command.
+ case CMD_PING:
+ case CMD_QUERY:
+ case CMD_CLEAR:
+ case CMD_DEMO:
+ // None of these commands take arguments
+ state = WAITING_END;
+ break;
+ case CMD_FRAME:
+ mode = SLAVE;
+ offset = 0;
+ state = WAITING_ARGS;
+ break;
+ default:
+ replyError(ERROR_CMD_UNKNOWN);
+ state = FLUSHING;
+ break;
+ }
+ break;
+ case WAITING_END:
+ debugMsg(" waiting end ");
+ if (b != CMD_BLOCK_END) {
+ replyError(ERROR_ARG_UNEXPECTED);
+ state = FLUSHING;
+ break;
+ }
+ // 'state' is IDLE until set otherwise
+ state = IDLE;
+ switch(command) {
case CMD_PING:
- Serial.println("Ping");
+ replyAck();
break;
case CMD_DEMO:
- Serial.println("Demo");
+ // If already in demo mode then toggle between demos;
+ // if not then put it in demo mode
+ if (mode == DEMO_BLUE_HORIZ) {
+ mode = DEMO_GREEN_VERT;
+ } else {
+ mode = DEMO_BLUE_HORIZ;
+ }
break;
case CMD_CLEAR:
- Serial.println("Clearing");
+ mode = SLAVE;
for(int x = 0; x < screenWidth; ++x) {
for(int y = 0; y < screenHeight; ++y) {
LEDArray[x][y][0] = 0;
@@ -265,32 +402,106 @@ void checkSerial() {
LEDArray[x][y][2] = 0;
}
}
- break;
- case CMD_FRAME:
- Serial.println("Frame...");
+ replyAck();
break;
case CMD_QUERY:
+ // TODO: Send this in binary instead. Use the protocol.
char buf[100];
snprintf(buf, 100, "%i %i", screenWidth, screenHeight);
Serial.print(buf);
break;
default:
- Serial.println("Unknown command!");
+ // This generally is not an error case
+ state = FLUSHING;
+ break;
+ }
+ break;
+ case WAITING_ARGS:
+ debugMsg(" waiting args ");
+ switch(command) {
+ case CMD_FRAME:
+ debugMsg(" in CMD_FRAME ");
+ // Avoid, very heavily, overrunning our array!
+ if (offset >= (screenWidth*screenHeight*3)) {
+ debugMsg(" offset too large ");
+ replyError(ERROR_ARG_UNEXPECTED);
+ state = FLUSHING;
+ break;
+ } else if (offset < 0) {
+ // WTF?
+ debugMsg(" offset < 0? ");
+ replyError(ERROR_INTERNAL);
+ state = FLUSHING;
+ break;
+ }
+ {
+ // Data here is RGBRGBRGBRGB... so we must divide by 3 to
+ // find out current offset.
+ int offset_xy = offset / 3;
+ // comp = component (0=R, 1=G, 2=B)
+ int comp = offset % 3;
+ // (1) offset_xy can't be negative, and the modulo with screenWidth
+ // limits it to [0,screenWidth], so that index should be safe.
+ // (2) offset < (screenWidth*screenHeight*3)
+ // => offset_xy < screenWidth*screenHeight
+ // => offset_xy / screenWidth < screenHeight
+ // So the second index should be safe.
+ // (3) comp is in [0,2] so that index should be safe.
+ LEDArray[offset_xy % screenWidth][offset_xy / screenWidth][comp] = b;
+
+ offset += 1;
+ if (offset == screenHeight*screenWidth*3) {
+ debugMsg(" hit offset end! ");
+ state = WAITING_END;
+ }
+ }
+ break;
+ default:
+ debugMsg(" premature end? ");
+ replyError(ERROR_PREMATURE_END);
+ state = FLUSHING;
break;
}
- state = WAITING;
+ debugMsg(" leaving waiting args ");
break;
default:
+ debugMsg(" unknown state? ");
+ // WTF?
+ replyError(ERROR_INTERNAL);
+ state = FLUSHING;
break;
}
++i;
}
+ /*
if (i) {
char buf[100];
snprintf(buf, 100, "Just received %i bytes over serial.", i);
//Serial.println(buf);
}
+ */
+}
+
+// Send an error reply with the given error code
+// errorCode should be one of the ERROR_* constants
+void replyError(byte errorCode) {
+ Serial.write(REPLY_BLOCK_START);
+ Serial.write(REPLY_ERROR);
+ Serial.write(errorCode);
+ Serial.write(REPLY_BLOCK_END);
+}
+
+void replyAck() {
+ Serial.write(REPLY_BLOCK_START);
+ Serial.write(REPLY_ACK);
+ Serial.write(REPLY_BLOCK_END);
+}
+
+void debugMsg(char * msg) {
+ #if DEBUG
+ Serial.println(msg);
+ #endif
}
// =============================================================================
Please sign in to comment.
Something went wrong with that request. Please try again.