Feedback!

C++

Here is another arduino library to handle Hue, Saturation, and Brightness as well as Red, Green, and ...
A library to interface Arduino code with the 3 Axis L3G4200D gyroscope....
An open source bitmap converter program built with Open Frameworks...

Assets

Arduino Code

This whole project basically boils down to these three lines of code:
      float avg_b = ((float)b*ratio )+ ((float)b2*(1-ratio));
      float avg_g = ((float)g*ratio )+ ((float)g2*(1-ratio));
      float avg_r = ((float)p*ratio )+ ((float)p2*(1-ratio));
the floating point variable 'ratio' controls the weight of each pixel. Notice that ratio is always between 0 and 1. So if ratio is 0.3 then 1-ratio = 0.7 and we will take 70% of the second pixel and 30% of the first. The code below is for the Joy Gamer so you will need to download the necessary libraries available at our basic Joy Gamer tutorial.
/* LucidTronix Joy Gamer SD Navigator
 * If the compiler gives you an error about sketch being too big make sure to
 * COMMENT OUT both the USE_ACCELEROMETER and the USE COLOR defines in the JoyGamer.h file.  
 * They should look like this:
 //#define USE_ACCELEROMETER
 //#define USE_COLOR
 * For more instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/27
 */

#include <JoyGamer.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#include <SD.h>

Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
JoyGamer jg = JoyGamer(&tft);

File bmpFile1;
File bmpFile2;

// information we extract about the bitmap file
int bmpWidth, bmpHeight;
int bmpWidth2, bmpHeight2;
uint8_t bmpDepth, bmpImageoffset;
uint8_t bmpDepth2, bmpImageoffset2;
float ratio = 0.5;

int img_index1 = 12;
int img_index2 = 16;
int num_images = 60;

void setup(void) {
  jg.initialize();
  jg.start_sd_card();
  bmp_load_and_draw_2_images("face36.bmp","face34.bmp");
}

void loop() {
  bmpdraw_2_images();
}

void bmp_load_and_draw_2_images(char* filename1, char* filename2) {
  bmpFile1 = SD.open(filename1);
  bmpFile2 = SD.open(filename2);
  if (!bmpFile1) {
    jg.draw_string(12,62, "Coodn't find image", ST7735_RED);
  }

  if (!bmpReadHeader(bmpFile1,0)) { 
    jg.draw_string(12,82, "Bad image" , ST7735_RED);
  }
  if (!bmpFile2) {
    jg.draw_string(12,62, "Coodn't find image 2" , ST7735_RED);
  }
  if (!bmpReadHeader(bmpFile2,1)) { 
    jg.draw_string(12,82, "Bad image 2" , ST7735_RED);
  }
  bmpdraw_2_images();
}

/*********************************************/
// This procedure reads 2 bitmaps and draws an average to the screen
// its sped up by reading many pixels worth of data at a time
// instead of just one pixel at a time. increading the buffer takes
// more RAM but makes the drawing a little faster. 20 pixels' worth
// is probably a good place

#define BUFFPIXEL 20
void bmpdraw_2_images() {
  bmpFile1.seek(bmpImageoffset);
  bmpFile2.seek(bmpImageoffset2);
  uint32_t time = millis();
  uint16_t p, p2; 
  uint8_t g, b;
  uint8_t g2, b2;
  int i, j;
  unsigned int file_pos = bmpImageoffset;

  uint8_t sdbuffer[3 * BUFFPIXEL];  // 3 * pixels to buffer
  uint8_t buffidx = 3*BUFFPIXEL;
  uint8_t sdbuffer2[3 * BUFFPIXEL];  // 3 * pixels to buffer
  //set up the 'display window'
  tft.setAddrWindow(0, 0, bmpWidth-1, bmpHeight-1);

  for (i=0; i< bmpHeight; i++) {
    // bitmaps are stored with the BOTTOM line first so we have to move 'up'

    for (j=0; j<bmpWidth; j++) {
      // read more pixels
      if (buffidx >= 3*BUFFPIXEL) {
        bmpFile1.read(sdbuffer, 3*BUFFPIXEL);
        bmpFile2.read(sdbuffer2, 3*BUFFPIXEL);
        buffidx = 0;
      }
      // convert pixel from 888 to 565
      b = sdbuffer[buffidx++];     // blue
      g = sdbuffer[buffidx++];     // green
      p = sdbuffer[buffidx++];     // red
      // convert pixel from 888 to 565
      buffidx -= 3;  

      b2 = sdbuffer2[buffidx++];     // blue
      g2 = sdbuffer2[buffidx++];     // green
      p2 = sdbuffer2[buffidx++];     // red

      file_pos += 3;

      float avg_b = ((float)b*ratio )+ ((float)b2*(1-ratio));
      float avg_g = ((float)g*ratio )+ ((float)g2*(1-ratio));
      float avg_r = ((float)p*ratio )+ ((float)p2*(1-ratio));

      b = (int) avg_r;
      g = (int) avg_g;
      p = (int) avg_b;

      p >>= 3;
      p <<= 6;

      g >>= 2;
      p |= g;
      p <<= 5;

      b >>= 3;
      p |= b;

      tft.drawPixel(j, bmpHeight - i, p);
    }
    if( digitalRead(jg.joystick_btn_pin) == HIGH){
      reset_image(&img_index1, &bmpFile1, file_pos); 
    }
    if(digitalRead(jg.btn_pin) == LOW) {
      reset_image(&img_index2, &bmpFile2, file_pos); 
    }
    float cur_val = analogRead(0);
    ratio = cur_val / 1024.0;
  }
}

void reset_image(int *index, File *a_file, int file_pos)
{
  *index = ++*index % num_images;
  a_file->close();
  String img2 = "face";
  img2 += String(*index);
  img2 += ".bmp";
  char  img2c[11] ;
  for(int i = 0; i < 10; i++){
    img2c[i] = img2.charAt(i);
  }
  if (*index < 10 ) {
    for(int i = 10; i >= 5; i--){
      img2c[i] = img2c[i-1];
    }
    img2c[4] = '0';
  }
  img2c[10] = 0;
  *a_file = SD.open(img2c);
  a_file->seek(file_pos); 
}

boolean bmpReadHeader(File f, int mode) {
  // read header
  uint32_t tmp;

  if (read16(f) != 0x4D42) {
    // magic bytes missing
    return false;
  }

  // read file size
  tmp = read32(f);  
  //Serial.print("size 0x"); Serial.println(tmp, HEX);

  // read and ignore creator bytes
  read32(f);

  if (mode == 0 ) bmpImageoffset = read32(f); 
  else if (mode == 1 ) bmpImageoffset2 = read32(f);  
  //Serial.print("offset "); Serial.println(bmpImageoffset, DEC);

  // read DIB header
  tmp = read32(f);
  //Serial.print("header size "); Serial.println(tmp, DEC);
  if (mode == 0 ) {
    bmpWidth = read32(f);
    bmpHeight = read32(f);
  } 
  else if (mode == 1 ) {
    bmpWidth2 = read32(f);
    bmpHeight2 = read32(f);
  }

  if (read16(f) != 1)
    return false;

  if (mode == 0 ) bmpDepth = read16(f);
  else if (mode == 1 ) bmpDepth2 = read16(f);
  //Serial.print("bitdepth "); Serial.println(bmpDepth, DEC);

  if (read32(f) != 0) {
    // compression not supported!
    return false;
  }
  return true;
}

/*********************************************/

// These read data from the SD card file and convert them to big endian 
// (the data is stored in little endian format!)

// LITTLE ENDIAN!
uint16_t read16(File f) {
  uint16_t d;
  uint8_t b;
  b = f.read();
  d = f.read();
  d <<= 8;
  d |= b;
  return d;
}

// LITTLE ENDIAN!
uint32_t read32(File f) {
  uint32_t d;
  uint16_t b;

  b = read16(f);
  d = read16(f);
  d <<= 16;
  d |= b;
  return d;
}

Arduino Code for Real time clock MCP79410 chip and LED Heart Matrix

This code displays the time on our heart matrix LED Display using the MCP79410 chip. Because we are using the RTC chip we can power down without needing to set the time again on startup. (Sorta what we expect from a clock nowadays, but it wasn't always that way!) The heart of the program is this code:
 if (millis() - last_update > 1000){
    last_update = millis();
    stime = "  ";    
    byte a_hour = clock.hour24();
    String a_hour_s = String(a_hour);
    int hour_int = a_hour_s.toInt();
    String ampm = "am";
    if (!show_24_hours && hour_int > 12){
      ampm = "pm";
      hour_int -= 12;
      a_hour_s = String(hour_int);
    }
    if (a_hour == 0) stime += String(12); 
    else stime += a_hour_s;  
    stime += ":";
    byte a_min = clock.minute();
    if (a_min < 10) stime += String(0);
    stime += String(a_min);
    stime += ":";
    byte a_sec = clock.second();
    if (a_sec < 10) stime += String(0);
    stime += String(a_sec);
    if(!show_24_hours) stime += ampm;
    hm.set_message(stime);
  }
To talk to the RTC chip we use the standard Arduino wire library. You will also need libraries for the MCP79410, the Heart Matrix, and the MSTimer2 all downloadable below!
/* LucidTronix Heart Matrix.
 * A clock using the heart matrix's 
 * on board MCP79410 real time clock chip
 * See the tutorial at: 
 * http://lucidtronix.com/tutorials/36 */
 
#include <Wire.h>
#include <MsTimer2.h>
#include <HeartMatrix.h>
#include <MCP79410.h>

String stime = "";
unsigned long last_update = 0;
boolean show_24_hours = false;
HeartMatrix hm = HeartMatrix(5,6,7); // dataPin is on 5, Latch is on 6, and clock is on 7
MCP79410 clock = MCP79410();

void displayer2(){  
  hm.displayer();
}

void setup()
{
   Wire.begin(); 
   //clock.setDateTime(__DATE__,__TIME__);
   MsTimer2::set(1,displayer2); 
   MsTimer2::start(); 
   pinMode(10, INPUT);
}
 
void loop(){
 hm.on();
 hm.set_scroll_wait(max(30, analogRead(0)/6)); 

 if (millis() - last_update > 1000){
    last_update = millis();
    stime = "  ";    
    byte a_hour = clock.hour24();
    String a_hour_s = String(a_hour);
    int hour_int = a_hour_s.toInt();
    String ampm = "am";
    if (!show_24_hours && hour_int > 12){
      ampm = "pm";
      hour_int -= 12;
      a_hour_s = String(hour_int);
    }
    if (a_hour == 0) stime += String(12); 
    else stime += a_hour_s;  
    stime += ":";
    byte a_min = clock.minute();
    if (a_min < 10) stime += String(0);
    stime += String(a_min);
    stime += ":";
    byte a_sec = clock.second();
    if (a_sec < 10) stime += String(0);
    stime += String(a_sec);
    if(!show_24_hours) stime += ampm;
    hm.set_message(stime);
  }
  
  if(digitalRead(10) == HIGH && millis() - last_update > 500){
    last_update = millis();
    show_24_hours = !show_24_hours;
  }
}

Arduino Code To Read Text From The XBEE on the Serial Port

This code displays Hi at startup. If it gets a message over the serial port it will append the characters to message string and display them on the Heart Matrix. If it receives an 8 (0x08) [the ascii delete control character] the message variable is set to the empty string. You will need the Heart Matrix arduino library and the msTimer2 library, both of which you can download here, at the basic Heart Matrix Tutorial.
/* LucidTronix write messages from
 * the serial port on the Heart Matrix.
 * For instructions details and schematic
 * http://www.lucidtronix.com/tutorials/32
 * If debug is true outputs whatever was received 
 * On serial1 to the standard USB serial. 
 */
 
#include <MsTimer2.h>
#include <HeartMatrix.h>

String message = " Hi  ";
bool debug = true;

HeartMatrix hm = HeartMatrix(5,6,7); // dataPin is on 5, Latch is on 6, and clock is on 7

void displayer2(){  
  hm.displayer();
}

void setup()
{
   if(debug) Serial.begin(9600); // initialize Serial
   Serial1.begin(9600);    	 // initialize Serial1
   MsTimer2::set(1,displayer2); 
   MsTimer2::start(); 
   hm.set_message(" Hello I'm the Heart Matrix from LucidTroniX! ");
}

void loop() {
   hm.on();    
   while (Serial1.available() ){
     char c = Serial1.read();
     if (c == '/') message = "";
     else {
       String s = String(c);
       message += s;
       hm.set_message(message);
     }
     if(debug){
       Serial.print("got the letter:");
       Serial.println(c);
     }
  } 
}

Arduino Code for Sensing Sound and Displaying Audio Waveforms on the Heart Matrix

This code displays sound on our Heart Matrix display. This program uses two arrays, one to keep track of the raw input from the electret microphone and the second to keep track of the state of the LED display. The arrays are both global and declared at the top of the program:
  const int buffer_size = 15;
  int noises[buffer_size];
  const int num_cols = 10;
  int volumes[num_cols];
The audio waveform is detected by keeping a buffer of sound values from the electret microphone. Every delay_speed we shift the buffer down and record a new sound. The volume or amplitude is detected by finding how much the noise buffer varies. We use the individual pixel access which our Heart Matrix library allows to display the waveform like a bar graph. The minimum and maximum variances are kept track of in the lines:
     max_var = max(var, max_var);
     min_var = min(var, min_var);
We map the sound level value to lie between 0 and 8 so that it will correspond to a row index in the LED Matrix.
     var = map(var, min_var, max_var, 0, 9);
Then we iterate over the buffer of volumes and light up the LEDs according to the sound value in the buffer. We go from left to right across the Heart Matrix so that the newest sounds are shown on the right and the oldest ones on the left. (The same direction you read in English). This is the code:
  for (int i = 0 ; i < num_cols; i++){
     for (int j = 0 ; j < volumes[i]; j++){
       hm.set_pixel(i, j, true);  
     }
     for (int j = volumes[i] ; j < 8; j++){
       hm.set_pixel(i, j, false);  
     }
   } 
As in our electret microphone breakout board tutorial, we calculate the noise at any given moment in time by finding the variance of the sound buffer. Importantly, we use unsigned long integers for the variance because this number can get very big and overflow the size of a regular int data type.
/* LucidTronix Heart Matrix.
 * A volume visualizwer using the Heart Matrix's
 * on board LM386 amplifier chip and the electret microphone
 * See the tutorial at: 
 * http://lucidtronix.com/tutorials/35 */
 
#include <MsTimer2.h>
#include <HeartMatrix.h>

// dataPin is on 5, Latch is on 6, and clock is on 7
HeartMatrix hm = HeartMatrix(5,6,7); 

// sound global variables
const int buffer_size = 15;
int noises[buffer_size];
const int num_cols = 10;
int volumes[num_cols];

int cur_index = 0;
unsigned long last_shift = 0;
int mic_pin = 1;
int delay_speed = 40;
unsigned long max_var = 0;
unsigned long min_var = 99999999;

void setup() {
   Serial.begin(9600);
   MsTimer2::set(1,displayer2);
   MsTimer2::start();
   for (int i = 0 ; i < num_cols; i++) volumes[i] = 8;
   for (int i = 0 ; i < buffer_size; i++) noises[i] = 512;
   hm.animate();
}

void loop() {
  hm.on();
  sound_display();
}

void displayer2(){  
  hm.displayer();
}

void sound_display(){
   // Get a value from the electret microphone   
   noises[cur_index] = analogRead(mic_pin) ;
   cur_index++;
   if(cur_index == buffer_size) cur_index = 0;
   // Control the scroll speed with a potentiometer on analog pin 0
   delay_speed = max(10, (analogRead(0)/4));
   
   // Shift the waveform over one column
   if (millis() - last_shift > delay_speed){
     last_shift = millis();
     unsigned int avg = average(noises, buffer_size);
     unsigned long var = variance(noises, buffer_size);
     max_var = max(var, max_var);
     min_var = min(var, min_var);
     var = map(var,min_var , max_var, 0, 9);
     for (int i = 1 ; i < num_cols; i++){
      volumes[i-1] = volumes[i];
     }
     volumes[num_cols-1] = var;
   }
   
   // Write the wave form to LED display
   for (int i = 0 ; i < num_cols; i++){
     for (int j = 0 ; j < volumes[i]; j++){
       hm.set_pixel(i, j, true);  
     }
     for (int j = volumes[i] ; j < 8; j++){
       hm.set_pixel(i, j, false);  
     }
   }
}

int average(int* array, int length){
  int sum = 0;
  int i;
  for(i = 0; i < length ; i++){
    sum += array[i];
  }
  int avg = sum / length ;
  return avg;
}

unsigned long variance(int* array, int length){
  long sum = 0;
  long avg = average(array, length);
  for(int i = 0; i < length ; i++){
    sum += (array[i] - avg)*(array[i] - avg);
  }
  unsigned long var = sum / length;
  return var;	
}

Arduino Code for The MMA8453 Accelerometer

Here is a little code snippet to spit the values of the MMA8453 Accelerometer out on the Serial port. Make sure to include the I2C library and the MMA8453 library in the sketch. You can download them both lower down on this page!
#include "I2C.h"
#include "MMA8453Q.h"

MMA8453Q accelerometer;
Now get accelerometer readings with the functions getX(), getY(), and getZ(), as in:
int x  = accelerometer.getX();
The values can be positive or negative and range from -512 to 512. See the entire example sketch below.
#include <I2C.h>
#include <MMA8453Q.h>

MMA8453Q accelerometer;

void setup()
{
  Serial.begin(9600);
}
 
void loop()
{
  Serial.print("X:");
  Serial.print(accelerometer.getX());
  Serial.print(", Y:");
  Serial.print(accelerometer.getY());
  Serial.print(", Z:");
  Serial.print(accelerometer.getZ());
  Serial.println();
  delay(300);
}

Heart Matrix Hello Code

To get started let's send some text to the Heart Matrix. You can edit the text inside the hm.set_message() function to send your own message.
   hm.set_message(" Your message here! ");
Notice how we set the scroll speed in the line:
  hm.set_scroll_wait(max(20, analogRead(0)/4)); 
Here we are making the thumb wheel potentiometer control the speed the text is scrolling. We use the max function so that it the delay will never be less than 20 milliseconds. Remember the larger the number you give to the hm.set_scroll_wait function the slower the text goes. This is the inverse of speed. See the entire sketch below:
/* LucidTronix Heart Matrix LEO LED Display.
 * Text on the Heart Matrix
 * For instructions details and schematic, See:
 * http://www.lucidtronix.com/tutorials/57  */
#include <MsTimer2.h>
#include <HeartMatrix.h>

HeartMatrix hm = HeartMatrix(5,6,7); // dataPin is on 5, Latch is on 6, and clock is on 7

void displayer2(){  
  hm.displayer();
}

void setup()
{
   MsTimer2::set(1,displayer2); 
   MsTimer2::start(); 
   hm.set_message(" Hello I'm the Heart Matrix from LucidTroniX! ");
}
   
void loop(){
  hm.on();
  hm.set_scroll_wait(max(20, analogRead(0)/4)); 
}

Wearable Wayfinder Diagnostic Arduino Code

This code shows the output from all the Wearable Wayfinder's sensors. The buttons trigger the piezo speaker and changing the text color. While it isn't to exciting this code serves as a nice stub for any project you may want to do with the Wearable Wayfinder. It is a good idea to make sure this code runs correctly before diving all the way into the awesome world of the Wayfinder :)!
/* LucidTronix Wearable Wayfinder
 * For instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/56
 */

#include <WearableWayfinder.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <Wire.h>
#include <SPI.h>
#include <Color.h>
#include <HMC5883L.h>
#include <MCP79410.h>
#include <I2C.h>
#include <MMA8453Q.h>

// for leo:
Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
WearableWayfinder ww = WearableWayfinder(&tft);
int cur_note = 0;
int old_pot = 0;
char notes[] = {'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };

void setup(){  
  Wire.begin(); // Start the I2C interface.
  ww.initialize();
  //ww.set_date_time(__DATE__, __TIME__);
}
void loop(){
  ww.draw_string(5, 4, "Time", ww.stroke.color_16(), 1);
  ww.show_time(10, 15);
  ww.draw_string(5, 36, "Date", ww.stroke.color_16(), 1);
  ww.show_date(10, 45);
  ww.draw_string(5, 66, "Compass", ww.stroke.color_16(), 1);
  ww.show_heading(10, 75);
  ww.draw_string(5, 91, "Accelerometer", ww.stroke.color_16(), 1);
  ww.show_accelerometer(10, 100);
  ww.draw_string(5, 136, "Potentiometer", ww.stroke.color_16(), 1);
  int pot =  analogRead(0);
  if(pot != old_pot) tft.fillRect(10, 145, 50, 10, ww.background.color_16()); 
  ww.print_integer(10, 145, pot, 1);
  old_pot = pot;
  if(digitalRead(ww.btn1_pin) == HIGH){
    ww.playNote(notes[cur_note++], 300);
    if (cur_note == 8) cur_note = 0;
  } 
  if(digitalRead(ww.btn2_pin) == HIGH){
    ww.stroke = Color(random(255), random(255), random(255));
  } 
}

Wearable Wayfinder Color Clock Arduino Code

This code shows time with a beautiful color wheel as well as plain vanilla digital displays of the current alarm time and the date. You will need several libraries installed which are all available at the basic Wearable Wayfinder tutorial. The crucial function for showing the color wheel is here:
void color_clock(int ax, int ay){
  int hour_angle = cur_time.hour()*30;
  int minute_angle = cur_time.minute()*6;
  int second_angle = cur_time.second()*6;
  if (cur_min != cur_time.minute() ){
    cur_min = cur_time.minute();
    tft.fillScreen(ST7735_WHITE);
    ww.draw_numbers(ST7735_BLACK);
  }
  if (cur_sec != cur_time.second() ){
   last_sec = millis();
   cur_sec = cur_time.second();
  }
  millisecond_six = constrain(((millis()-last_sec) / 167),0,6);
  int second_angle_hi_res = (cur_sec*6) + millisecond_six;
  ww.draw_orbiter(ax,ay,hour_angle, 15,3);
  ww.draw_ray(ax,ay,hour_angle, 15);
  ww.draw_orbiter(ax,ay,minute_angle, 25,2); 
  ww.draw_ray(ax,ay,minute_angle, 25);
  ww.draw_orbiter(ax,ay,second_angle_hi_res, 35,5); 
}
See the whole sketch below:
/* LucidTronix Wearable Wayfinder Color Clock
 * For instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/29
 */

#include <WearableWayfinder.h>
#include <Adafruit_GFX.h>   stroke // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <Wire.h>
#include <SPI.h>
#include <Color.h>
#include <HMC5883L.h>
#include <MCP79410.h>
#include <I2C.h>
#include <MMA8453Q.h>

// for leo:
Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
WearableWayfinder ww = WearableWayfinder(&tft);

int speakerPin = 6;
int potPin = 0;
int cur_sec = 0;
int cur_min = 0;
int millisecond_six = 0;
unsigned int last_sec = 0;
unsigned int last_press = 0;
int old_val = 0;
char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
int cur_note = 0;

boolean alarm_on = false;
boolean set_minute = false;

DateTime cur_time, alarm_time;
void setup(){  
  Wire.begin(); // Start the I2C interface.
  ww.initialize();
  //ww.set_date_time(__DATE__, __TIME__);
  alarm_time = ww.now();
}
void loop(){
  cur_time = ww.now();
  ww.show_time(2,4);
  ww.show_time(80,4, alarm_time);
  if(set_minute) tft.drawLine(96, 12, 106, 12,  Color(0,255,0).color_16());
  else {
    tft.drawLine(80, 12, 90, 12, ww.background.color_16());
    if (alarm_time.hour()>9) tft.drawLine(80, 12, 90, 12, Color(255,0,0).color_16());
    else tft.drawLine(85, 12, 90, 12, Color(255,0,0).color_16());
  }
  ww.show_date(4, 145);
  color_clock(64,80);
  check_buttons();
  if (alarm_on){
    ww.draw_string(2, 14,"Alarm is ON", ww.stroke.color_16(), 1);
    if (alarm_time.hour() == cur_time.hour()
      && alarm_time.minute() == cur_time.minute()){
         ww.playNote(names[cur_note++], 300);
         if (cur_note == 8) cur_note = 0;
     }
  } else {
    analogWrite(speakerPin, 0);
    ww.draw_string(2, 14,"Alarm is OFF", ww.stroke.color_16(), 1);
  }
}  

void color_clock(int ax, int ay){
  int hour_angle = cur_time.hour()*30;
  int minute_angle = cur_time.minute()*6;
  int second_angle = cur_time.second()*6;
  if (cur_min != cur_time.minute() ){
    cur_min = cur_time.minute();
    tft.fillScreen(ST7735_WHITE);
    ww.draw_numbers(ST7735_BLACK);
  }
  if (cur_sec != cur_time.second() ){
   last_sec = millis();
   cur_sec = cur_time.second();
  }
  millisecond_six = constrain(((millis()-last_sec) / 167),0,6);
  int second_angle_hi_res = (cur_sec*6) + millisecond_six;
  ww.draw_orbiter(ax,ay,hour_angle, 15,3);
  ww.draw_ray(ax,ay,hour_angle, 15);
  ww.draw_orbiter(ax,ay,minute_angle, 25,2); 
  ww.draw_ray(ax,ay,minute_angle, 25);
  ww.draw_orbiter(ax,ay,second_angle_hi_res, 35,5); 
}

void check_buttons(){
 if ( digitalRead(ww.btn1_pin) == HIGH && millis() - last_press > 800){
   tft.fillRect(0, 14, 128, 12, ST7735_WHITE);  
   alarm_on = !alarm_on;
   ww.draw_numbers(ST7735_BLACK);
   last_press = millis();
 }
 if (digitalRead(ww.btn2_pin) == HIGH && millis() - last_press > 800){
   set_minute = !set_minute;
   tft.drawLine(80, 12, 110, 12, ST7735_WHITE);
   last_press = millis();
 }
 int val = analogRead(potPin);
 if (abs(val - old_val) > 1){
   tft.fillRect(64, 0, 128, 12, ST7735_WHITE);  
   if(set_minute){
     val = map(val,0,1024,0,60);
     alarm_time = DateTime(cur_time.year(), cur_time.month(), cur_time.day(), 
                          alarm_time.hour(), val, 0 );
   } else { //set hour 
     val = map(val,0,1024,0,24);
     alarm_time = DateTime(cur_time.year(), cur_time.month(), cur_time.day(), 
                          val, alarm_time.minute(), 0 );
   } 
 }
 old_val = analogRead(potPin);
}

Cellular Automata on the Wearable Wayfinder Code

Here is code for displaying a several different beautiful cellular Automata on your Wearable Wayfinder. We display the 1D cellular Automata by moving down the screen one row of pixels at a time. The pixels in each row are determined by the three pixels above them in the previous row. The mapping between the three pixels in the parent row and the one pixel in the next row is given by the cellular automata rule. We handle this in this function:
int get_bit_from_rules(int a, int b, int c) {
  if (a == 1 && b == 1 && c == 1) return rules[cur_rule][0];
  if (a == 1 && b == 1 && c == 0) return rules[cur_rule][1];
  if (a == 1 && b == 0 && c == 1) return rules[cur_rule][2];
  if (a == 1 && b == 0 && c == 0) return rules[cur_rule][3];
  if (a == 0 && b == 1 && c == 1) return rules[cur_rule][4];
  if (a == 0 && b == 1 && c == 0) return rules[cur_rule][5];
  if (a == 0 && b == 0 && c == 1) return rules[cur_rule][6];
  if (a == 0 && b == 0 && c == 0) return rules[cur_rule][7];
  return 0;
}
There are 8 different possible arrangements of 3 pixels (2^3). We check all possibilities and then return the bit for the child row as determined by the current rule.
We only have to keep track of the previous row and the current row and we do this with the two arrays declared at the top:
uint8_t scanline[scanline_size];
uint8_t nextline[scanline_size];
Then we get the tree pixels from the parent row in the lines:
    if (i > 0) left = get_bit_from_index(i-1);   // Left neighbor state
    int me = get_bit_from_index(i);       // Current state
    int right = get_bit_from_index(i+1);  // Right neighbor state
The functions:
boolean get_bit_from_index(int index){
  int byte_index = index / 8;
  int bit_index = 7 - (index % 8);
  byte cur_b = scanline[byte_index];
  if ( (cur_b & (1 << bit_index)) != 0x00 ) return 1;
  else return 0;
}

void set_bit_from_index(int index, boolean abit){
  int byte_index = index / 8;
  int bit_index = 7 - (index % 8);
  byte cur_b = nextline[byte_index];
  if ( abit)  cur_b |= (1 << bit_index);
  else cur_b &= ~(1 << bit_index) ;
  nextline[byte_index] = cur_b;
}
are used to get and set the individual bits from the array of bytes scanline and nextline. This is more efficient than wasting a whole byte to just store one binary value. See the entire code below. And make sure you have all the necessary libraries installed from the basic Wearable Wayfinder tutorial before you try to compile :).
/* LucidTronix Wearable Wayfinder Cellular Automata Animation
 * Code by Samwell Freeman August 2014
 * For instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/9
 */

#include <WearableWayfinder.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <Wire.h>
#include <SPI.h>
#include <Color.h>
#include <HMC5883L.h>
#include <MCP79410.h>
#include <I2C.h>
#include <MMA8453Q.h>

// for leo: 
Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
WearableWayfinder ww = WearableWayfinder(&tft);

const int scanline_size = 20;
uint8_t scanline[scanline_size];
uint8_t nextline[scanline_size];
int cur_rule = 0;
const int num_rules = 8;
boolean rules[num_rules][8] = {
                                {0,1,0,1,1,0,1,0},
                                {0,0,0,1,1,1,1,0},
                                {1,0,1,1,0,1,1,0},
                                {0,1,1,1,1,1,1,0},
                                {0,1,0,1,0,1,1,0},
                                {1,0,0,1,1,0,0,1},
                                {1,1,0,0,0,0,0,1},
                                {0,0,0,1,1,0,1,0}
                              }; 

int cur_y = 0;
int mode = 0;
int num_modes = 4;
unsigned int last_press = 0;

void setup(){
  ww.initialize();
  int bx = 0;
  int by = 0;
  for (int i = 0; i < scanline_size; i++) {
    scanline[i] = 0;
    if ( i == scanline_size/2) scanline[i] = 0x08;
  }
}

void loop(){
  for (int i = 0; i < (scanline_size*8)-1; i++) {
    int left = 0;
    if (i > 0) left = get_bit_from_index(i-1);   // Left neighbor state
    int me = get_bit_from_index(i);       // Current state
    int right = get_bit_from_index(i+1);  // Right neighbor state
    boolean abit = get_bit_from_rules(left,me,right); // Compute next generation state based on ruleset
    set_bit_from_index(i, abit);
    if (mode == 0){
      int cur_r = 15;
      int cur_g = 2;
      int cur_b = random(200, 250);
      ww.background = Color(cur_r, cur_g, cur_b); 
      cur_r = 230;
      cur_g = 62;
      cur_b = 0;
      ww.fill = Color(cur_r, cur_g, cur_b); 
    }
    else if (mode == 1){
      int cur_r =  0;
      int cur_g =  0;
      int cur_b = 0;
      ww.background = Color(cur_r, cur_g, cur_b); 
      cur_r =  random(23,55);
      cur_g =   62;
      cur_b = 230;
      ww.fill = Color(cur_r, cur_g, cur_b); 
    }
    else if (mode == 2){
      int cur_r =  5;
      int cur_g = random(150,222);
      int cur_b = 215;
      ww.background = Color(cur_r, cur_g, cur_b); 
      cur_r = 219;
      cur_g = 200;
      cur_b = 30;
      ww.fill = Color(cur_r, cur_g, cur_b); 
    }
    if (abit) tft.drawPixel(cur_y, i, ww.fill.color_16());
    else tft.drawPixel(cur_y, i, ww.background.color_16());
    if( digitalRead(ww.btn1_pin) == HIGH && millis() - last_press > 500 ){
      cur_rule = ++cur_rule%num_rules;
      for (int i = 0; i < scanline_size; i++) {
        nextline[i] = 0;
        if ( i == scanline_size/2) nextline[i] = 0x08;
      }
      last_press = millis();
      break;
    }
    if( digitalRead(ww.btn2_pin) == HIGH ){
      ww.clear_screen(ww.background);
      for (int i = 0; i < scanline_size; i++) {
        nextline[i] = 0;
        if ( i == scanline_size/2) nextline[i] = 0x08;
      }
      mode = ++mode%num_modes;
      break;
    }
  }
  cur_y++;
  if (cur_y > 128) cur_y = 0;
  for (int i = 0; i < scanline_size; i++) {
    scanline[i] = nextline[i];
  }
}

boolean get_bit_from_index(int index){
  int byte_index = index / 8;
  int bit_index = 7 - (index % 8);
  byte cur_b = scanline[byte_index];
  if ( (cur_b & (1 << bit_index)) != 0x00 ) return 1;
  else return 0;
}

void set_bit_from_index(int index, boolean abit){
  int byte_index = index / 8;
  int bit_index = 7 - (index % 8);
  byte cur_b = nextline[byte_index];
  if ( abit)  cur_b |= (1 << bit_index);
  else cur_b &= ~(1 << bit_index) ;
  nextline[byte_index] = cur_b;
}

int get_bit_from_rules(int a, int b, int c) {
  if (a == 1 && b == 1 && c == 1) return rules[cur_rule][0];
  if (a == 1 && b == 1 && c == 0) return rules[cur_rule][1];
  if (a == 1 && b == 0 && c == 1) return rules[cur_rule][2];
  if (a == 1 && b == 0 && c == 0) return rules[cur_rule][3];
  if (a == 0 && b == 1 && c == 1) return rules[cur_rule][4];
  if (a == 0 && b == 1 && c == 0) return rules[cur_rule][5];
  if (a == 0 && b == 0 && c == 1) return rules[cur_rule][6];
  if (a == 0 && b == 0 && c == 0) return rules[cur_rule][7];
  return 0;
}

Joy Gamer Demo Code

Here is a little Arduino sketch to get started with the Joy Gamer. The main loop is just 6 lines of code which outputs the current readings from the accelerometer and joystick. In the setup function we also check for an SD card and if there is one we print out "SD GOOD" and otherwise print out SD Failed.
While simple this code shows some of the common design patterns for coding on the Joy Gamer. First notice that we declare both Adafruit_ST7735 and JoyGamer variables:
Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
JoyGamer jg = JoyGamer(&tft);
This allows us to directly access all of the functionality of the Adafruit graphics library for the ST7735 screen directly by calling functions on our tft variable. However, we also pass a pointer to this variable to the JoyGamer. Don't worry if you don't know what pointers are, but thats what we get when we put the ampersand before a variable. Since the JoyGamer knows about the tft LCD screen our library can also create graphics. So when you are writing code for the joy gamer you can use functions from the JoyGamer library and/or from the Adafruit graphics library. This is why we have to include all those libraries at the top of this file.
/* LucidTronix Joy Gamer
 * For instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/48
 */

#include <JoyGamer.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#include <Color.h>
#include <I2C.h>
#include <MMA8453Q.h>
#include <SD.h>

Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
JoyGamer jg = JoyGamer(&tft);

void setup(){  
  jg.initialize();
  jg.start_sd_card();
}

void loop(){ 
  jg.show_accelerometer(10, 40);
  tft.fillRect(49, 75, 40, 22, jg.background.color_16());
  jg.draw_string(10, 75, "Joy X:", jg.stroke.color_16(), 1);
  jg.print_integer(50, 75, analogRead(0), 1);
  jg.draw_string(10, 85, "Joy Y:", jg.stroke.color_16(), 1);
  jg.print_integer(50, 85, analogRead(1), 1);
}

Here is some code to get started with the gyro. Make sure to include the gyro library and the standard Arduino wire library (the communication protocol used by the gyro). Then in the setup function start up the Wire communication and initialiaze the gyro with a scale. You can use 2000, 500, or 250. This number determines the range of the values from the gyro.
  Wire.begin();
  gyro.initialize(2000);
  Serial.begin(9600);
In the main loop we get X, Y, and Z orientations with the get functions, for example:
  x = gyro.getX();
Then we spit these values out on the serial port wait 300 milliseconds and repeat. See the whole example sketch and grab the library from the download section below.
/* LucidTronix L3G4200D Gyroscope Library
 * For instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/63
 */
 
#include <L3G4200D.h>
#include <Wire.h>

L3G4200D gyro;

int x, y, z;

void setup(){
  Wire.begin();
  gyro.initialize(2000);
  Serial.begin(9600);
}

void loop(){
  x = gyro.getX();
  Serial.print("X:");
  Serial.print(x);

  y = gyro.getY();
  Serial.print(" Y:");
  Serial.print(y);

  z = gyro.getZ();
  Serial.print(" Z:");
  Serial.println(z);
  delay(300); //Just here to slow down the serial to make it more readable
}

Display a Bitmap on the Joy Gamer Arduino Code

Here is Arduino Code to show a bitmap image on the Joy Gamer. Our library does almost all the work for you :) Just call the function:
  jg.bmp_load_and_draw_image("sample.bmp");
Make sure you have the SD card inserted properly, and loaded with the bitmap file "sample.bmp". You can of course load whatever images you want. The images do have to be in bitmap format and at largest 128 by 160 pixels. We wrote a little program to convert images into the bmp format, you can download it below.
/* LucidTronix Joy Gamer
 * For instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/48
 */

#include <JoyGamer.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#include <Color.h>
#include <I2C.h>
#include <MMA8453Q.h>
#include <SD.h>

Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
JoyGamer jg = JoyGamer(&tft);

void setup(){
  jg.initialize();
  jg.start_sd_card();
  jg.bmp_load_and_draw_image("sample.bmp");
}

void loop() {

}

SD Navigator Arduino Code

Here is the Arduino code behind the SD card navigator on the Joy Gamer. We are pushing right up against the edge of what the little Atmega32u4 chip can handle so we had to hack a bit to make this work. First in the JoyGamer.h file you have to comment out the defines like this:
//#define USE_ACCELEROMETER
//#define USE_COLOR
This way we don't load libraries we won't use and we can fit all the SD navigator code on the device. Otherwise you will get the nasty sketch too big error.
Okay now that the code compiles let's have a bit of explanation. This code has 3 modes: display a directory, display a bitmap or display a text file. There is a global variable cur_file which is either the directory, bitmap or text file we are currently viewing. We keep track of the modes using an enum defined at the top of the file. This is a little cleaner and easier to read than just using an int for the mode:
enum modes_t{
  DISPLAY_DIRECTORY,
  DISPLAY_BITMAP,
  DISPLAY_TEXT
};

modes_t mode = DISPLAY_DIRECTORY;
Then in the main loop we have switch statement on the current mode. In the setup function we open the root directory and set it as the cur_file in the function open_file(). This function sets the mode appropriately given the type of file fed in as an argument.
Throughout the code we check if jg.btn_pin is pressed and if so we jump back to displaying the root directory. Links to all the necessry libraries can be found at the Joy Gamer Tutorial. See the whole file below:
/* LucidTronix Joy Gamer SD Navigator
 * If the compiler gives you an error about sketch being too big make sure to
 * COMMENT OUT both the USE_ACCELEROMETER and the USE COLOR defines in the JoyGamer.h file.  
 * They should look like this:
 //#define USE_ACCELEROMETER
 //#define USE_COLOR
 * For more instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/21
 */

#include <JoyGamer.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#include <SD.h>

Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
JoyGamer jg = JoyGamer(&tft);

enum modes_t{
  DISPLAY_DIRECTORY,
  DISPLAY_BITMAP,
  DISPLAY_TEXT
};

modes_t mode = DISPLAY_DIRECTORY;

int cursor_x = 64;
int cursor_y = 64;

File cur_file;
File selected_file;
int cur_dir_files;
const int max_files = 12;
String files[max_files];

int line_height = 12;
int char_width = 6;
unsigned long last_switch = 0;

void setup(){
  jg.initialize();
  jg.start_sd_card();
  open_file("/");
}

void loop(){
  switch(mode){
    case DISPLAY_DIRECTORY:
      handle_cursor();
      for(int i = 0; i < cur_dir_files; i++){
        if(cursor_y > i*10+ 10 && cursor_y < i*10 + 20){
          jg.draw_string(10, i*10 + 10, files[i], ST7735_MAGENTA);
          if(digitalRead(jg.joystick_btn_pin) == HIGH) {
            open_file(files[i]);
            break;
          }
        } 
        else {
          jg.draw_string(10, i*10 + 10, files[i], ST7735_BLACK);
        }
        if(digitalRead(jg.btn_pin) == LOW && millis() - last_switch > 500) {
          open_file("/");
          break;
        }
      }
      break;
    case DISPLAY_TEXT:
      jg.clear_screen(ST7735_WHITE);
      jg.clear_screen();
      display_text_file();
      break;
    case DISPLAY_BITMAP:
      jg.bmpdraw(cur_file);
      delay(2000);
      open_file("/");
      break;
  }
}

void handle_cursor(){
  tft.fillCircle(cursor_x, cursor_y, 5, ST7735_WHITE);
  int delta_x = map(analogRead(0), 0, 1024, -6, 6); 
  int delta_y = map(analogRead(1), 0, 1024, -8, 8);
  cursor_x = constrain(cursor_x + delta_x, 5, 123);
  cursor_y = constrain(cursor_y + delta_y, 5, 155); 
  tft.fillCircle(cursor_x, cursor_y, 5, ST7735_GREEN);
}

void open_file(String filename){
  jg.clear_screen(ST7735_WHITE);
  cur_file.seek(0);
  cur_file.close();
  delay(100);
  char buffer[13];
  filename.toCharArray(buffer, 13);
  cur_file = SD.open(buffer);
  filename.toLowerCase();
  if(cur_file.isDirectory()){
    mode = DISPLAY_DIRECTORY;
    load_dir(cur_file);
  }
  else if(filename.lastIndexOf(".bmp") != -1){
    mode = DISPLAY_BITMAP;
  }
  else {
    mode = DISPLAY_TEXT;
  }
  last_switch = millis();
}

void load_dir(File dir){
  for(int i = 0; i < max_files; i++) files[i] = "";
  jg.clear_screen(ST7735_WHITE);
  dir.rewindDirectory();
  File entry =  dir.openNextFile();
  cur_dir_files = 0;
  while(entry && SD.exists(entry.name()) && max_files > cur_dir_files){
    String filename = entry.name();
    if(filename[0] != '.' && filename[0] != '_' && filename.lastIndexOf('~') == -1){ // don't show hidden files
      files[cur_dir_files] = filename;
      cur_dir_files++;
    }
    entry.close();
    entry = dir.openNextFile();
  }
  dir.rewindDirectory();
}

void display_text_file(){
  int aax = 2;
  int aay = 3;
  tft.setTextColor(ST7735_BLACK);
  while(cur_file && cur_file.available() && aay < 150){
    char cur_c = cur_file.read();
    if(cur_c == 10 || cur_c == 13 || (cur_c == ' ' && aax > 85)){
      aay += line_height;
      aax = 2;
    }
    else {   
      tft.setCursor(aax, aay);
      tft.print(cur_c);
      aax += char_width;
      if (aax >= 118) {
        aax = 2;
        aay += line_height;
      }
    }
    delay(10);
    if(digitalRead(jg.btn_pin) == LOW && millis() - last_switch > 500) {
      open_file("/");
      break;
    }
  }
  delay(1500);
}

JoyGamer Bitmap Slideshow Arduino Code

This code expects a bunch of bitmap files in the root directory of an SD card named exp0.bmp, exp1.bmp, up to exp[num_pics].bmp. You can create these files using our bitmap converter tool. In the main loop a random number between 0 and num_pics is chosen then we construct the filename correponding to this image in the line:
 pic_name = base_name + cur_pic + String(".bmp");
We display that image wait 3 seconds and do it again. All the necessary libraries can be downloaded from our main Joy Gamer Tutorial.
/* LucidTronix Joy Gamer SD Navigator
 * If the compiler gives you an error about sketch being too big make sure to
 * COMMENT OUT both the USE_ACCELEROMETER and the USE COLOR defines in the JoyGamer.h file.  
 * They should look like this:
 //#define USE_ACCELEROMETER
 //#define USE_COLOR
 * For more instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/65
 */

#include <JoyGamer.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#include <Color.h>

#include <SD.h>

// for leo:
Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
//for uno etc.. 
//Adafruit_ST7735 tft = ST7735(10, 9, 8);
// for mega
//Adafruit_ST7735 tft = ST7735(53, 9, 8);

JoyGamer jg = JoyGamer(&tft);

int cur_pic = 0;
const int num_pics = 19;
String base_name = "exp";
String pic_name = base_name + "2.bmp";
void setup(){
  jg.initialize();
  jg.clear_screen();
  jg.start_sd_card();
}

void loop() {
 cur_pic = random(0, num_pics);
 pic_name = base_name + cur_pic + String(".bmp");
 jg.bmp_load_and_draw_image(pic_name);
 delay(3000);
}

Bluetooth Transceiver

Here is Arduino code to talk to a Wearable Wayfinder over bluetooth. The loop function just 3 lines long. We read the Serial Port to see if any messages have been sent, we check the inputs on the Wayfinder to see if the buttons are being pushed or the potentiometer has been spun, then if we have received a message we scroll it across the Wayfinder's screen. Note that we use the Serial1 port for bluetooth communication as in:
  if (ww.btn_1_pressed()){
    ww.clear_screen();
    Serial1.println("Btn 1 pressed");
    ww.draw_string(5, 96, "Btn 1 pressed", ww.stroke.color_16(), 1);
  }
This is because the Wayfinder is based on the Arduino Leonardo and the Serial port refers to the built-in Serial line for communication over USB. When you spin the wayfinder's potentiometer or press its buttons we send messages over bluetooth to the Serial Monitor. You can send messages to the Wayfinder by typing them into the Serial Monitor and pressing return/enter.
/* LucidTronix Wearable Wayfinder 
 * Blue Tooth Transeiver
 * written by samwell freeman for LucidTronix
 * For instructions, and documentation See:
 * http://www.lucidtronix.com/tutorials/64
 */

#include <WearableWayfinder.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <Wire.h>
#include <SPI.h>
#include <Color.h>
#include <HMC5883L.h>
#include <MCP79410.h>
#include <I2C.h>
#include <MMA8453Q.h>

// for leo:
Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
WearableWayfinder ww = WearableWayfinder(&tft);

int old_pot = 0;
const int buffer_size = 18;
char buffer_in[buffer_size];
int char_index = 0;

void setup() {
  Serial1.begin(115200);    	//initialize Serial1
  Serial.begin(115200);    	//initialize Serial1

  for(int i = 0; i < buffer_size; i++) buffer_in[i] = 0;

  ww.initialize();
  ww.draw_string(5, 6, "BLUE", Color(30, 35, 255).color_16(), 3);
  ww.draw_string(15, 36, "TOOTH", Color(30, 35, 255).color_16(), 3);
  ww.draw_string(2, 76, "transiever", Color(30, 135, 235).color_16(), 2);
  
  delay(2500);
  ww.clear_screen();

}

void loop() {   
  if (Serial1.available() > 0){
    char cur_char;
    while((cur_char = Serial1.read()) != -1){
      buffer_in[char_index] = cur_char;
      char_index = ++char_index % buffer_size;
    }
    Serial1.print("received string:");
    Serial1.println(buffer_in);
    tft.fillRect(1, 145, 127, 10, ww.background.color_16());
    ww.draw_string(2, 145, buffer_in, ww.stroke.color_16(), 1);
  }
  
  if (ww.btn_1_pressed()){
    ww.clear_screen();
    Serial1.println("Btn 1 pressed");
    ww.draw_string(5, 106, "Btn 1 pressed", ww.stroke.color_16(), 1);
  }
  
  if (ww.btn_2_pressed()){
    ww.clear_screen();
    Serial1.println("Btn 2 pressed");
    ww.draw_string(5, 106, "Btn 2 pressed", ww.stroke.color_16(), 1);
  }

  int pot =  analogRead(0);
  if(abs(pot-old_pot) > 5) {
    old_pot = pot;
    tft.fillRect(10, 45, 50, 10, ww.background.color_16()); 
    ww.draw_string(5, 36, "Potentiometer", ww.stroke.color_16(), 1);
    ww.print_integer(10, 45, pot, 1);
    Serial1.print("Wheel Reeds:");
    Serial1.println(pot);
  }
}

The App

This is the cpp file that does all the work in this little app. There are 2 ways to make bitmaps.
1) Press spacebar and select an image from your computer. It will display in the window and you can press 's' to save it. This is nice but you can only make 1 Bitmap at a time. So we also developed option 2...
2) You can also create bitmaps from every image in a folder. Change the lines near the top of the file:
	directoryName = "/Users/samfriedman/Pictures/small/";
        newFileName = "tl_";
Change the directoryName path to point to the folder with images that you want to make bitmaps from. The bitmaps will be numbered and have the prefix of the variable newFileName, so you may want to adjust that variable as well. Re-compile the project, run it and press 'd'.
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
	ofSetLogLevel(OF_LOG_VERBOSE);
	directoryName = "/Users/samfriedman/Pictures/small/";
    newFileName = "tl_";
}

//--------------------------------------------------------------
void ofApp::update(){
}

//--------------------------------------------------------------
void ofApp::draw()
{
	ofDrawBitmapString("Press spacebar to open an image, \"s\" to save the processed output", 20, 15);
	
	for (unsigned int i=0; i<loadedImages.size(); i++){
		loadedImages[i].draw(0, i*160 + 20);

	}
	
	for (unsigned int i=0; i<processedImages.size(); i++){
		processedImages[i].draw(processedImages[i].getWidth()+5, i*160 + 20);
	}


	
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
	
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){
	if (key == 'd'){
        dir.listDir(directoryName);
        dir.sort(); // in linux the file system doesn't return file lists ordered in alphabetical order
        
        processedImages.clear();
        loadedImages.clear();
        
        // you can now iterate through the files and load them into the ofImage vector
        for(int i = 0; i < (int)dir.size(); i++){
            //Load the selected image
            ofImage image;
            int lastindex = dir.getPath(i).find_last_of(".");
            string rawname = dir.getPath(i).substr(0, lastindex);
            
            image.loadImage(dir.getPath(i));
            image.resize(128, 160);
            loadedImages.push_back(image);
            
            //Make some short variables
            int w = image.getWidth();
            int h = image.getHeight();
            
            //Make a new image to save manipulation by copying the source
            ofImage processedImage = image;
            
            //Walk through the pixels
            for (int y = 0; y < h; y++){
                for (int x = 0; x < w; x++){
                    
                    //Capture the colors for the row
                    ofColor color = image.getColor(x, y);
                    processedImage.setColor(x, y, color);
                }
            }
            //Store the processed image
            processedImages.push_back(processedImage);\
            processedImages[i].saveImage(dir.getOriginalDirectory()+newFileName+ofToString(i)+".bmp");

        }

    }
	if (key == ' '){
		
		//Open the Open File Dialog
		ofFileDialogResult openFileResult= ofSystemLoadDialog("Select a jpg or png"); 
		//Check if the user opened a file
		if (openFileResult.bSuccess){
			
			ofLogVerbose("User selected a file");
			
			//We have a file, check it and process it
			processOpenFileSelection(openFileResult);
			
		}else {
			ofLogVerbose("User hit cancel");
		}
	}
	
	if (key == 's'){
		
		if (processedImages.size()==0){
			//User is trying to save without anything to output - bail
			return;
		}
		
		//
		ofFileDialogResult saveFileResult = ofSystemSaveDialog(ofGetTimestampString() + ".bmp" , "Save your file as a bitmap");
		if (saveFileResult.bSuccess){
			processedImages[0].saveImage(saveFileResult.filePath);
		}
	}
	
	
}

//Sort function for stl::sort http://www.cplusplus.com/reference/algorithm/sort/
bool sortColorFunction (ofColor i,ofColor j) { 
	return (i.getBrightness()<j.getBrightness()); 
}


void ofApp::processOpenFileSelection(ofFileDialogResult openFileResult){
	
	ofLogVerbose("getName(): "  + openFileResult.getName());
	ofLogVerbose("getPath(): "  + openFileResult.getPath());
	
	ofFile file (openFileResult.getPath()); 
	
	if (file.exists()){
		//Limiting this example to one image so we delete previous ones
		processedImages.clear();
		loadedImages.clear();
		
		ofLogVerbose("The file exists - now checking the type via file extension");
		string fileExtension = ofToUpper(file.getExtension());
		
		//We only want images
		if (fileExtension == "JPG" || fileExtension == "PNG" || fileExtension == "BMP" || fileExtension == "GIF") {
			
			//Save the file extension to use when we save out
			originalFileExtension = fileExtension;
			
			//Load the selected image
			ofImage image;
			image.loadImage(openFileResult.getPath());
			image.resize(128, 160);
			loadedImages.push_back(image);
			
			//Make some short variables 
			int w = image.getWidth();
			int h = image.getHeight();
			
			//Make a new image to save manipulation by copying the source
			ofImage processedImage = image;
			
			//Walk through the pixels
			for (int y = 0; y < h; y++){
				for (int x = 0; x < w; x++){
					
					//Capture the colors for the row
					ofColor color = image.getColor(x, y); 
					processedImage.setColor(x, y, color);
				}
			}
			//Store the processed image
			processedImages.push_back(processedImage);
		}
        ofLogVerbose("The file has been copied.");

	}
	
}
//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){
	
}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
	
}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){
	
}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){
	
}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){
	
}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){
	
}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 
	
}

Lucid Bounce Arduino Code

Here is code for the Joy Gamer to Play the paddle bounce game. First, make sure you have necessary libraries downloaded from the main Joy Gamer tutorial. The point of the game is just to keep the ball alive the longer you do it the more points you get. The whole program boils down to three functions and one struct. The struct is called point to encapsulate the data for the ball, the ball's direction and speed of travel, and the paddle.
struct point{ 
  float x;
  float y;
  float sizer;
  Color c;
};
The functions are start_game(), draw_game() and draw_stats(). The start_game function clears the screen and resets the ball's position. The draw_stats() function draws the score and the remaining lives to the top of the screen. The draw_game() function does most of the work reading in the joy stick data and translating that into a position for the paddle. Then update the ball's position using the ball speed vector.
/* LucidTronix JoyGamer Joy Bounce
 * For instructions, details and schematic, See:
 * http://www.lucidtronix.com/tutorials/55
 */

#include <JoyGamer.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#include <Color.h>
#include <I2C.h>
#include <MMA8453Q.h>
#include <SD.h>

Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);
JoyGamer jg = JoyGamer(&tft);

struct point{ 
  float x;
  float y;
  float sizer;
  Color c;
};

int score = 0;
int lives = 5; // dont go over 5 without adjusting paddle color code
unsigned long last_press, last_level_up;
point paddle, ball, ball_speed;

void setup(){
  jg.initialize();
  start_game();
}

void loop() {
  draw_game();
  delay(5);
}

// reset the ball and updates ball speed and padddle size;
void start_game()
{
  jg.clear_screen();
  paddle.x = 64;
  paddle.y = 150;
  paddle.c = Color(255 - (lives*50), 50, 50);
  paddle.sizer = 40 - min(36, (score/50));
  ball.x = 64;
  ball.y = 64;
  ball.c = Color(random(0,245), random(0,245), random(0,245));
  ball.sizer =  max(2, 8 - score /50);
  long randx = random(60, 500) + score;
  long randy = random(150, 500) + score/3;
  ball_speed.x = (float)randx / 1000.0; 
  ball_speed.y = (float)randy / 300.0; ;
  draw_stats(); 
}

void draw_game(){
  tft.fillCircle(ball.x, ball.y, ball.sizer, jg.background.color_16());
  int dx = map(analogRead(jg.joy_x), 0, 1024, -3, 4);
  if(dx != 0) tft.fillRect(paddle.x-1, paddle.y-1, paddle.sizer+1, 6, jg.background.color_16());
  ball.x += ball_speed.x;
  ball.y += ball_speed.y;
  if((dx > 0 && paddle.x < 128-paddle.sizer) || (dx < 0 && paddle.x > 0)) paddle.x += dx;
  tft.fillCircle(ball.x, ball.y, ball.sizer, ball.c.color_16());
  tft.fillRect(paddle.x, paddle.y, paddle.sizer, 5, paddle.c.color_16());
  
  if(bounce_ball_y()) {
    ball_speed.y *= -1;
    // avoid double bounces by pushing the ball above the paddle when necessary
    tft.fillCircle(ball.x, ball.y, ball.sizer, jg.background.color_16());
    ball.y = min(paddle.y-1, ball.y);
  }
  if(ball.x < ball.sizer || ball.x > 128 - ball.sizer) ball_speed.x *= -1;
  if(ball.y >  paddle.y + 6){
    if(lives <= 0) {
       jg.clear_screen();
       jg.draw_string(3, 10, "Game Over.", Color(150, 20, 50).color_16(), 2);
       jg.draw_string(10, 60, "Death becomes you.", Color(0, 50, 250).color_16(), 1);
       jg.draw_string(10, 74, "Press Joystick", Color(0, 50, 250).color_16());
       jg.draw_string(10, 88, "to Start Over", Color(0, 50, 250).color_16());
       while(digitalRead(jg.joystick_btn_pin) == LOW){
         delay(40); 
       }
       lives = 5;
       score = 0;
       start_game();
       last_level_up = millis();
    }
    else {
      lives--;
      start_game();
      last_level_up = millis();
    }
  }
  if(millis() - last_level_up > 4000){
    score += 50;
    last_level_up = millis();
    ball_speed.x *= 1.1;
    ball_speed.y *= 1.1;
    paddle.sizer = 40 - min(36, (score/50));
    draw_stats();
  } 
}

boolean bounce_ball_y(){
  if (ball.y >= paddle.y && ball.x > paddle.x && ball.x < paddle.x+paddle.sizer) return true;
  if (ball.y < 12 + ball.sizer) return true;
  return false;
}

void draw_stats(){
  tft.fillRect(0, 0, 128, 12, Color(250, 250, 250).color_16());
  jg.draw_string(2, 2, "Score:", Color(0, 50, 250).color_16());
  String s = String(score);
  jg.draw_string(45, 2, s, Color(0, 50, 250).color_16());
  jg.draw_string(78, 2, "Lives:", Color(0, 50, 250).color_16());
  String s2 = String(lives);
  if ( lives <= 1 ) jg.draw_string(115, 2, s2,  Color(255, 10, 10).color_16());
  else if ( lives <= 3 ) jg.draw_string(115, 2, s2, Color(250, 200, 0).color_16());
  else jg.draw_string(115, 2, s2, Color(5, 250, 5).color_16());
}
List All Tags

Open Interactive Tag Tree