diff --git a/Arduino/RainbowCube_Designmix.ino b/Arduino/RainbowCube_Designmix.ino new file mode 100644 index 0000000..15fb307 --- /dev/null +++ b/Arduino/RainbowCube_Designmix.ino @@ -0,0 +1,498 @@ +// +// RainbowCube +// Cube4Fun +// +// Created by Nikolai Rinas on 27.03.15. +// Copyright (c) 2015 Nikolai Rinas. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see + +#include +#include + +#define DEBUG + +#ifdef DEBUG + #define DEBUG_PRINTLN(x) Serial.println (x) + #define DEBUG_PRINT(x) Serial.print (x) + #define DEBUG_PRINTLN_TXT(x) Serial.println (F(x)) + #define DEBUG_PRINT_TXT(x) Serial.print (F(x)) +#else + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINT(x) + #define DEBUG_PRINTLN_TXT(x) + #define DEBUG_PRINT_TXT(x) +#endif + + +// New balanced colors +static unsigned char RED[256] = {255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,247,243,238,230,226,217,213,209, + 200,196,188,183,179,171,166,158,154,145,141,137,128,124,115,111,107,98,94,85,81,77,68,64,56,51,47,39,34, + 26,22,13,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,9,13,22,26,35,39,43,52,56, + 64,68,77,81,85,94,98,107,111,115,124,128,136,141,145,153,158,166,171,179,183,188,196,200,209,213,217,226, + 230,239,243,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0}; +static unsigned char GREEN[256] = {0,0,7,17,22,30,34,39,47,51,60,64,68,77,81,90,94,98,107,111,119,124,132,136,141,149,153,162,166,170,179, + 183,192,196,200,209,213,221,226,230,238,243,251,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,251,243,238,230,226,221,213,209,200,196, + 192,183,179,170,166,158,154,149,141,136,128,124,120,111,107,98,94,90,81,77,68,64,56,52,47,39,34,26,22,18,9, + 5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0}; +static unsigned char BLUE[256] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,9,18,22,26,34,39,47,52,56,64, + 68,77,81,90,94,98,107,111,120,124,128,136,141,149,154,158,166,170,179,183,188,196,200,209,213,221,226,230, + 238,243,251,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,251,243,239,234,226,221,213,209,200,196,192,183,179,171,166,162,153,149,141, + 136,132,124,120,111,107,103,94,90,81,77,68,64,60,52,47,39,35,30,22,17,255,0}; + +unsigned char plasmaMatrix[8][8]; +unsigned char plasmaCube[4][4][4]; +unsigned char buffer3D[4][4][4]; +byte displayLength = 0; +char blinkColor = 'r'; +boolean blinkSet = false; +boolean initAnim = true; +boolean newFrame = false; +unsigned char blinkCount = 0; +boolean streamMode = false; +long animation = 0; +unsigned long lastChangeTime = 0; +unsigned long streamStartedTime = 0; +const long animInterval = 300000; // 5 minutes +const long streamTimeoutInterval = 5000; // 5 seconds +#define BLINK_COUNT_MAX 7 +const long blinkInterval = 1000; // interval at which to blink (milliseconds) +unsigned long blinkPreviousMillis = 0; + +unsigned char z,x,y,colorshift=0; +int receivePart; + +// --------------- FUNCTIONS ---------------- // + +void genPlasmaMatrix() { + // Generate Plasma-Array + for(x = 0; x < 8; x++) { + for(y = 0; y < 8; y++) { + int color = int(32.0 + (32.0 * sin(x / 4.0)) + 32.0 + (32.0 * sin(y / 4.0))) / 2; + plasmaMatrix[x][y] = color; + } + } +} + +void genPlasmaMatrix2() { + // Generate Plasma2-Array + for(x = 0; x < 8; x++) { + for(y = 0; y < 8; y++) { + int color = int(32.0 + (32.0 * sin(x / 1.0)) + 32.0 + (32.0 * sin(y / 1.0))) / 2; + plasmaMatrix[x][y] = color; + } + } +} + +void genPlasmaCube() { + // Generate PlasmaCube-Array + for(x = 0; x < 4; x++) { + for(y = 0; y < 4; y++) { + for(z = 0; z < 4; z++) { + int color = int(32.0 + (32.0 * sin(x / 1.0))+ 32.0 + (32.0 * sin(y / 1.0)) + 32.0 + (32.0 * sin(z / 1.0))) / 3; + plasmaCube[x][y][z] = color; + } + } + } +} + +void genPlasmaCube2() { + // Generate PlasmaCube2-Array + for(x = 0; x < 4; x++) { + for(y = 0; y < 4; y++) { + for(z = 0; z < 4; z++) { + int color = int(32.0 + (32.0 * sin(x / 4.0))+ 32.0 + (32.0 * sin(y / 4.0)) + 32.0 + (32.0 * sin(z / 4.0))) / 3; + plasmaCube[x][y][z] = color; + } + } + } +} + +void genPlasmaCube3() { + // Generate PlasmaCube3-Array + for(x = 0; x < 4; x++) { + for(y = 0; y < 4; y++) { + for(z = 0; z < 4; z++) { + int color = int(32.0 + (32.0 * sin(x / 8.0))+ 32.0 + (32.0 * sin(y / 8.0)) + 32.0 + (32.0 * sin(z / 8.0))) / 3; + plasmaCube[x][y][z] = color; + } + } + } +} + +void setByteColor2D() { + unsigned char color255 = (plasmaMatrix[x][y] * 4 + colorshift ) % 256; // Transform to 8 Bit color + if ( color255 > 0 ) { + if ( color255 > 253 ) { // 254 and 255 are reserved + color255 = 253; + } + }else{ + color255 = 0; + } // Range 0 ... 253 + + Rb.setPixelXY(x,y,RED[color255],GREEN[color255],BLUE[color255]); +} + +void setByteColor3D() { + unsigned char color255 = (plasmaCube[x][y][z] * 4 + colorshift ) % 256; // Transform to 8 Bit color + if ( color255 > 0 ) { + if ( color255 > 253 ) { // 254 and 255 are reserved + color255 = 253; + } + }else{ + color255 = 0; + } // Range 0 ... 253 + + Rb.setPixelZXY(z,x,y,RED[color255],GREEN[color255],BLUE[color255]); +} + + +void draw2D() { + for(x=0;x<8;x++) { + for(y=0;y<8;y++) { + setByteColor2D(); + } + } +} + +void draw3D() { + for(x=0;x<4;x++) { + for(y=0;y<4;y++) { + for(z=0;z<4;z++) { + setByteColor3D(); + } + } + } +} + + +void drawMoodlamp() { + for(z=0; z<254 ;z++) { + for(x=0;x<8;x++) { + for(y=0;y<8;y++) { + //Paint random colors + Rb.setPixelXY(x,y,RED[z],GREEN[z],BLUE[z]); //uses R, G and B color bytes + } + } + delay(100); + } + for(z=253; z > 0 ;z--) { + for(x=0;x<8;x++) { + for(y=0;y<8;y++) { + //Paint random colors + Rb.setPixelXY(x,y,RED[z],GREEN[z],BLUE[z]); //uses R, G and B color bytes + } + } + delay(100); + } +} + +void drawNewFrame() { + for(x=0;x<4;x++) { + for(y=0;y<4;y++) { + for(z=0;z<4;z++) { + Rb.setPixelZXY(z,x,y,(RED[buffer3D[x][y][z]]),(GREEN[buffer3D[x][y][z]]),(BLUE[buffer3D[x][y][z]])); //uses R, G and B color bytes + } + } + } + newFrame = false; + // Update the timeout timer + streamStartedTime = millis(); + + if (displayLength > 0 ) { + delay(displayLength*100); // Delay (max 6 seconds) + } + displayLength = 0; +} + +void performBlink() { + unsigned char r = 255; + unsigned char g = 0; + unsigned char b = 0; + + switch (blinkColor) { + case 'g' : // green + r = 0; + g = 255; + b = 0; + break; + case 'b' : // blue + r = 0; + g = 0; + b = 255; + break; + // default red + } + + unsigned long currentMillis = millis(); + if (currentMillis - blinkPreviousMillis >= blinkInterval) { + // save the last time you blinked the LED + blinkPreviousMillis = currentMillis; + blinkCount++; + } + + if ( blinkCount % 2 == 0 ) { + for(x=0;x<8;x++) { + for(y=0;y<8;y++) { + Rb.setPixelXY(x,y,r,g,b); //uses R, G and B color bytes + } + } + }else{ + Rb.blankDisplay(); + } + delay(100); + + if ( blinkCount > BLINK_COUNT_MAX ) { + blinkSet = false; + blinkCount = 0; + } +} + +void performClearScreen() { + Rb.blankDisplay(); +} + +void changeAnim() { + animation = random(0,5); + initAnim = true; +} + + +// function that executes whenever data is received from master +// this function is registered as an event, see setup() +void receiveEvent(int howMany) { + if(Wire.available() > 0) { + // receive Data in parts + if ( receivePart > 0 && receivePart < 3 && Wire.available() == 32) { + switch (receivePart) { + unsigned char receivedValue; + case 1 : // Part 1 + for(y=0;y<4;y++) { + for(z=0;z<4;z++) { + receivedValue = Wire.read(); + buffer3D[0][y][z] = receivedValue; + //DEBUG_PRINT(receivedValue); + } + } + for(y=0;y<4;y++) { + for(z=0;z<4;z++) { + receivedValue = Wire.read(); + buffer3D[1][y][z] = receivedValue; + //DEBUG_PRINT(receivedValue); + } + } + receivePart++; + break; + case 2 : // Part2 + for(y=0;y<4;y++) { + for(z=0;z<4;z++) { + receivedValue = Wire.read(); + buffer3D[2][y][z] = receivedValue; // integer read + //DEBUG_PRINT(receivedValue); + } + } + for(y=0;y<4;y++) { + for(z=0;z<4;z++) { + receivedValue = Wire.read(); + buffer3D[3][y][z] = receivedValue; // integer read + //DEBUG_PRINT(receivedValue); + } + } + if ( true == streamMode ) { + receivePart = 0; + newFrame = true; + }else{ + receivePart++; + } + break; + default: + receivePart++; + } + return; // go back + + } + if ( receivePart > 3 ) { // Reset if something went wrong + DEBUG_PRINTLN_TXT("Incomplete data received. receivePart > 3"); + receivePart = 0; + } + + + char c = Wire.read(); // first char identify the command + //DEBUG_PRINTLN_TXT("Data received"); + //DEBUG_PRINT(c); + switch (c) { + case 'f' : // Frame was sent + DEBUG_PRINTLN_TXT("Frame receive started"); + receivePart = 1; + break; + case 'F' : // Frame completed + displayLength = Wire.read(); // how long should it be displayed + DEBUG_PRINT(displayLength); + if ( receivePart == 3 ) { + newFrame = true; + DEBUG_PRINTLN_TXT("Frame data received"); + } + receivePart = 0; // Reset the counter + break; + case 'b' : // Blink command + blinkSet = true; + blinkColor = Wire.read(); + blinkCount = 0; + DEBUG_PRINTLN_TXT("Blink data received"); + break; + case 'd' : // Delete screen + performClearScreen(); + DEBUG_PRINTLN_TXT("Clear screen received"); + break; + case 's' : // Stream mode + DEBUG_PRINTLN_TXT("Stream mode set"); + streamMode = true; + receivePart = 1; + streamStartedTime = millis(); + break; + case 'n' : // Next frame + //DEBUG_PRINTLN_TXT("Expect next frame"); + receivePart = 1; + break; + case 'S' : // Stream mode ended + DEBUG_PRINTLN_TXT("Stream mode ended"); + streamMode = false; + receivePart = 0; + return; + break; + } + + if ( newFrame == false && blinkSet == false && receivePart < 1) { + DEBUG_PRINTLN_TXT("Incomplete data received"); + DEBUG_PRINTLN(streamMode); + //streamMode = false; + blinkSet = false; + receivePart = 0; + // clear rest buffer + while ( Wire.available() > 0 ) { + char c = Wire.read(); + } + } + } +} + + +// ---------------- MAIN --------------- // + +void setup() { + Rb.init(); //initialize Rainbowduino driver + Wire.begin(2); // initialize wire connection as slave #2 + Wire.onReceive(receiveEvent); // set function to be called + +#ifdef DEBUG + Serial.begin(9600); +#endif + DEBUG_PRINTLN_TXT("Empfaenger 2"); + + randomSeed(analogRead(0)); // Init randomizer + + changeAnim(); // First random animation +} + + +void loop() +{ + unsigned long currentMillis; + + if ( true == newFrame ) { + drawNewFrame(); + } + // In the streamMode we are only waiting for new frames and display them + if ( false == streamMode ) { + // Event based animations + if ( true == blinkSet ) { + performBlink(); + }else{ + currentMillis = millis(); + if ( currentMillis - lastChangeTime > animInterval) { + lastChangeTime = currentMillis; + changeAnim(); + } + + switch (animation) { + case 0: // Plasma1 + // Create the animation array + if ( true == initAnim ) { + genPlasmaMatrix(); + initAnim = false; + } + draw2D(); + break; + case 1: // Plasma2 + // Create the animation array + if ( true == initAnim ) { + genPlasmaMatrix2(); + initAnim = false; + } + draw2D(); + break; + case 2: // Cube3 + if ( true == initAnim ) { + genPlasmaCube3(); + initAnim = false; + } + draw3D(); + break; + case 3: // Cube1 + if ( true == initAnim ) { + genPlasmaCube(); + initAnim = false; + } + draw3D(); + break; + case 4: // Cube2 + if ( true == initAnim ) { + genPlasmaCube2(); + initAnim = false; + } + draw3D(); + break; + default: + if ( true == initAnim ) { + genPlasmaCube(); + initAnim = false; + } + draw3D(); + } + delay(100); + colorshift = colorshift + 1; + } + }else{ + // Timeout for unexpected situations + currentMillis = millis(); + if ( currentMillis - streamStartedTime > streamTimeoutInterval) { + DEBUG_PRINTLN_TXT("Stream Timeout"); + newFrame = false; + streamMode = false; + } + } + +} + diff --git a/Arduino/Webserver4Rainbow_v3.ino b/Arduino/Webserver4Rainbow_v3.ino new file mode 100644 index 0000000..fb0b109 --- /dev/null +++ b/Arduino/Webserver4Rainbow_v3.ino @@ -0,0 +1,812 @@ +// +// Webserver4Rainbow +// Cube4Fun +// +// Created by Nikolai Rinas on 27.03.15. +// Copyright (c) 2015 Nikolai Rinas. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see + +#define DEBUG + +#include +#include +#include +#include +//#include + + + +#ifdef DEBUG + #define DEBUG_PRINTLN(x) Serial.println (x) + #define DEBUG_PRINT(x) Serial.print (x) + #define DEBUG_PRINT2(x,y) Serial.print (x,y) + #define DEBUG_PRINTLN_TXT(x) Serial.println (F(x)) + #define DEBUG_PRINT_TXT(x) Serial.print (F(x)) +#else + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINT(x) + #define DEBUG_PRINT2(x,y) + #define DEBUG_PRINTLN_TXT(x) + #define DEBUG_PRINT_TXT(x) +#endif + +#define INBUFFER 65 +#define MY_CUBE_ADDR 2 +#define MAX_SEND_RETRY 10 +#define SERVER_PORT 8081 +#define ANIM_FILE_NAME "ANIMS.FRM" +#define MAX_ANIMKEY_LENGTH 12 +#define MY_STREAM_KEEPALIVE_TIME 2000 // Send at least every 2 seconds one frame +//#define MY_STREAM_TIMEOUT 30000 // Reset broken connection + +unsigned char myReadBuffer[INBUFFER]; // string for fetching data from address +unsigned char myReadBufferCount = 0; + +//int getState = 0; +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { + 0x90, 0xA2, 0xDA, 0x06, 0x00, 0x2A +}; +// Fallback defaults +IPAddress ip(192,168,1,79); +IPAddress gateway(192,168,1, 1); +IPAddress subnet(255, 255, 255, 0); +//unsigned char x,y,z; +unsigned char mySendBuffer[32]; +unsigned char myReceiveBuffer[64]; + +unsigned char serverMode = 0; // Default waiting for commands/requests + +unsigned char myAnimationCount = 0; +unsigned int _animationSpeed = 0; +unsigned long _animationLength = 0; +unsigned long _animationStartPos = 0; +unsigned long _animationEndPos = 0; +unsigned int _animationActFrame = 0; +unsigned long _previousMillis = 0; // will store last time animation frame was sent + +unsigned long _lastTimeFrameSent = 0; // general check when last frame was sent, use for timeout + +// often used chars +unsigned const char lc_b = 'b'; +unsigned const char lc_g = 'g'; +unsigned const char lc_r = 'r'; +unsigned const char lc_n = 'n'; +unsigned const char lc_f = 'f'; +unsigned const char lc_F = 'F'; +unsigned const char lc_s = 's'; +unsigned const char lc_S = 'S'; +unsigned const char lc_G = 'G'; +unsigned const char lc_E = 'E'; +unsigned const char lc_T = 'T'; +unsigned const char lc_coma = ','; +unsigned const char lc_space = ' '; +unsigned const char lc_slash = '/'; +unsigned const char lc_question = '?'; +unsigned const char lc_newline = '\n'; +unsigned const char lc_return = '\r'; + + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 80 is default for HTTP): +//EthernetClient client; + +// Initialize the Ethernet server library +// with the IP address and port you want to use +EthernetServer server(SERVER_PORT); + + +// --- FUNCTIONS --- // +// String functions START +void appendChar2readBuffer(unsigned char c) { + myReadBuffer[myReadBufferCount] = c; + myReadBufferCount++; +} +int readBufferCompare2(const char* c, int count) { + int result = 0 - count; + for (unsigned char x=0;x "); + DEBUG_PRINTLN(newMode); + // Transition from 0 -> 1 + if ( serverMode == 0 && newMode == 1 ) { + clearBufferedFrame(); + sendStartFrameStream(); + // Transition from 0 -> 2 + }else if ( serverMode == 0 && newMode == 2 ) { + clearBufferedFrame(); + sendStartFrameStream(); + // Transition from 1 -> 0 + }else if ( serverMode == 1 && newMode == 0 ) { + sendEndFrameStream(); + // Transition from 2 -> 0 + }else if ( serverMode == 2 && newMode == 0 ) { + sendEndFrameStream(); + clearSavedAnimation(); + // Transition from 2 -> 1 + }else if ( serverMode == 2 && newMode == 1 ) { + // Don't display saved animation again + // this behaviour may be changed if prefered + clearBufferedFrame(); + clearSavedAnimation(); + } + // Transition from 1 -> 2 + // nothing special to do + + serverMode = newMode; + } +} + +unsigned char checkRequest(unsigned char c, unsigned char readState) { +// Check for GET request and safe the data + switch (readState) { + case 0: + if ( c == lc_G ) { readState = 1; }; break; + case 1: + if ( c == lc_E ) { readState = 2; }else{ readState = 0; }; break; + case 2: + if ( c == lc_T ) { readState = 3; }else{ readState = 0; }; break; + case 3: + if ( c == lc_space ) { readState = 4; }else{ readState = 0; }; break; + case 4: + if ( c == lc_slash ) { readState = 5; }else{ readState = 0; }; break; + case 5: + if ( c == lc_question ) { readState = 6; }else{ readState = 0; }; break; + case 6: + // Falls der Buffer noch nicht voll + if ( myReadBufferCount < INBUFFER ) { + if ( c == lc_space || c == lc_return || c == lc_newline ) { + // Less data than buffersize + readState = 7; + }else{ + // Fille the buffer + appendChar2readBuffer(c); + } + }else{ + // More data than buffersize + readState = 7; + } + break; + default: + // Reset the state for unknown data + readState = 0; + } + return readState; +} + +void processRequest(EthernetClient client) { + boolean debugFunc = false; +#ifdef DEBUG + // check the form + if(readBufferCompare2("2=red", 5) > -1) { + sentBlink(lc_r); + DEBUG_PRINTLN_TXT("Red blink sent"); + debugFunc = true; + }else if(readBufferCompare2("3=blue", 6) > -1) { + sentBlink(lc_b); + DEBUG_PRINTLN_TXT("Blue blink sent"); + debugFunc = true; + }else if(readBufferCompare2("4=green", 7) > -1) { + sentBlink(lc_g); + DEBUG_PRINTLN_TXT("Green blink sent"); + debugFunc = true; + }else if(readBufferCompare2("5=send", 6) > -1) { + setFrame(); + DEBUG_PRINTLN_TXT("Frametest started"); + debugFunc = true; + }else if(readBufferCompare2("6=start", 7) > -1) { + sendStream(); + DEBUG_PRINTLN_TXT("Streamtest started"); + debugFunc = true; + }else if(readBufferCompare2("7=file", 6) > -1) { + printFileContent(); + DEBUG_PRINTLN_TXT("File content"); + debugFunc = true; + } +#endif + + if ( debugFunc == false ) { + if (readBufferCompare2("Ss", 2) > -1) { // Streammode, expect frames + DEBUG_PRINTLN_TXT("Set mode to 1"); + setServerMode(1); + }else if(readBufferCompare2("Ww", 2) > -1 ) { // Stream write mode + writeAnimationSDCard(client); // blocking mode !! + }else{ + checkAnimationSDCard(); // Check if we have animations on the SD card + } + } +} + +void wireSendBytes(const uint8_t *data, size_t quantity) { + boolean try_again = true; + uint8_t error; + unsigned char send_retries = 0; + while ( try_again == true ) { + Wire.beginTransmission(MY_CUBE_ADDR); + if ( quantity == 1 ) { + Wire.write(data[0]); + }else{ + Wire.write(data, quantity); + } + error = Wire.endTransmission(); + if ( error == 0 || send_retries > MAX_SEND_RETRY ) { + // everything went well or timeout + try_again = false; + }else{ + // Something went wrong + DEBUG_PRINTLN_TXT("Send failed"); + } + delayMicroseconds(10); + } + send_retries++; +} + + +void sentBlink(unsigned char color) { + mySendBuffer[0] = lc_b; + mySendBuffer[1] = color; + wireSendBytes(mySendBuffer, 2); +} + +void setFrame() { + int color = 0; + int pos = random(0,64); + mySendBuffer[0] = lc_f; + wireSendBytes(mySendBuffer, 1); + for (unsigned char x=0;x<32;x++) { + if ( x == pos ) { color = random(0,64);} + mySendBuffer[x] = color; + color = 0; + } + wireSendBytes(mySendBuffer, 32); + for (unsigned char x=0;x<32;x++) { + if ( x+32 == pos ) { color = random(0,64);} + mySendBuffer[x] = color; + color = 0; + } + wireSendBytes(mySendBuffer, 32); + mySendBuffer[0] = lc_F; + mySendBuffer[1] = 50; // Show for two seconds + wireSendBytes(mySendBuffer, 2); +} + +void sendStream() { + unsigned char pos; + + // Set to the stream mode + mySendBuffer[0] = lc_s; + wireSendBytes(mySendBuffer,1); + // Begin data transmission + for ( pos=0;pos<64;pos++ ) { + //DEBUG_PRINT_TXT("Sende frame: "); + //DEBUG_PRINTLN(pos); + // Send walking led + int color = 0; + for (unsigned char x=0;x<32;x++) { // First half frame + if ( x == pos ) { color = 128;} + mySendBuffer[x] = color; + color = 0; + } + wireSendBytes(mySendBuffer, 32); + + for (unsigned char x=0;x<32;x++) { + if ( x+32 == pos ) { color = 128;} + mySendBuffer[x] = color; + color = 0; + } + wireSendBytes(mySendBuffer, 32); + + // call for new frame + if ( pos<63 ) { + mySendBuffer[0] = lc_n; + wireSendBytes(mySendBuffer, 1); + } + + // Delay for 200ms + delay(50); + } + // End the stream mode + mySendBuffer[0] = lc_S; + wireSendBytes(mySendBuffer, 1); + +} + +void sendBufferedFrame() { + // Split the received Buffer in two parts and send them to the LED shield + + for (unsigned char x=0;x<32;x++) { // First half frame + mySendBuffer[x] = myReceiveBuffer[x]; + } + wireSendBytes(mySendBuffer, 32); + for (unsigned char x=0;x<32;x++) { // Second half frame + mySendBuffer[x] = myReceiveBuffer[x+32]; + } + wireSendBytes(mySendBuffer, 32); + + mySendBuffer[0] = lc_n; + wireSendBytes(mySendBuffer, 1); + + // Remember when the frame was sent las time + _lastTimeFrameSent = millis(); +} + +void sendEmptyFrame() { // Because we need last frame for the LED shield + for (unsigned char x=0;x<32;x++) { // First half frame + mySendBuffer[x] = 0; + } + wireSendBytes(mySendBuffer, 32); + wireSendBytes(mySendBuffer, 32); +} + +int sendFrameCached(unsigned char c, int bufferSize) { + myReceiveBuffer[bufferSize] = c; + if ( bufferSize<63 ) { + bufferSize++; + }else{ + // send the buffer + sendBufferedFrame(); + bufferSize=0; + } + return bufferSize; +} + +void printFileContent() { + File myProjectFile = SD.open(ANIM_FILE_NAME, FILE_READ); + if (myProjectFile) { + while (myProjectFile.available()) { + unsigned char myC = myProjectFile.read(); + DEBUG_PRINTLN(myC); + } + } +} + + +void checkAnimationSDCard() { + // Open File for read + File myProjectFile = SD.open(ANIM_FILE_NAME, FILE_READ); + if (myProjectFile) { + DEBUG_PRINTLN_TXT("Reading keys"); + + unsigned char myReadStatus = 0; // 0 = expect key + unsigned char myC = 0; + unsigned char myKeyBuffer[MAX_ANIMKEY_LENGTH]; // maximum length for the key + unsigned char myKeyLength = 0; + unsigned char myIntBuffer[2]; // Byte to int + unsigned char myIntBufferLength = 0; + + while (myProjectFile.available() && myReadStatus < 20) { + myC = myProjectFile.read(); + + switch (myReadStatus) { + case 0: // expect key + if ( myC == lc_coma ) { + myReadStatus = 1; // Startkey found + } + break; + case 1: // expect keyline confirmation + if ( myC == lc_F ) { + myReadStatus = 2; // Key-line confirmed + }else{ + myReadStatus = 0; // Reset + } + break; + case 2: // Reading animation key + if ( myC == lc_coma ) { + myReadStatus = 3; // change to read next element + }else if ( myKeyLength < MAX_ANIMKEY_LENGTH ) { // Fill till max buffer + myKeyBuffer[myKeyLength] = myC; + myKeyLength++; + } + break; + case 3: // Save playtime + if ( myIntBufferLength < 2 ) { // Save playtime value + myIntBuffer[myIntBufferLength] = myC; + myIntBufferLength++; + } + if ( myIntBufferLength == 2 ) { // Buffer is full, set values + unsigned int animationLength = word(myIntBuffer[1], myIntBuffer[0]); + // Save the time the animation will end + _animationLength = animationLength * 1000 + millis(); + // clear buffer + myIntBufferLength = 0; + // expect next value + myReadStatus = 4; + } + break; + case 4: // Save speed + if ( myIntBufferLength < 2 ) { // Save speed value + myIntBuffer[myIntBufferLength] = myC; + myIntBufferLength++; + } + if ( myIntBufferLength == 2 ) { // Buffer is full, set values + _animationSpeed = word(myIntBuffer[1], myIntBuffer[0]); + // clear buffer + myIntBufferLength = 0; + // expect next value + myReadStatus = 5; + } + break; + case 5: // Save frames + if ( myIntBufferLength < 2 ) { // Save speed value + myIntBuffer[myIntBufferLength] = myC; + myIntBufferLength++; + } + if ( myIntBufferLength == 2 ) { // Buffer is full, set values + // set to complete read + myReadStatus = 6; + unsigned int animationFrames = word(myIntBuffer[1], myIntBuffer[0]); + // Calculate start and end position + _animationStartPos = myProjectFile.position() + 1; + _animationEndPos = _animationStartPos + 65 * animationFrames; + // Check if the key matches + if(readBufferCompare2(reinterpret_cast(myKeyBuffer), myKeyLength) > -1) { + // We found animation + DEBUG_PRINTLN_TXT("Found animation"); + myReadStatus = 20; // End search, we found our animation + }else{ + DEBUG_PRINTLN_TXT("No animation found"); + if (_animationEndPos > _animationStartPos ) { + // Goto next frame + myProjectFile.seek(_animationEndPos); + } + } + + // clear buffer + myIntBufferLength = 0; + } + break; + case 6: + if( myC == lc_newline ) { // uncomplete keyline or no animation found + // check for values + if ( _animationEndPos > 0 && _animationStartPos > 0 && _animationSpeed > 0 ) { + DEBUG_PRINTLN_TXT("Anim key found"); + }else{ + DEBUG_PRINTLN_TXT("--keyline failed--"); + // Reset everything + myReadStatus = 0; + myKeyLength = 0; + myIntBufferLength = 0; + clearSavedAnimation(); + } + } + break; + } + } + } + myProjectFile.close(); +} + +boolean readAnimationSD() { + unsigned char myC; + unsigned char myBytes = 0; + // Read SD Card + File myProjectFile = SD.open(ANIM_FILE_NAME, FILE_READ); + // Goto start position + if ( myProjectFile.available() ) { + unsigned long _animationPos = _animationStartPos + _animationActFrame * 65; // startPos + offset + if ( _animationPos < _animationEndPos ) { + myProjectFile.seek(_animationPos); + }else{ + _animationActFrame = 0; // Start at the first frame again + myProjectFile.seek(_animationStartPos); + } + } + // Read one frame + while ( myProjectFile.available() && myBytes < 64 ) { + myC = myProjectFile.read(); + myReceiveBuffer[myBytes] = myC; + myBytes++; + } + + // close the file: + myProjectFile.close(); + + // If we have complete frame, return true + if ( myBytes > 63 ) { + // read successfull + _animationActFrame++; + return true; + }else{ + return false; + } +} + +unsigned long byte2Long(unsigned char* byteArray) { + // little endian conversion + unsigned long retval; + retval = (unsigned long) byteArray[3] << 24 | (unsigned long) byteArray[2] << 16; + retval |= (unsigned long) byteArray[1] << 8 | byteArray[0]; + return retval; +} +void writeAnimationSDCard(EthernetClient client) { + // Send answer + unsigned char readBuffer[4]; + readBuffer[0] = myReadBuffer[2]; + readBuffer[1] = myReadBuffer[3]; + readBuffer[2] = myReadBuffer[4]; + readBuffer[3] = myReadBuffer[5]; + unsigned long fileSize = byte2Long(readBuffer); + +// DEBUG_PRINT_TXT("fileSizeBuffer: "); + for (unsigned char i=0; i<4; i++ ) { + client.write(readBuffer[i]); +// DEBUG_PRINT(readBuffer[i]); + } + client.write(lc_return); + client.write(lc_newline); +// DEBUG_PRINTLN_TXT(" "); + + + +// DEBUG_PRINT_TXT("Filesize expected:"); +// DEBUG_PRINTLN(fileSize); + + // Blocking mode to write receiving data to file + if (client) { + // Remove file if exists + if ( SD.exists(ANIM_FILE_NAME) ) { + SD.remove(ANIM_FILE_NAME); + } + + File myProjectFile = SD.open(ANIM_FILE_NAME, FILE_WRITE); + unsigned long receivedBytes = 0; + + if (myProjectFile) { + while (client.connected() && receivedBytes < fileSize) { + if (client.available()) { + unsigned char c = client.read(); + //writing bytes + myProjectFile.write(c); + receivedBytes++; + } + } + } +/* + if ( receivedBytes == fileSize ) { + DEBUG_PRINTLN_TXT("Complete data received\n"); + }else{ + DEBUG_PRINTLN_TXT("Data incomplete\n"); + } + DEBUG_PRINT_TXT("Expected:"); + DEBUG_PRINTLN(fileSize); + DEBUG_PRINT_TXT("Received:"); + DEBUG_PRINTLN(receivedBytes); +*/ + myProjectFile.close(); + } +} + +void displaySavedAnimation() { + if ( _animationLength > 0 ) { // Animation was set + unsigned long currentMillis = millis(); + // Display animation set over the network + if ( currentMillis < _animationLength ) { // __animationLength = startTime + animLengthTime + if ( _animationStartPos > 0 && _animationEndPos > 0 && _animationSpeed > 0){ // validity check + if ( _animationEndPos > _animationStartPos ) { + //DEBUG_PRINTLN_TXT("Setting mode to 2"); + setServerMode(2); // Switch the server mode + if ( currentMillis - _previousMillis >= _animationSpeed ) { // Sent data with the set speed + // save the last time you sent frame + _previousMillis = currentMillis; + // Fill Buffer + if ( true == readAnimationSD() ) { + // Send buffered frame + sendBufferedFrame(); + } + } + } + } + }else{ + // End the stream mode + setServerMode(0); + } + } +} + +void keepAliveFrame() { + // Check if we need to send a keep alive frame + if ( millis() - _lastTimeFrameSent > MY_STREAM_KEEPALIVE_TIME ) { + // Send buffered frame + sendBufferedFrame(); + } +} + +// --------- MAIN ---------- // + +void setup() { + // put your setup code here, to run once: + Wire.begin(); + +#ifdef DEBUG + Serial.begin(9600); +#endif + DEBUG_PRINTLN_TXT("Sender1"); + + DEBUG_PRINTLN_TXT("Init SD card..."); + // On the Ethernet Shield, CS is pin 4. It's set as an output by default. + // Note that even if it's not used as the CS pin, the hardware SS pin + // (10 on most Arduino boards, 53 on the Mega) must be left as an output + // or the SD library functions will not work. + // disable w5100 SPI + pinMode(10, OUTPUT); + //digitalWrite(10,HIGH); + + if (!SD.begin(4)) { + DEBUG_PRINTLN_TXT("init failed!"); + // TODO: deny some functions + }else{ + DEBUG_PRINTLN_TXT("init done."); + } + + // start the Ethernet connection: + Ethernet.begin(mac, ip, gateway, subnet); + +// ---------------------- +/* Sorry, but had to drop DHCP support due to missing memory +// If you have a MEGA, feel free to use it + if (Ethernet.begin(mac) == 0) { + DEBUG_PRINTLN_TXT("Failed to configure Ethernet using DHCP"); + DEBUG_PRINTLN_TXT("Using defaults 192.168.1.79"); + // initialize the ethernet device not using DHCP: + Ethernet.begin(mac, ip, gateway, subnet); + } +*/ +// ----------------------- + +#ifdef DEBUG + // print your local IP address: + DEBUG_PRINTLN_TXT("My IP: "); + for (byte thisByte = 0; thisByte < 4; thisByte++) { + // print the value of each byte of the IP address: + DEBUG_PRINT2(Ethernet.localIP()[thisByte], DEC); + DEBUG_PRINT("."); + } + DEBUG_PRINTLN_TXT(""); +#endif + + // Print free memory + //DEBUG_PRINT("freeMemory()="); + //DEBUG_PRINTLN(freeMemory()); +} + + +void loop() { + // Process Server requests + // listen for incoming clients + EthernetClient client = server.available(); + + if (client) { // Client connected and sending data + DEBUG_PRINTLN_TXT("Client attached"); + // Init status + unsigned char readState = 0; + unsigned char lastChar = 0; // Make sure you never check for 0-value !!! + int bufferSize = 0; + // If we receive data, interrupt other states + setServerMode(0); + + // Read incoming data + while (client.connected()) { + if (client.available()) { + // get incoming byte + unsigned char c = client.read(); + + // check for server mode + switch (serverMode) { + case 0: + // Checking incoming requests + readState = checkRequest(c, readState); + + // Processing if request is recognized + if ( readState == 7 ) { // HTTP GET recognized + processRequest(client); + // process only one time + readState = 0; + myReadBufferCount = 0; + } + break; + + case 1: + // Streaming frames + bufferSize = sendFrameCached(c, bufferSize); + + // Check for streaming end + if ( c == lc_S && lastChar == lc_s ) { // "sS" received + //sendEndFrameStream(); + setServerMode(0); + bufferSize = 0; + } + + break; + } + + // if we got whole HTTP Head and still waiting for commands, + // send HTTP answer + if ( serverMode == 0 && c == lc_newline && lastChar == lc_return ) { // \r\n was send + // send a standard http response header + client.println(F("HTTP/1.1 200 OK\nContent-Type: text/html\r\n\nRainbowduino Webserver

Rainbowduino Webserver 1.0



Functions:

Blink:
Frametest
Streamtest
Print file to serial

")); + // Close the connection + client.stop(); + } + + lastChar = c; + } + + // If we are in the streaming mode and don't receive any frames, + // check for timeout + if ( serverMode == 1 ) { + // keep the connection to the Rainbowduino alive + keepAliveFrame(); + } + } + + // give web browser time to receive the data + delay(1); + // close the connection + client.stop(); + } + + if ( serverMode == 1 && client == false) { + // If client closed the connection and we are still in the + // stream mode, reset to normal operation + setServerMode(0); + } + + // ------------------------------------------------// + // Display Animation + displaySavedAnimation(); + +}