diff --git a/Arduino/Webserver4Rainbow_v3.ino b/Arduino/Webserver4Rainbow_v3.ino deleted file mode 120000 index d635dc8..0000000 --- a/Arduino/Webserver4Rainbow_v3.ino +++ /dev/null @@ -1 +0,0 @@ -/Users/nrinas/ownCloud/Arduino/Webserver4Rainbow_v3/Webserver4Rainbow_v3.ino \ No newline at end of file diff --git a/Arduino/Webserver4Rainbow_v3.ino b/Arduino/Webserver4Rainbow_v3.ino new file mode 100644 index 0000000..fc89a96 --- /dev/null +++ b/Arduino/Webserver4Rainbow_v3.ino @@ -0,0 +1,817 @@ +// +// 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 +#define MY_CONTROL_PASS "Ss12345678" // need to be the same and 10 char length +#define MY_WRITE_PASS "Ww12345678" // need to be the same and 10 char length + + +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,0,79); +IPAddress gateway(192,168,0, 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(MY_CONTROL_PASS, 10) > -1) { // Streammode, expect frames + DEBUG_PRINTLN_TXT("Set mode to 1"); + setServerMode(1); + }else if(readBufferCompare2(MY_WRITE_PASS, 10) > -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); + myReadStatus = 0; + } + } + + // 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]; + // First 10 bytes is a key, following by the length + readBuffer[0] = myReadBuffer[10]; + readBuffer[1] = myReadBuffer[11]; + readBuffer[2] = myReadBuffer[12]; + readBuffer[3] = myReadBuffer[13]; + 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

")); + client.println(F("HTTP/1.1 200 OK")); + // 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(); + +} diff --git a/bin/Cube4Fun.zip b/bin/Cube4Fun.zip index 9570aff..928e32e 100644 Binary files a/bin/Cube4Fun.zip and b/bin/Cube4Fun.zip differ