My BTTF Display – Work in progress

Will write more, but just posting arduino sketch up for now

_BTTFv01

Posted in Uncategorized | Leave a comment

Arduino powered 300 RGB LED Display with Ethernet and SD card. The belated how to “guide”.

Apologies for the slapdash nature of this “How to”. I started with the best of intentions, and set out to create videos documenting everything I’ve done up to this point, after a 10 month break.

The construction of the display is straight forward and this video covers it well. The Fast-SPI explanation I think is fine, but as soon as I started to put together the video for how all the LED/pixel calculations work for the “Matrix” example, I realised I was going to bore the hell out of everyone who watched it.

The problem with code is that is difficult to explain without overcomplicating the matter, and this whole project is stupidly complex already.

So, instead of creating a whole series of step by step videos, I’ve spent quite a few hours annotating all of the code as clearly as I can. This comes with warnings.

There is absolutely no way that you will build exactly the same display that I’ve built, you will most likely build something better. The code will not transplant into your Arduino and work perfectly, there are many reasons for this, the main one being that my web server which is hosting my time and weather scraper, will not be the same as yours and as a result, that whole section will probably not work for you.

The point of me posting up this annotated code is that you can get the general gist of what I’ve done and use the individual functions for you in your own situation.

  • You want to read Bitmap images from an SD card? Check the function that does that and rip it out.
  • You want to read files from a website, use that bit.
  • You want to look at my php file to see how I set up the World Weather Online API and manipulated the data for my own purpose? Cool, go for it.
  • You want to recreate the Matrix, Snake or Raindrop generators. Cool.
  • You want to see how I bodged together a set of characters pixel by pixel and then displayed them? All here.
  • You want to laugh at my coding style. Keep it to yourself. This works. Might not be pretty, but neither is elitism.
  • Want to store data in the flash memory by using PROGMEM Have a butchers…

There are many tips I can give when setting out on a project like this, but these are key.

  • Don’t even bother with the wifi shield. It’s expensive, a pain to update the firmware, doesn’t like to work with certain versions of the Arduino IDE (And I’m running 4 versions).
  • Do make sure you solder the wires properly which loop around the back. I have a slightly loose connection somewhere and it’s very annoying.
  • Do spend time googling, the answers are out there. There are a lot of people out there who like nothing better than to disparage others with “Go away and learn C” when you just want to make something cool.

So here is the Opt300RGBEthAnnPub file

And here is the php file for the WWO and time calculator for your own website ardData

Also the files of the images that I’m reading off my SD card and the myData.txt file that the script it accessing SDCardContents

So what next? For this project, I want to get some weather icons alongside the temperature. I’m thinking about perspex rods to shine the light through in a fibre optic style.

 

Here for all the search engine indexing spider things, is all of my code…

// Darrenlloyd Gent 2013-2014
// This version 26/06/2014
// Under a whatever, do what you want licence.
// Most of this has been cobbled together from various places and I wasn't diligent enough to make notes.
// Fastled has been updated since I did all this. http://fastled.io/
// They deserve way more credit.

#include  //SD Library
#include "FastSPI_LED2.h" // FastSPI_LED2 library
#include <avr/pgmspace.h>
#include  // SPI library
#include  // Ethernet Library
//#include  // comment out
#define NUM_LEDS 300 // Defining the number of LEDs in the strip
CRGB leds[NUM_LEDS]; // Create the led Array which will contain the RGB values for each pixel 0 -299

// The offsets for the splash radius. Stored in Progmem as there wasn't enough space to store in normal memory
// Each splash 1-10 gets larger and larger needing more pixels.LEDs.
// This was much quicker than calculating the radius in realtime.
prog_char splash1[4][2] PROGMEM={{0,0},{0,1},{1,0},{1,1}}; 
prog_char splash2[8][2] PROGMEM = {{0,-1},{0,2},{-1,0},{1,-1},{-1,1},{1,2},{2,0},{2,1}};
prog_char splash3[12][2] PROGMEM = {{0,-2},{0,3},{-1,-1},{1,-2},{-1,2},{1,3},{-2,0},{2,-1},{-2,1},{2,2},{3,0},{3,1}};
prog_char splash4[16][2] PROGMEM = {{0,-3},{0,4},{-1,-2},{1,-3},{-1,3},{1,4},{-2,-1},{2,-2},{-2,2},{2,3},{-3,0},{3,-1},{-3,1},{3,2},{4,0},{4,1}};
prog_char splash5[36][2] PROGMEM = {{0,-4},{0,5},{-1,-3},{1,-4},{-1,4},{-1,-4},{1,5},{-1,5},{-2,-2},{2,-3},{-2,3},{-2,-3},{2,4},{2,-4},{-2,4},{2,5},{-3,-1},{3,-2},{-3,2},{-3,-2},{3,3},{3,-3},{-3,3},{3,4},{-4,0},{4,-1},{-4,1},{-4,-1},{4,2},{4,-2},{-4,2},{4,3},{5,0},{5,1},{5,-1},{5,2}};
prog_char splash6[28][2] PROGMEM = {{0,-5},{0,6},{1,-5},{-1,-5},{1,6},{-1,6},{-2,-4},{2,-5},{-2,5},{2,6},{-3,-3},{3,-4},{-3,4},{3,5},{-4,-2},{4,-3},{-4,3},{4,4},{-5,0},{-5,1},{-5,-1},{5,-2},{-5,2},{5,3},{6,0},{6,1},{6,-1},{6,2}};
prog_char splash7[32][2] PROGMEM= {{0,-6},{0,7},{1,-6},{-1,-6},{1,7},{-1,7},{-2,-5},{2,-6},{-2,6},{2,7},{-3,-4},{3,-5},{-3,5},{3,6},{-4,-3},{4,-4},{-4,4},{4,5},{-5,-2},{5,-3},{-5,3},{5,4},{-6,0},{-6,1},{-6,-1},{6,-2},{-6,2},{6,3},{7,0},{7,1},{7,-1},{7,2}};
prog_char splash8[36][2] PROGMEM= {{0,-7},{0,8},{1,-7},{-1,-7},{1,8},{-1,8},{-2,-6},{2,-7},{-2,7},{2,8},{-3,-5},{3,-6},{-3,6},{3,7},{-4,-4},{4,-5},{-4,5},{4,6},{-5,-3},{5,-4},{-5,4},{5,5},{-6,-2},{6,-3},{-6,3},{6,4},{-7,0},{-7,1},{-7,-1},{7,-2},{-7,2},{7,3},{8,0},{8,1},{8,-1},{8,2}};
prog_char splash9[68][2] PROGMEM= {{0,-8},{0,9},{1,-8},{-1,-8},{1,9},{-1,9},{-2,-7},{2,-8},{-2,8},{-2,-8},{2,9},{-2,9},{-3,-6},{3,-7},{-3,7},{-3,-7},{3,8},{3,-8},{-3,8},{3,9},{-4,-5},{4,-6},{-4,6},{-4,-6},{4,7},{4,-7},{-4,7},{4,8},{-5,-4},{5,-5},{-5,5},{-5,-5},{5,6},{5,-6},{-5,6},{5,7},{-6,-3},{6,-4},{-6,4},{-6,-4},{6,5},{6,-5},{-6,5},{6,6},{-7,-2},{7,-3},{-7,3},{-7,-3},{7,4},{7,-4},{-7,4},{7,5},{-8,0},{-8,1},{-8,-1},{8,-2},{-8,2},{-8,-2},{8,3},{8,-3},{-8,3},{8,4},{9,0},{9,1},{9,-1},{9,2},{9,-2},{9,3}};
prog_char splash10[48][2] PROGMEM= {{0,10},{0,-9},{1,10},{-1,10},{1,-9},{-1,-9},{10,0},{10,1},{10,-1},{10,2},{10,-2},{10,3},{2,10},{-2,10},{2,-9},{-2,-9},{3,10},{-3,-8},{3,-9},{-3,9},{-4,-7},{4,-8},{-4,8},{4,9},{-5,-6},{5,-7},{-5,7},{5,8},{-6,-5},{6,-6},{-6,6},{6,7},{-7,-4},{7,-5},{-7,5},{7,6},{-8,-3},{8,-4},{-8,4},{8,5},{-9,0},{-9,1},{-9,-1},{-9,2},{-9,-2},{9,-3},{-9,3},{9,4}};

// My characters for each ASCII character, stored in ASCII order, hence the first 30 odd blank.
// A binary representation of the individual columns is a 10bit interger. 
// 1024 indicates end of the character so then the updatescroll moves onto the next letter

prog_uint16_t myChars[126][8] PROGMEM={
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  0     ,  0     ,  0     ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  382   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  14    ,  0     ,  14    ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  160   ,  496   ,  160   ,  496   ,  160   ,  1024  ,  1024  ,  1024  },
{  288   ,  336   ,  1016  ,  336   ,  144   ,  1024  ,  1024  ,  1024  },
{  136   ,  64    ,  32    ,  16    ,  136   ,  1024  ,  1024  ,  1024  },
{  118   ,  153   ,  169   ,  70    ,  160   ,  1024  ,  1024  ,  1024  },
{  7     ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  60    ,  66    ,  129   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  129   ,  66    ,  60    ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  17    ,  10    ,  31    ,  10    ,  17    ,  1024  ,  1024  ,  1024  },
{  32    ,  32    ,  248   ,  32    ,  32    ,  1024  ,  1024  ,  1024  },
{  256   ,  128   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  32    ,  32    ,  32    ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  256   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  448   ,  48    ,  14    ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  252   ,  322   ,  290   ,  274   ,  252   ,  1024  ,  1024  ,  1024  }, //0
{  8     ,  4     ,  510   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  }, //1
{  396   ,  322   ,  290   ,  274   ,  268   ,  1024  ,  1024  ,  1024  }, //2
{  258   ,  290   ,  290   ,  290   ,  220   ,  1024  ,  1024  ,  1024  }, //3
{  62    ,  32    ,  32    ,  504   ,  32    ,  1024  ,  1024  ,  1024  }, //4
{  318   ,  290   ,  290   ,  290   ,  194   ,  1024  ,  1024  ,  1024  }, //5
{  252   ,  290   ,  290   ,  290   ,  194   ,  1024  ,  1024  ,  1024  }, //6
{  2     ,  258    ,  194   ,  50    ,  14    ,  1024  ,  1024  ,  1024  }, //7
{  220   ,  290   ,  290   ,  290   ,  220   ,  1024  ,  1024  ,  1024  }, //8
{  28    ,  290   ,  290   ,  290   ,  252   ,  1024  ,  1024  ,  1024  }, //9
{  0     ,  144   ,  0     ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  }, //:
{  256   ,  144   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  }, //;
{  32    ,  80    ,  136   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  }, //< {  80    ,  80    ,  80    ,  80    ,  1024  ,  1024  ,  1024  ,  1024  }, //= {  136   ,  80    ,  32    ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  }, //>
{  2     ,  1     ,  177   ,  9     ,  6     ,  1024  ,  1024  ,  1024  }, //?
{  72    ,  180   ,  330   ,  330   ,  50    ,  68    ,  56    ,  1024  }, //@
{  508   ,  34    ,  34    ,  34    ,  508   ,  1024  ,  1024  ,  1024  }, //A
{  510   ,  290   ,  290   ,  290   ,  200   ,  1024  ,  1024  ,  1024  }, //B
{  120   ,  132   ,  258   ,  258   ,  258   ,  1024  ,  1024  ,  1024  }, //C
{  510   ,  258   ,  258   ,  132   ,  120   ,  1024  ,  1024  ,  1024  }, //D
{  510   ,  290   ,  290   ,  290   ,  290   ,  1024  ,  1024  ,  1024  }, //E
{  510   ,  34    ,  34    ,  34    ,  34    ,  1024  ,  1024  ,  1024  }, //F
{  120   ,  132   ,  258   ,  290   ,  482   ,  1024  ,  1024  ,  1024  }, //G
{  510   ,  32    ,  32    ,  32    ,  510   ,  1024  ,  1024  ,  1024  }, //H
{  258   ,  258   ,  510   ,  258   ,  258   ,  1024  ,  1024  ,  1024  }, //I
{  226   ,  258    ,  254   ,  2     ,  2     ,  1024  ,  1024  ,  1024  }, //J
{  510   ,  32    ,  80    ,  136   ,  262   ,  1024  ,  1024  ,  1024  }, //K
{  510   ,  256   ,  256   ,  256   ,  256   ,  1024  ,  1024  ,  1024  }, //L
{  510   ,  4     ,  24    ,  4     ,  510   ,  1024  ,  1024  ,  1024  }, //M
{  508   ,  2     ,  2     ,  2     ,  508   ,  1024  ,  1024  ,  1024  }, //M
{  252   ,  258   ,  258   ,  258   ,  252   ,  1024  ,  1024  ,  1024  }, //O
{  510   ,  34    ,  34    ,  34    ,  28    ,  1024  ,  1024  ,  1024  }, //P
{  252   ,  258   ,  386   ,  258   ,  764   ,  1024  ,  1024  ,  1024  }, //Q
{  510   ,  34    ,  34    ,  100   ,  408   ,  1024  ,  1024  ,  1024  }, //R
{  284   ,  290   ,  290   ,  290   ,  194   ,  1024  ,  1024  ,  1024  }, //S
{  2     ,  2     ,  510   ,  2     ,  2     ,  1024  ,  1024  ,  1024  }, //T
{  254   ,  256   ,  256   ,  256   ,  254   ,  1024  ,  1024  ,  1024  }, //U
{  126   ,  128   ,  256   ,  128   ,  126   ,  1024  ,  1024  ,  1024  }, //V
{  254   ,  256   ,  224   ,  256   ,  254   ,  1024  ,  1024  ,  1024  }, //W
{  398   ,  80    ,  32    ,  80    ,  398   ,  1024  ,  1024  ,  1024  }, //X
{  6     ,  24    ,   480  ,  24    ,  6     ,  1024  ,  1024  ,  1024  }, //Y
{  386   ,  322   ,  290   ,  274   ,  270   ,  1024  ,  1024  ,  1024  }, //Z
{  255   ,  129   ,  129   ,  129   ,  1024  ,  1024  ,  1024  ,  1024  }, //(
{  3     ,  12    ,  48    ,  192   ,  1024  ,  1024  ,  1024  ,  1024  }, //backslash
{  129   ,  129   ,  129   ,  255   ,  1024  ,  1024  ,  1024  ,  1024  }, //)
{  4     ,  2     ,  1     ,  2     ,  4     ,  1024  ,  1024  ,  1024  }, //
{  128   ,  128   ,  128   ,  128   ,  128   ,  1024  ,  1024  ,  1024  }, //_
{  1     ,  2     ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  }, //`
{  112   ,  136   ,  136   ,  136   ,  112   ,  128   ,  1024  ,  1024  }, //a
{  255   ,  136   ,  136   ,  136   ,  112   ,  1024  ,  1024  ,  1024  }, //b
{  112   ,  136   ,  136   ,  136   ,  1024  ,  1024  ,  1024  ,  1024  },//c
{  112   ,  136   ,  136   ,  136   ,  127   ,  1024  ,  1024  ,  1024  },//d
{  112   ,  168   ,  168   ,  168   ,  16    ,  1024  ,  1024  ,  1024  },//e
{  8     ,  254   ,  9     ,  9     ,  1024  ,  1024  ,  1024  ,  1024  },//f
{  112   ,  648   ,  648   ,  648   ,  496   ,  1024  ,  1024  ,  1024  },//g
{  255   ,  8     ,  8     ,  8     ,  240   ,  1024  ,  1024  ,  1024  },//h
{  250   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },//i
{  384   ,  512   ,  506   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },//j
{  255   ,  32    ,  80    ,  136   ,  1024  ,  1024  ,  1024  ,  1024  },//k
{  127   ,  128   ,  128   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },//l
{  240   ,  8     ,  48    ,  8     ,  240   ,  1024  ,  1024  ,  1024  },//m
{  240   ,  8     ,  8     ,  8     ,  240   ,  1024  ,  1024  ,  1024  },//n
{  112   ,  136   ,  136   ,  136   ,  112   ,  1024  ,  1024  ,  1024  },//o
{  1016  ,  136   ,  136   ,  136   ,  112   ,  1024  ,  1024  ,  1024  },//p
{  112   ,  136   ,  136   ,  1016  ,  256   ,  1024  ,  1024  ,  1024  },//q
{  240   ,  8     ,  8     ,  8     ,  1024  ,  1024  ,  1024  ,  1024  },//r
{  144   ,  168   ,  168   ,  72    ,  1024  ,  1024  ,  1024  ,  1024  },//s
{  16    ,  124   ,  144   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },//t
{  120   ,  128   ,  128   ,  120   ,  1024  ,  1024  ,  1024  ,  1024  },//u
{  120   ,  128   ,  120   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },//v
{  120   ,  128   ,  112   ,  128   ,  120   ,  1024  ,  1024  ,  1024  },//w
{  136   ,  80    ,  32    ,  80    ,  136   ,  1024  ,  1024  ,  1024  },//x
{  120   ,  640   ,  640   ,  640   ,  504   ,  1024  ,  1024  ,  1024  },//y
{  136   ,  200   ,  168   ,  152   ,  136   ,  1024  ,  1024  ,  1024  },//z
{  16    ,  126   ,  129   ,  129   ,  1024  ,  1024  ,  1024  ,  1024  },
{  255   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  },
{  129   ,  129   ,  126   ,  16    ,  1024  ,  1024  ,  1024  ,  1024  },
{  32    ,  16    ,  8     ,  16    ,  32    ,  64    ,  32    ,  1024  }  };

// general variables
CRGB currBackCol = CRGB(64,64,0); // A global variable to keep a note of the current background GRB value (Some strips may have Red and Green switched
CRGB currTextCol = CRGB(0,0,255); // a global variable to keep a track of the current text colour.
/*
1 = Message
2 = Time
3 = Date
4 = Temp
5 = Ripples
6 = Matrix
7 = Snake
8 = Eyes
9 = Pac
*/
char stateOrder[] = {9,1,2,3,4,5,6,7,8,9};// an array indicating the order that each feature or state should run in.
//
char stateX = 0; // Related to the stateOrder array. Indicates the current state that should be run

// variables for ethernet and getting data from website

boolean connectNow = true; // boolean, set to true after x millisencds have passed since the last website retrieval
boolean readyToRead = false; // if the data has been retrieved from the webpage and if the first character is an * then this will become true and the data will be stored. Used as error checking
char comma = 0; // used to read through how many fields have been read from the web page.
char c,cc; // c is used to store the character being read in the parseData function. cc is used to keep track of which index in the myData array is it inserting.
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // A mac address for the ethernet client.
EthernetClient client; // The ethernet client
long sinceLastConnect = 0; // the number of millisencds since the last update from the website
char postcode[5]; // used for future plans to read postcode from sd card and send to WWO API via website to allow for custom users
char message[60]; // A short message to display. Will store line from txt file on SD card
char myData[15][4]; // stores data retrieved from webpage.
char iSecond,iMinute, iHour, iDay, iMonth, iYear; // data for time once extracted from myData

// variables for  State 1. Scrolling Message

char currLetter =0; //a variable used to track the current letter that the scrolling message should be processing
int cl =0; // cl is the current line (column) that is to be processed by the scrolling text. Each character's column defined by a 10 bit interger. lower case i has one column, m has 5
int h; // something to with counting characters...

int endScroll = 0; // Once the final character from the scrolling message passes, this will increment until 30, when all the characters have left the left of the display

/// variables for states 2,3 &4 Time, Date and Temp

char myTime[5]; // a char array used for time and date. Might not need to be global... "hh:mm" or "dd/mm";
char myTemp; //char used for the current temp "10c" "-5c". Not sure if negative works. Will check next winter.
long sinceLastSecond = 0; // a variable used to check if one second has passed since the last time calcTime was called.
boolean blinkX = true; // should a : be used to blink the seconds?
// variables for state 5, ripples

int dropletA[2] = {-1,-1}; // For the dropletCycle. There can be four droplets at a time and this tracks the centre for each.
int dropletB[2] = {-1,-1};
int dropletC[2] = {-1,-1};
int dropletD[2] = {-1,-1};
char dropCount = 0; //How many droplets are in action
char dropRandom = 0; // a global variable used to countdown to the next droplet
unsigned long sinceStateStarted; // used to count howlong in milliseconds since the sinceStateStarted function started. Stupidly used and not renamed as it's used by other states. 

// variables for state 6, matrix
boolean matStart = false; // A boolean used to see if the Matrix effect has been started
int mats[30]; // an array used to track the individual Matrix Slivers

// variables for state 7, snake

char lastMove = 0; // char used for the snake to retain the last direction the snake moved in.
boolean snakeStart = false; // boolean to check if the snake has or hasn't started
unsigned char snakeHue = 0; // what value is the hue currently for the snake?
int snakePos=0; // pixel that the snake is.

//variables for state 8 & 9 Bitmaps

File bmpFile2; // file for the bitmap for the temp stored in

/////////////////////////////

void setup() {
  LEDS.setBrightness(32); // sets the brightness of the LEDs
  LEDS.addLeds<WS2811, 6>(leds, NUM_LEDS); // Adds the leds colour array to the LEDS array with chip type on data pin 6, with the number of leds.
      digitalWrite(10,HIGH); // Deselect the Wiznet Chip
  digitalWrite(4,HIGH); // Deselect the SD Card
      if (!SD.begin(4)) { // ensure the SD card is working
    return;
  } 
 File myData; //local variable used for reading contents of memory card. As local, doesn't conflict with other myData.
  int lineCount=0; // set the counter for reading in lines to 0
  myData = SD.open("myData.txt"); // open the myData.txt
      int charCount=0; // set the character counter to 0
      while (myData.available()) { // while there is data to be read
        char cx = myData.read(); // store the next character as cx
        if(cx==10){ // if cx == 10 which is line feed
          switch(lineCount){ // which line is this? 
          // Previously when the wifi shield was used, the SD card was used to store SSID and Password. This is a bit overkill just for a string, but can be used for future customisation.
           case 0: // if it's the first line
           h = charCount; // store the number of characters we've readed as h
           message[charCount]='\0'; // append onto the last char in the message array a Nul teminator
          break; 
          case 1: // if it's the second line
           postcode[charCount]='\0'; // append onto the last char in the postcode array a Nul teminator
          break; 
        }
         lineCount++; //increment the line count
        charCount=0; // rest the char count to 0
        }else if(cx==13){ // if a carridge return, ignore it.
         
        }else { // if not a carriage return or a nul terminator
        switch(lineCount){ // check the line
           case 0: // if the first line
           message[charCount]=cx; // add a character onto the message variable
          break; 
           case 1:// if the next line
           postcode[charCount]=cx; // add a character onto the postcode variable
          break; 
        }
        charCount++; // incremement the character count
        }
    }
  myData.close(); // once done close the file
  setupLetters(); // clean up variables, wipe screen 
  delay(100); // pause has been added to give the ethernet a moment to kick in
  if (Ethernet.begin(mac) == 0) { // if the ethernet hasn't begun
    Ethernet.begin(mac); // begin
  }
connectToData(); // call the data from the website
}

void loop(){
  if(millis()>(sinceLastConnect+600000)){ // if it's been 10 minutes since the last time we checked the website for data
    connectNow = true;  // set this to true
  }
  if(connectNow == true){ // if this is true and it's time to check for data
    connectToData(); // call this function to check
    connectNow = false; // turn off the boolean checking for data
    sinceLastConnect = millis(); // store the time in millis since the last check
  }
  while (client.available()) {  // if the ethernet client is available
   parseData(); // parse the data
  }

  if (!client.connected() && comma ==15) { // if the datat has been read in and the 15th item has been identified. Must consider a variable to identify how many items are coming in as this might change.
    readyToRead = false; // We're not ready to read.
    outputResults(); // Doesn't output anything, but does sort the data and save into variable
    sinceStateStarted = millis(); // generic counter to ID the last time a state changed
    client.stop(); // stop the ethernet client
    comma++; // increment comma to 16 to prevent this if statement from running again
  }
  if(sinceLastConnect != 0) // if we've connected at some point
  {
      if(sinceLastSecond == 0){ 
          sinceLastSecond = sinceLastConnect; /// not sure about this, but not going to remove it...
      }
      else
      {
        checkSecond(); // check if a second has passed since the last time
      }
      
  }
  if(stateX>=sizeof(stateOrder)){ // if we've gone through more states than we have listed
    stateX=0; // go back to the first state
  }
  char currState = stateOrder[stateX]; // what state are we in?
  switch(currState){ // lets do something for each
  case 1: // Message
      updateScroll(); // scroll the text 
      if(currLetter>=h){ // if the current letter we are checking is the length of the message we read from the SD card
      endScroll++; //, start counting the number of updates since it passed
      if(endScroll>30){ //Finished Scrolling 
       stateX++; // move to the next state in the list. This will force the next loop to use the next state
       sinceStateStarted = millis(); // record the time of this change
       setupLetters(); //wipe, clean, change...
      }
    }
    break;
    case 2:
     
        showTime(); // errr.... run this function
        if(millis()-sinceStateStarted>10000){ // if 10 seconds has past since we started this.
         stateX++; // move to the next state in the list. This will force the next loop to use the next state
         sinceStateStarted = millis();// record the time of this change
         setupLetters(); //wipe, clean, change...
    }
    break;
    case 3:
     
        showDate();
        if(millis()-sinceStateStarted>5000){
         stateX++; // move to the next state in the list. This will force the next loop to use the next state
         sinceStateStarted = millis();// record the time of this change
     setupLetters(); 
    }
    break;
        case 4:
         
        showTemp();
        if(millis()-sinceStateStarted>5000){
         stateX++; // move to the next state in the list. This will force the next loop to use the next state
         sinceStateStarted = millis();// record the time of this change
     setupLetters(); //wipe, clean, change...
    }
    break;
  case 5:
   
    setRandomDrop();
    dropletCycle();
    if(millis()-sinceStateStarted>10000){
     stateX++; // move to the next state in the list. This will force the next loop to use the next state
     sinceStateStarted = millis();// record the time of this change
    setupLetters(); //wipe, clean, change...
    }
    break;

  case 6:
  
    matrixStyle();
    if(millis()-sinceStateStarted>10000){
     // Serial.println("End");
       stateX++;// move to the next state in the list. This will force the next loop to use the next state
       //writeLog("Finished Matrix");
       sinceStateStarted = millis();// record the time of this change
        matStart = false; // set the boolean that is check when this first starts to false
       setupLetters(); //wipe, clean, change...
    }
  break;
  
    case 7:
     
    snake();
    
    if(millis()-sinceStateStarted>30000){
       stateX++;// move to the next state in the list. This will force the next loop to use the next state
       sinceStateStarted = millis();// record the time of this change
       snakeStart = false; // set Snake so that it hasn't started
       setupLetters(); //wipe, clean, change...
    }
  break;
  
    case 8:
     
    bmpDraw("VID002/", 0, 0); 
    
    if(millis()-sinceStateStarted>15000){
       stateX++; // move to the next state in the list. This will force the next loop to use the next state
       sinceStateStarted = millis(); // record the time of this change
       setupLetters(); //wipe, clean, change...
    }
  break;
      case 9: 
    bmpDraw("VID001/", 0, 0); 
    if(millis()-sinceStateStarted>8000){
       stateX++; // move to the next state in the list. This will force the next loop to use the next state
       sinceStateStarted = millis();// record the time of this change
       setupLetters(); //wipe, clean, change...
    }
  break;
  
      case 10:    
    //memCheck();
    if(millis()-sinceStateStarted>3000){
       stateX++; // move to the next state in the list. This will force the next loop to use the next state
       sinceStateStarted = millis();// record the time of this change
       setupLetters(); //wipe, clean, change...
    }
  break;
  }
  delay(1);
}

void checkSecond(){ // has a second passed?
           if(millis()>(sinceLastSecond+1000)) // if a second has passed since the last time we checked
          {
            calcTime(); // calculate the new time
            sinceLastSecond+=1000; // add 1000 millis to the last sencond checkpoint
          } 
}

void snake(){ // The snake generator
  CHSV temphsv;// This is a varaible that stored Hue Saturation and brightness instead of RGB
  if(!snakeStart){ // if we haven't already started
    snakeHue=random(256); // create a random hue 0-255
    snakePos=random(300); // create a random start point
    for(int v=0;v<300;v++){ // for each pixel, 
      temphsv.h = snakeHue; // the temp hue to the snake hue
      temphsv.s = 187; //medium saturation
      temphsv.v = 255; // full brightness
      hsv2rgb_rainbow( temphsv, leds[v]); // Converts HSV to RGB annd sets the LED
      snakeStart = true; // set this to true so this first bit doesn't run again
    }
    LEDS.show(); // send all of the LED data through to the strip
  }
  snakeHue++; // adjust the snake hue by 1. As it's an unsigned character it will automatically become 0 when it goes over 255
  temphsv.h = snakeHue; // do the set up of the HSV
      temphsv.s = 187;
      temphsv.v = 255;
      hsv2rgb_rainbow( temphsv, leds[snakePos]); // This time only make the snake pos the new colour
  boolean goodMove = false;  // set this to false so that it sets this to think no good move has been made
  char diceRoll = random(4); //  a random number 0 -3 sets the char also
   while(!goodMove){ // whislt no good move has been made 
     diceRoll = random(4); // make a new random number...
     /// 0 moves left, 1 moves right, 2 moves up, 3 moves down
     if(diceRoll == 0 && lastMove!=1){goodMove = true;} // if this move is left and the last move was not right (back on itself) then this is a good move
     if(diceRoll == 1 && lastMove!=0){goodMove = true;} // if this move is right and the last move was not left (back on itself) then this is a good move
     if(diceRoll == 2 && lastMove!=3){goodMove = true;} // if this move is up and the last move was not moving down (back on itself) then this is a good move
     if(diceRoll == 3 && lastMove!=2){goodMove = true;} // if this move is down and the last move was not up (back on itself) then this is a good move
   }
   lastMove = diceRoll; // set the last move to the current move
  switch(diceRoll){ // act on the random number
   case 0: // left
     if(snakePos%30==0){ //if the snake position is right on the left so that the LED number is whole divisable by 0
       snakePos+=29; // move the pixel to the far right of the display (so it wraps around)
     }else{
       snakePos--; // otherwise move it to the LED to the left
     } 
   break;
   case 1: // right
     if(snakePos%30==29){ // if the remainder from this is 29, it means the snake is at the far right of the display
       snakePos-=29; // substract 29 moves it to the other side of the screen
     }else{
       snakePos++; // otherwise just move it to the pixel on the right
     } 
   break;
   case 2: // up
     if(snakePos<29){ //if the current  LED is less than 29, it is already at the top        snakePos+=270; // so adding 270 moves it to the bottom        }else{        snakePos-=30; // otherwise substract 30 will move it directly to the line above      }     break;    case 3: // down      if(snakePos>269){ // if it's greater than 269 then the snake is already on the bottom line
       snakePos-=270; // subtracting 270 will move it to the top
     }else{
       snakePos+=30; // otherwise, adding 30 will move it to the next line down
     } 
   break;
  } 
  LEDS.show(); // Once everything is done, send it through to the strip
}

void setupLetters(){ // cleans up the variables for messages and text, calls the random colour chooser, wipes background, 
    endScroll = 0; // reset the endscroll to 0, otherwise the next scrolling message will think it's already completed
    currLetter = 0; // set the current letter back to 0 so that it starts with the first character next time
    cl=0; // set the current line to 0 so that the first column of the letter is rendered first
randomColourPicker(); // chooses a new colour from a selection
    wipeBG(currBackCol); // wipes everything with the new background colour
}

 void randomColourPicker(){ // for picking a random colour scheme from a selection
  char randChar = random(7); // pick a random number
  // thinking about this, an array in progmem might be better for this?
  switch(randChar){ // what's our number?
    case 0:
     currBackCol = CRGB(0,0,0); // set backgroun to black
     currTextCol = CRGB(255,0,255); // set the text to whatever
    break; 
        case 1:
     currBackCol = CRGB(0,0,0);// rinse and repeat
     currTextCol = CRGB(255,255,255); 
    break;
        case 2:
     currBackCol = CRGB(0,0,0);
     currTextCol = CRGB(255,255,0); 
    break;
        case 3:
     currBackCol = CRGB(0,0,0);
     currTextCol = CRGB(0,255,255);
     break;
             case 4:
     currBackCol = CRGB(0,0,0);
     currTextCol = CRGB(0,0,255);
     break;
                 case 5:
     currBackCol = CRGB(0,0,0);
     currTextCol = CRGB(0,255,0);
     break;
                  case 6:
     currBackCol = CRGB(0,0,0);
     currTextCol = CRGB(255,0,0);
     break;
//        case 7: // not using these are the moment as they didn't work that well
//     currBackCol = CRGB(255,0,0);
//     currTextCol = CRGB(0,0,0); 
//    break;
//        case 8:
//     currBackCol = CRGB(0,255,0);
//     currTextCol = CRGB(0,0,0); 
//    break;
//        case 9:
//     currBackCol = CRGB(0,0,255);
//     currTextCol = CRGB(0,0,0); 
//    break;
//        case 10:
//     currBackCol = CRGB(0,255,255);
//     currTextCol = CRGB(0,0,0); 
//    break;
//        case 11:
//     currBackCol = CRGB(255,255,0);
//     currTextCol = CRGB(0,0,0); 
//    break;
//        case 12:
//     currBackCol = CRGB(255,0,255);
//     currTextCol = CRGB(0,0,0); 
//    break;
//            case 13:
//     currBackCol = CRGB(255,255,255);
//     currTextCol = CRGB(0,0,0); 
//    break;
  }

 }
 

void wipeBG(CRGB thisCol){ // with this colour
  for(int v=0; v<300; v++){ // every LED in the house
    leds[v] = thisCol;// change to this colour;
  }
  LEDS.show(); //update the strip
}

void showTime(){ //showTime, really, not the TV channel
 char buffer[2]; // creates a buffer so that we can get around a single digit for numbers less than 10
 itoa(iHour,buffer,10); // converts iHour, an integer to a string in the char buffer, to base 10
 if(iHour<10){ //if less than ten
   myTime[0] = '0'; //set the first char in myTime to '0'
   myTime[1] = buffer[0]; // set the send char to the number
 }else{
   myTime[0] = buffer[0]; //if greater than 9 then just take the hour to the respective index.
   myTime[1] = buffer[1];
 }
 if(blinkX){ // the third char is a blinking divider
  myTime[2] = ':'; // which is either on...
 }else{
   myTime[2] = ' ';// or not on
 }
 itoa(iMinute,buffer,10); // do the same conversion and check for minutes
 if(iMinute<10){
   myTime[3] = '0'; // enter a zero for 'tens' of minutes into the 4th char
   myTime[4] = buffer[0]; //the then the rest
 }else{
  myTime[3] = buffer[0];
  myTime[4] = buffer[1];
 }
 blinkX^=true; //reverse blink.
 delay(500); // pause for half a second
 showStillText(myTime); // call the function to display the time
 
}

void showDate(){ // pretty much as the showTime function. Read that for the same
 char buffer[2];
 itoa(iDay,buffer,10);
 if(iDay<10){
   myTime[0] = '0';
   myTime[1] = buffer[0];
 }else{
   myTime[0] = buffer[0];
   myTime[1] = buffer[1];
 }
  myTime[2] = '/'; 
 itoa(iMonth,buffer,10);
 if(iMonth<10){
   myTime[3] = '0';
   myTime[4] = buffer[0];
 }else{
  myTime[3] = buffer[0];
  myTime[4] = buffer[1];
 }
 delay(500);
 showStillText(myTime);
}

void showTemp(){//Simliar to the previous two functions.
  char myTemp[5];
  for(int l=0;l<5;l++){
   if(myData[0][l]!='\0'){ // if this isn't the end of the data
    myTemp[l] = myData[0][l]; // add it to the char array
   }else{
    myTemp[l++] = 'c'; // if it is, then add celius on the end
    while(l<5){
    myTemp[l++] = ' '; // if still less than 5 chars, add a space
    }
   } 
  }
 showStillText(myTemp); // show the text
 delay(500); // pause
}

void showStillText(char newText[5]){ // only take five characters
    int updateLine,vv,i, k, y;
    for(i=0;i<29;i++){ // for each column         if(i==0){              vv=0; // which character are we dealing with?             cl=0; // which column in the character are we dealing with             updateLine = 0;         }else{             y =newText[vv]; // y is the current chracter we need to process             updateLine = findLine(y); // checks for the binary 10 bit column data.              if(vv>4){ // if we've checked all the chars in the newText
                updateLine = 0; 
            }
            if(updateLine==1024){ //if 1024 indicates that all columns in the character have been rendered
                for(char pp=0;pp<10;pp++){ // for each pixel in this column
                    leds[((270+i)-(pp*30))]  = currBackCol; // wipe all to the background colour before we begin
                }
                cl=0; // set the current column to process to 0
                vv++; //increment the variable counting which character we are processing
            }else{ /// see the bottom of the script for an ASCII explaination
                if(updateLine/512==1){ // If the data can be divided by 512 and is 1 (integers are whole), then the the bottom LED is lit 
                    leds[270+i] = currTextCol; // The bottom LED is set to the the text colour. 
                    updateLine-=512; // Remove 512, otherwise everything else can divide into it
                }else{
                    leds[270+i] = currBackCol; // otherwise set this LED to off
                }
                if(updateLine/256==1){ // If divisable by 256
                    leds[240+i] = currTextCol; // then go to the second to bottom LED and set
                    updateLine-=256; // remove
                }else{
                    leds[240+i] = currBackCol; 
                }
                if(updateLine/128==1) //rinse and repeat
                {
                    leds[210+i] = currTextCol;
                    updateLine-=128;
                }else{
                    leds[210+i] = currBackCol; 
                }
                if(updateLine/64==1)
                {
                    leds[180+i] = currTextCol; 
                    updateLine-=64;
                }else{
                    leds[180+i] = currBackCol;
                }
                if(updateLine/32==1){
                    leds[150+i] = currTextCol;
                    updateLine-=32;
                }else{
                    leds[150+i] = currBackCol; 
                }
                if(updateLine/16==1)
                {
                    leds[120+i] = currTextCol;
                    updateLine-=16;
                }else{
                    leds[120+i] = currBackCol;
                }
                if(updateLine/8==1)
                {
                    leds[90+i] = currTextCol; 
                    updateLine-=8;
                }else{
                    leds[90+i] = currBackCol;
                }
                if(updateLine/4==1)
                {
                    leds[60+i] = currTextCol; 
                    updateLine-=4;
                }else{
                    leds[60+i] = currBackCol;
                }
                if(updateLine/2==1)
                {
                    leds[30+i] = currTextCol;
                    updateLine-=2;
                }else{
                    leds[30+i] = currBackCol; 
                }
                if(updateLine==1)
                {
                    leds[i] = currTextCol;
                }else{
                    leds[i] = currBackCol; 
                }
            }
       }
    } 
    LEDS.show();
}

void matrixStyle(){
  int v; // holding integer for the current "sliver" running down the screen
  if(matStart == false){ //If this is the start of the cycle...
    for(v=0;v<30;v++){ // Set up 30 "slivers"
      mats[v] = random(420)-120; // between -120 and 300
    }
    //writeLog("Start Martix");
    matStart = true; // switch the boolean so the above won't run each time.
  }
    for(int v=0; v<300; v++){ // for every pixel on the display, wipe all and
  leds[v] = CRGB(16,0,0); // set to 16 green.
   }
   
   // each silver is 5 pixels long, and the start postion is from the top (screen starts top left)
   //so the following if statements checks 
   for(v=0;v<30;v++){  // each silver in the mats array   int pv = mats[v]; // temp store the top position in pv     if(pv>-120 && pv < 179){ // if the start position is at least more than four lines from the bottom of the display     leds[pv+120] = CRGB(255,0,0); // make the pixel four lines down the brightest green it can.     }     if(pv>-90 && pv < 209){ // if the start position is at least more than three lines from the bottom of the display     leds[pv+90] = CRGB(200,0,0); // make the pixel three lines down the 200 green.     }     if(pv>-60 && pv < 239){ // if the start position is at least more than two lines from the bottom of the display     leds[pv+60] = CRGB(150,0,0); // make the pixel two lines down the 150 green.     }        if(pv>-30 && pv < 269){// if the start position is at least more than one line from the bottom of the display     leds[pv+30] = CRGB(100,0,0); // make the pixel one line down the 100 green.     }         pv+=30; //add 30 to the sliver position so the next cycle it is one line down     if(pv>300){ // if the srat position is past the bottom of the screen
    mats[v] = random(30)-120; // set the new position between -120 and -91
    }else{
     mats[v]=pv; // otherwise set it to one line down
    } 
   }
   LEDS.show(); //push this data to the strip
   delay(50); //pause for 1/20th second
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Start of the code dealing with the droplets.
// Made up of four functions.
// setRandomDrop
// dropletCycle
// droplets
// ripple1, ripple2, ripple3. ripple4, ripple5, ripple6, ripple7, ripple8, ripple9, ripple 10
// setRipPix
//
//     setRandomDrop(); and dropletCycle(); are called from the update loop every loop that char currState = stateOrder[stateX]; = 5
// setRandomDrop times the creation of new drops and gives them a random position
// dropletCycle makes the ripple effect and for each active drop, calls droplets
// droplets works out the age of the droplet and calls one to three of the ripple(1-10) functions that make up the size of the ripple
// ripple(1-10) accesses the splash(1-10) arrays that holds the offsets for every pixel that makes up the ripple. Calls setRipPix
// setRipPix  uses the offsets for each pixel in each ripple to set the colour of each.

void setRandomDrop(){
  if(dropRandom<0){ // dropRandom allows a random number of frame updates before it creates another droplet.     int r = random(300); // create a random pixel to start the droplet at.     switch(dropCount){ // depending on which droplet should be created....       case 0:         dropletA[0] = r; // set the position of the new droplet         dropletA[1] = 0; // set the age of the new droplet to 0       break;       case 1:         dropletB[0] = r;         dropletB[1] = 0;       break;       case 2:         dropletC[0] = r;         dropletC[1] = 0;       break;       case 3:         dropletD[0] = r;         dropletD[1] = 0;       break;       }     dropRandom = random(20); // Create a new random amount of cycles until the next droplet is generated     dropCount++; // Increment the order of droplets     }   if(dropCount>3){ //if the order of droplets has gone beyond 3, switch to 0 so that it can't have mroe than 4.
   dropCount = 0; 
  }
  dropRandom--; //decrement the counter countingdown to the next droplet
}

void dropletCycle(){
      for(int v=0; v<300; v++){ // for all pixels         int blueRand = random(25); // a random number        leds[v] = CRGB(0,0,25+blueRand); // this will create a rippling effect as each pixel will be set to a blue between 25 and 50     }   if(dropletA[1] != -1){ // if dropletA's age is -1, it hasn't been created, so only run this if it exists.     dropletA[1] = droplets(dropletA); // call the droplets function for dropletA. The returned value will increment the age of the ripple.   }     if(dropletB[1] != -1){     dropletB[1] = droplets(dropletB);   }     if(dropletC[1] != -1){     dropletC[1] = droplets(dropletC);   }     if(dropletD[1] != -1){     dropletD[1] = droplets(dropletD);   }    LEDS.show(); } int droplets(int x[2]){ // from above. This will expect an integer array with two values    // int c = 85;     int stage = x[1]; // stage =  age of droplet     int q = x[0]; // centre of the droplet. Pixel          switch(stage){ // depending on the age of the droplet      case 1:       ripple1(q, 1); // only run the smallest ripple, first variable sent through is position, second is the brightness of the part of the ripple 1*85 = 85, 3*85 is 255     break;       case 2:     ripple1(q,3); // Ripple 1 (smallest) will be 3 times brighter than the larger ripple.     ripple2(q,1); // Ripple 2 will be one third of the brightness of ripple 1.     break;    case 3:     ripple1(q,1);      ripple2(q,3);     ripple3(q,1);     break;    case 4:     ripple2(q,1);     ripple3(q,3);     ripple4(q,1);     break;    case 5:     ripple3(q,1);     ripple4(q,3);     ripple5(q,1);         break;    case 6:     ripple4(q,1);     ripple5(q,3);     ripple6(q,1);        break;    case 7:     ripple5(q,1);     ripple6(q,3);     ripple7(q,1);         break;    case 8:     ripple6(q,1);     ripple7(q,3);     ripple8(q,1);      break;    case 9:     ripple7(q,1);     ripple8(q,3);     ripple9(q,1);      break;         case 10:     ripple8(q,1);     ripple9(q,2);     ripple10(q,1);      break;         case 11:     ripple9(q,1);     ripple10(q,2);     break;         case 12:       ripple10(q,1);     break;     }  stage++;  if(stage>14){
  stage = -1; // if the ripple is past the age of 14, then set it to -1, which indcates non-existance.
 }
 return stage; // return the age
}

void ripple1(int w, int f){
 
  for(int p=0;p<4;p++){ //splash1 which is in progMem has 4 pixels, hence looping 4 times
        char ax = pgm_read_byte_near(&(splash1[p][0])); // retrive the x offset for this pixel of the ripple
        char ay = pgm_read_byte_near(&(splash1[p][1])); // retrive the y offset for this pixel of the ripple
          setRipPix(w,f,ax,ay); // send the centre position of the ripple, the intensity of the brightness, x offset and y offet to setRipPix....
        }
}

void ripple2(int w, int f){
  for(int p=0;p<8;p++){ //splash2 which is in progMem has 8 pixels, hence looping 8 times
        char ax = pgm_read_byte_near(&(splash2[p][0]));
        char ay = pgm_read_byte_near(&(splash2[p][1]));
        setRipPix(w,f,ax,ay);
        }
}

void ripple3(int w, int f){
          for(int p=0;p<12;p++){
        char ax = pgm_read_byte_near(&(splash3[p][0]));
        char ay = pgm_read_byte_near(&(splash3[p][1]));
        setRipPix(w,f,ax,ay);
        }
}

void ripple4(int w, int f){
  for(int p=0;p<16;p++){
        char ax = pgm_read_byte_near(&(splash4[p][0]));
        char ay = pgm_read_byte_near(&(splash4[p][1]));
        setRipPix(w,f,ax,ay);
        }
}

void ripple5(int w, int f){
  for(int p=0;p<36;p++){
        char ax = pgm_read_byte_near(&(splash5[p][0]));
        char ay = pgm_read_byte_near(&(splash5[p][1]));
       setRipPix(w,f,ax,ay);
        }
}

void ripple6(int w, int f){
  for(int p=0;p<28;p++){
        char ax = pgm_read_byte_near(&(splash6[p][0]));
        char ay = pgm_read_byte_near(&(splash6[p][1]));
        setRipPix(w,f,ax,ay);
        }
}

void ripple7(int w, int f){
  for(int p=0;p<32;p++){
        char ax = pgm_read_byte_near(&(splash7[p][0]));
        char ay = pgm_read_byte_near(&(splash7[p][1]));
        setRipPix(w,f,ax,ay);
        }
}

void ripple8(int w, int f){
  for(int p=0;p<36;p++){
        char ax = pgm_read_byte_near(&(splash8[p][0]));
        char ay = pgm_read_byte_near(&(splash8[p][1]));
        setRipPix(w,f,ax,ay);
        }
}

void ripple9(int w, int f){
  for(int p=0;p<68;p++){
        char ax = pgm_read_byte_near(&(splash9[p][0]));
        char ay = pgm_read_byte_near(&(splash9[p][1]));
        setRipPix(w,f,ax,ay);
        }
}

void ripple10(int w, int f){
  for(int p=0;p<48;p++){         char ax = pgm_read_byte_near(&(splash10[p][0]));         char ay = pgm_read_byte_near(&(splash10[p][1]));         setRipPix(w,f,ax,ay);         } } void setRipPix(int w, int f, char ax, char ay){   int pix = w + ax + (ay*30); //calculate the individual pixel sent that makes up  part of the current ripple by taking the centre (w), adding the x offset, and adding the yOffset by multiplying it by 30 to get the Y position   if(pix > -1 && pix < 300){ // ensure that  this pixel is in the display range
    leds[pix] = CRGB(0,0,85*f); // set that pixel to the intestity.
  }
}

void updateScroll(){
  int i, k, l;
  
  ////////////////////////////////////////////
    for(i=0;i<29;i++)          //
    {                          // This nested loop will just budge each pixel/LED value 
      for(k=0;k<10;k++)        // back one The value of LED[10] will become LED[9],       {                        // LED[234] becomes LED[233]. This means the only LEDs that         l = i+(k*30);          // have to be recalculated are 29,59,89,119,149,179,209,239,269         leds[l] = leds[l+1];   //       }                        //     }                          //     //////////////////////////////////////////     int updateLine; // See the function showStillText for the rest of this function explaination as they are very much the same                     // This one just budges the LEDs left once and calculates the far right LEDs.     char y =message[currLetter];     if(currLetter>h-1){
     updateLine=0; 
     currLetter++;
    }else{
    updateLine = findLine(y);
    }
    if(updateLine==1024)
    { 
      for(char pp=0;pp<10;pp++){        leds[(299-(pp*30))]  = currBackCol;       }       cl=0;       currLetter++;     }     else     {         if(updateLine/512==1)       {         leds[299] = currTextCol;         updateLine-=512;       }       else       {         leds[299] = currBackCol;       }         if(updateLine/256==1)       {         leds[269] = currTextCol;         updateLine-=256;       }       else       {         leds[269] = currBackCol;;        }              if(updateLine/128==1)       {         leds[239] = currTextCol;         updateLine-=128;       }       else       {         leds[239] = currBackCol;        }              if(updateLine/64==1)       {         leds[209] = currTextCol;          updateLine-=64;       }       else       {         leds[209] = currBackCol;       }              if(updateLine/32==1)       {         leds[179] = currTextCol;         updateLine-=32;       }       else       {         leds[179] = currBackCol;        }               if(updateLine/16==1)       {         leds[149] = currTextCol;         updateLine-=16;       }       else       {         leds[149] = currBackCol;       }              if(updateLine/8==1)       {         leds[119] = currTextCol;          updateLine-=8;       }       else       {         leds[119] = currBackCol;       }              if(updateLine/4==1)       {         leds[89] = currTextCol;          updateLine-=4;       }       else       {         leds[89] = currBackCol;       }              if(updateLine/2==1)       {         leds[59] = currTextCol;         updateLine-=2;       }       else       {         leds[59] = currBackCol;        }               if(updateLine==1)       {        leds[29] = currTextCol;       }       else       {         leds[29] = currBackCol;        }     }    LEDS.show(); delay(50); } int findLine(char x){ // identifies the 10bit integer that indicates the LEDs in the column of the character. X is the character/ASCII code  int p; //     p = pgm_read_word(&(myChars[x-1][cl])); //Accesss the ProgMem array myChars. Minus 1 for zero index. CL is the current line(column) of the character to be processed     cl++; //increments the current line (column) for the next time   return p; // returns the 10 bit column information. } void calcTime(){ //Some of this is overkill due to the 10 minute check, but it might look stupid if you have a March 32 at 00.01     iSecond++; //Add one to iSecond     if(iSecond>59) // if we have 60 or more seconds
    {
        iSecond=0; // set seconds to 0
        iMinute++; // add one to minutes
        if(iMinute>59) // if we now have more than 60 minutes
        {
            iMinute=0; // set minutes to 0
            iHour++;// and add one to the hour
            if(iHour>23)// if we have more than 23 hours
            {
                iHour=0; // set hours to 0
                iDay++; // add a day
                if(iMonth==2) // if feb
                {
                    if(iDay>=28)// if days over 28
                    {
                        if(iYear%4==0)//if a leap year
                        {
                            if(iDay==29) // if over 29
                            {
                                iMonth++; // add a month
                                iDay=1; // set day to 1
                            }
                        } 
                        else // else must be 28 days
                        {
                            iMonth++; // add a month
                            iDay=1; // set day to 1
                        }
                   }
              }
              if(iMonth==1,3,5,7,8,10,12) // if jan, march, July, August, October or December
              {
                  if(iDay>31) // if the day is over 31
                  {
                      iMonth++; // add a month
                      iDay=1;  // set day to 1
                  }
              }
              if(iMonth==4,6,9,11) // if April, June, Septmeber or November
              {
                  if(iDay>30)// if day is greater than 30
                  {
                       iMonth++; // add a month
                       iDay=1; // set day to 1
                  } 
              }
              if(iMonth==13)// if month is 13
              {
                  iMonth==1; /// set to 1 (Jan)
                  iYear++; // add a year
              }
         }
    }
    }
}

void connectToData(){ // This is the function that checks for data from a website. See the php files for how this works
  for(int p=0;p<15;p++){ // for 15 bits of data, this should be in a variable rather than hardcoded....
  for(int o=0;o<4;o++){ // for five characters
    myData[p][o] = '\0'; // blitz the lot with Nul terminators.
  }
  }
 comma = 0; //set the comma count to 0
delay(2000);  // The delay allows the Enternet client time to settle, otherwise data might not be reached....
  if (client.connect("your.wesbite.com", 80)) { // The website
    client.println("GET /ardData.php HTTP/1.1"); // The URL
    client.println("Host: your.wesbite.com"); // The host
    client.println("Connection: close"); // Not sure, but needed....
    client.println(); // No idea, but not going to mess with it.
    delay(2000);  // Another delay to also the client time....
  }
}

void parseData(){ // Reading in the data from the website

// Example

// *19,116,8,ESE,21,10,8,E,116,06,26,14,13,28,43

// * indidcates the start and is used to check the data is mine.
// Each bit of data is delimited with a comma
// Check youtube video for more info.

c = client.read(); // read in the next (or first if we've just started)
  if(c=='*'){ // if the first character is an * then it's my data. An error checker of sorts
     readyToRead = true; // Ready to read the data in
  }
  if(c!=',' && readyToRead == true && c!='*' && c!=-1){ // If the character is not a comma, and not an * or dead, and we're ready to read
myData[comma-1][cc]+=c; // add the character read into c appropate field
      cc++; //increment the character counter
     }
    if(c==','){ // if we've read in a comma, then it's the next bit of data
      myData[comma-1][cc]= '\0';; // end the current bit of data we're reading with a Nul Terminator
      cc=0; // Reset the character
       comma++; //increment the counter counting the different bits of data.
      }
}

void outputResults(){ // this will save the data from the website into various variables
  iSecond = atoi(myData[14]); //Convert chars to integers and save
  iMinute = atoi(myData[13]);
  iHour = atoi(myData[12]);
  iDay = atoi(myData[10]);
  if(iDay==0){
   connectNow=true; // if iDay = 0 then something is wrong with my website so try again.
  }
  iMonth = atoi(myData[9]);
  if(iMonth==0){
   connectNow=true; 
  }
  iYear = atoi(myData[11]);
  if(iYear==0){
   connectNow=true; 
  }
/*  Serial.print("currTemp "); //1,1
  Serial.println(myData[0]); //1,1
Serial.print("currWeathercode "); //1,2
Serial.println(myData[1]); //1,2
Serial.print("currWindSpeed "); //1,5
Serial.println(myData[2]); //1,5
Serial.print("currWindDirection "); //1,8
Serial.println(myData[3]); //1,8
Serial.print("futTempHigh "); //2,1
Serial.println(myData[4]); //2,1
Serial.print("futTempLow "); //2,3
Serial.println(myData[5]); //2,3
Serial.print("futWindSpeed "); //2,5
Serial.println(myData[6]); //2,5
Serial.print("futWeatherCode "); //2,9
Serial.println(myData[7]); //2,9
Serial.print("futWindDirection "); //2,8
Serial.println(myData[8]); //2,8
printTime();*/
}

void bmpDraw(char *filename, uint8_t x, uint8_t y) { // This is slightly complex
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*20]; // pixel buffer (R+G+B per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col; // setting up integers for width, height, row and column.
  uint8_t  r, g, b,lc; // unsigned 8 bit integers....
  bmpFile2 = SD.open(filename); // open the folder
  bmpFile2.rewindDirectory(); // go to the start of the directory
  while(true) { 
    delay(1);
    lc++; //?
     File bmpFile =  bmpFile2.openNextFile(); // read in the next file
     if (! bmpFile) { //If no file
      break; // stop
    }
  
  uint32_t pos = 0;
  if(read16(bmpFile) == 0x4D42) { // If this has a BMP signature
    //Going through the header data, there are things we can read and ignore....
    (void)read32(bmpFile); //ignore this 
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    (void)read32(bmpFile); // Ignore
    bmpWidth  = read32(bmpFile); //This retrieves the width of the bitmap in pixels
    bmpHeight = read32(bmpFile); //This retrieves the height of the bitmap in pixels
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed
        goodBmp = true; // Supported BMP format -- proceed!
        rowSize = (bmpWidth * 3 + 3) & ~3;
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        w = bmpWidth;
        h = bmpHeight;
        for (row=0; row<h; row++) { // For each scanline...
          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          } // not sure about these bits....

          for (col=0; col<w; col++) { // For each pixel...             // Time to read more pixel data?             if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
            }
            b = sdbuffer[buffidx++]; //blue first
            g = sdbuffer[buffidx++]; // then green
            r = sdbuffer[buffidx++]; // then red
          leds[(row*30)+col] = CRGB(g,r,b); // set led
          } // end pixel
        } // end scanline
      } // end goodBmp
    }
  }
  bmpFile.close(); // close file
  if(!goodBmp) {
  } else {
    LEDS.show();
    if(lc==10){
     lc = 0;
     checkSecond();
    }
  }
  }
  bmpFile2.close();
 
}

/// These bits are not mine, from somewhere....

uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}

/// I've left the next in just incase you need to debug in some way
// The serial monitor seems to be broken when running this script

//void //writeLog(String debug){
// File myLog = SD.open("MyLog.txt", FILE_WRITE);
//if(myLog){
//   char buffer[2];
// itoa(iHour,buffer,10);
//  myLog.print(itoa(iHour,buffer,10));
//  myLog.print(":");
//  myLog.print(itoa(iMinute,buffer,10));
//  myLog.print(":");
//  myLog.print(itoa(iSecond,buffer,10));
//  myLog.print(" --- ");
// myLog.println(debug);
// myLog.print("freeMemory()=");
//  myLog.println(freeMemory());
//}
//myLog.close();
//
//delay(500);
//}

//void memCheck(){
//  char buff[5] ="    ";
// 
// itoa(freeMemory(),buff,10);
// showStillText(buff); 
//}

//void showMem(){
//          Serial.print("freeMemory()=");
//    Serial.println(freeMemory());
//}

//////// Text rendering explaination

/* The display is ten pixels high, so for a character there is a width that is dependent on the character width, these are refered 
to in this script as columns. Each column has ten pixels. This script uses values between 0 and 1024 to indicate which pixels should
be lit. 1024 is a sort of Nul terminator, so when this is read in, the functions leave a gap and move to the next character

A lower case 'i' has {  250   ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  ,  1024  } 

2+8+16+32+64+128 = 250

An uppercase 'N' is {  508   ,  2     ,  2     ,  2     ,  508   ,  1024  ,  1024  ,  1024  }

4+8+16+32+64+128+256 = 508

0001
0002  X     XXX
0004       X   X
0008  X    X   X
0016  X    X   X
0032  X    X   X
0064  X    X   X
0128  X    X   X
0256       X   X
0512

*/

Posted in Arduino | Tagged , , , , , , , , | 2 Comments

SensoDuino with a Parallax 30086 RN-42 Bluetooth Module and Arduino Uno

2013-11-23 18.33.48My colleague Ryan send me on a Tweet about SensoDuino, an app for Android Phones that allows you through the phone’s bluetooth to send sensor data from your phone; from gyroscope and accelerometer data to light, heat and humidity!; to your Arduino. As luck would have it I had borrowed a few items from work and had a Bluetooth module sitting right next to me on my coffee table. The problem being that the author recommends the cheaper RC05 bluetooth module and I had the rather expensive RN42 bluetooth module, adapted by Parallax into their 30086 module, which rather handily narrows down all of the various pins of the RN42 into seven outputs. There is very little information out there on connecting this module to the Arduino. So this post is to document process I went through to get the RN42 module to work with SensoDuino.

2013-11-24 14.19.01The wiring of the module is extremely easy to connect to the Arduino Pro as it has a jumper for 3.3v and 5v. The wiring is as so…

Arduino —> 30086 RN42

5v — > VIN
GND —> GND
0(RX) –> TX
1(TX) —> RX

That’s it, no need to knock the voltage down with combinations of resistors.

Uploading the sketch to the Arduino is standard, although as Hazam Bitar points out, you may need to disconnect the power from the bluetooth module as it will interfere with uploading a new sketch.

By default, the jumper settings on the Bluetooth module sets the baud rate to 9600, so you will need to change this in the sketch

Serial.begin(9600);

Or change the first jumper to off and it will run at 115200.

2013-11-24 14.24.38

So that’s it. It will be interesting to see if I will be able to make my own app that can use the sensors in my phone to control whatever I wish without going through the rather excellent SensoDuino, but for those learning and wanting to prototype devices without working with complex wiring and badly documented components, then this is a brilliant app.

Posted in Arduino, UDK | Tagged , , , , , , , , , | Leave a comment

Heightmap data. Web based and quick.

Yesterday I posted my Photoshop Script for creating heightmaps from OS data, but I wasn’t happy with the 30 minutes it took Photoshop to render the map.

Many moons ago I used to be a web developer and I loved messing around with the Image creation in PHP. I thought that generating images was a lot quicker, so this evening, I’ve ported my script to PHP and I can confirm it is quicker. About 29 minutes and 50 seconds quicker. In fact it takes longer to upload the OS data than it does render the image.

biddynew

So the new steps are this:

1. Get the OS data as before.
2. Go to My Heightmap generator
3. Choose between Relative (best for single heightmaps) or Absolute (Best for multiple if used with multiple terrain)
4. Right click and copy image.
5. Open a package of your choice and create a new file. Photoshop will take the settings of whatever is in the clipboard. Ensure it is greyscale.
6. Save as a RAW file.

That’s it.

Posted in Uncategorized | Leave a comment

Heightmap creation from OS data. (UK Only)

This tutorial will allow you to create heightmap data from Ordnance Survey’s OS Terrain 50 grid data. The information is free to download from https://www.ordnancesurvey.co.uk/opendatadownload/products.html

BenNevis

This process may well work for data from other sources, but as far as I know, this works for 10x10km areas of the UK only and produces a resolution of 50 metres.

Continue reading

Posted in Photoshop, UDK, Unity | Leave a comment

A preview of my Arduino/Unity experiment that I will talk about at LUUG 21.

Posted in Uncategorized | Leave a comment

Arduino to Unity. Reading in a photoresistor.

This is much the same as the Ultrasonic experiment from a while back. I’m not going to write this up, but I have made another video which explains everything.

Posted in Arduino, Unity | Leave a comment

Controlling an Arduino from Unity

Later this month I’ll be doing a talk for LUUG (London Unity Usergroup) on using Arduinos with Unity, so to prepare for that I’ve gone through the basic processes of talking to the Arduino from Unity.

For this tutorial, you’ll need…

Continue reading

Posted in Arduino, Unity | 1 Comment

Retroshooter. Source materials.

Howdy all,

A few weeks ago I revisited Retroshooter for my class as a part of a transfer from Unity to UDK. I don’t think that I sold UDK very well due to my ranting about creating a basic terrain, or the process of shutting down UDK to compile, then reopen and test…

Anyway, I’ve resurrected Retroshooter for UDK Feb 2013 beta and included a model I created, rigged and brought into UDK to take the place of the Zombies. Unfortunately the poor chaps seem to lose their arms, probably because I disarmed them, making them… (h)armless.

I thank you… Please comment on youTube if you wish, I can’t work out how to turn on comments here without getting hammered by spam. The original video is at Retroshooter on youTube

Retroshooter files This contains…
Continue reading

Posted in Uncategorized | Leave a comment

Ultrasonic sensor, Arduino and Unity

Today I did a quick test to see how well I could get an Arduino to talk to Unity. The video is the result of the experiment.

aurduinoTest - Zip File of my Unity Project

The Ultrasonic code has largely been taken from http://www.instructables.com/id/Simple-Arduino-and-HC-SR04-Example/ and the Unity example was largely taken from http://arduino.cc/forum/index.php?action=printpage;topic=73425.0 I just stuck the two together.

My altered code for the Arduino is below.

Continue reading

Posted in Arduino, Unity | Leave a comment