Project development

Reminder About my Project

Goal :


Be connected to the wall using bluetooth, then select the lane by level, choose a color and lane will be light up with the chosen color.

Technology

At the beginning the idea was to integrated a Tiny44 with a simple RGB led, the tiny44 will use I2C network and will have a fixed address, then with an ATmega has Master I will address each Hold and light it up.
The I Decided to Mold one hold with an RGB led, after some tests it appears that I will need more LED by hold, which became nor realistic in term of number of pin and Microcontroller.
Then I discoverd the WS2811, WS2812B technology used in most of the RGB led strip of the market.
More information in "Output Devices" .
We ordered some chipset to test the technology but regarding the cost It was less expensive to buy a led-strip of 60 Leds, so I ordered one but took a long time to come from china. The technology needs only 4 cables by hold no matter the number of leds inside. (VCC,GND,IN,OUT)
Leds are addressable and cascadable.

Molding and Casting

After test it appears that having a 'natural' shape with a kind of grip is really hard the obtain using computer aided design and machining.
So I decide to produce the shape and the mold manually. I used :
- dense polystyrene for the shape using cutter, sandpaper , pumice to create the grip by pressing it on the shape .
- Silicon for the mold.
- Crystal resin for the casting.

holds creation
Using the Laser cut, I created the support for the 4 Leds which is put inside the mold when casting.

JMDuino Board


JMDuino board with HC06
the main board of the project, you can fine all the details in Section "Output Devices"

Program



#include "FastLED.h"


#define NUM_LEDS 136    /*4 in the 1st breakout */

// For led chips like Neopixels, which have a data line, ground, and power, you just
// need to define DATA_PIN.  
#define DATA_PIN 5
#define NUM_HOLDS 109
// Define the array of leds
CRGB leds[NUM_LEDS];
uint32_t MyColor;

static const boolean Lane[10][NUM_HOLDS] = {
{0,0,1,0,1,0,0,1,1, 0,0,0,0,0,0,0,0,1,0, 0,0,1,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,0, 0,1,0,1,0,0,0,0,0,0, 0,0,0,0,0,1,0,1,0,0, 0,1,0,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,1,0, 1,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,1,0, 0,1,0,0,0,0,0,0,0,0},
{0,1,1,0,0,1,0,1,0, 0,0,0,0,1,1,0,0,0,0, 0,0,0,1,0,1,0,0,0,0, 0,0,0,0,1,1,0,0,0,0, 0,0,0,0,1,0,0,0,0,0, 0,0,1,0,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,0, 0,0,1,0,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,0, 0,0,0,1,1,0,0,0,0,0, 0,0,0,0,0,1,0,0,0,0},
{0,1,1,0,1,1,1,0,0, 0,0,1,1,0,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,0, 0,1,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,0, 0,0,1,1,0,0,0,0,0,0, 0,0,0,0,0,1,1,0,0,0, 0,0,0,0,1,1,0,0,0,0, 0,0,0,1,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,0, 0,0,0,1,0,0,0,0,0,0},
{1,0,0,1,0,1,1,0,0, 0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,1, 0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,1, 0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,1, 0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,1,0},
{0,0,1,0,1,0,0,0,1, 0,0,0,0,0,0,0,1,0,1, 1,0,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0, 1,0,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,1,0, 1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,1, 0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0, 1,0,0,0,0,0,0,0,0,0},
{1,0,0,1,0,1,1,0,0, 1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,1,0, 0,0,0,1,0,0,0,0,0,0, 0,0,0,0,1,1,0,0,0,0, 0,0,0,0,0,1,1,0,0,0, 0,0,1,0,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,0, 0,0,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,0, 0,0,1,0,0,0,0,0,0,0},
{0,0,1,0,1,0,0,1,0, 0,0,0,0,0,0,1,0,0,0, 0,1,0,0,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,1, 0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1, 0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,1, 1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,1, 0,1,0,0,0,0,0,0,0,0},
{0,0,1,0,1,0,0,1,1, 0,0,0,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,0, 0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,1,0, 1,0,0,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,0,0, 1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
{0,1,0,1,0,1,1,1,0, 0,0,0,0,1,1,0,0,0,0, 0,0,0,0,1,1,0,0,0,0, 0,0,0,1,1,0,0,0,0,0, 0,0,0,0,0,0,1,1,0,0, 0,0,0,0,1,0,0,0,0,0, 0,0,0,0,1,1,0,0,0,0, 0,0,0,1,0,1,0,0,0,0, 0,0,0,0,0,1,0,0,0,0, 0,0,0,0,0,1,0,0,0,0, 0,0,0,0,1,1,0,0,0,0},
{1,1,1,1,1,1,1,1,1, 0,1,1,1,0,1,1,1,0,0, 0,0,0,1,0,0,0,1,0,0, 0,1,1,0,0,1,1,0,0,0, 0,0,0,1,0,0,0,1,0,0, 0,0,0,0,0,0,0,0,0,0, 0,1,1,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,1,0,1, 0,1,1,0,1,1,1,0,1,1, 1,0,0,1,0,1,0,1,0,1, 0,1,1,0,0,1,0,1,1,1}};

/*{0,1,0,1,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0}*/

const byte NbLedByHolds[NUM_HOLDS] = {4,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};

int ActiveLane[NUM_LEDS];
int CompteurAddressLED;
int MyLane;
char CharRead;
char FlushExtraChar;
int compteurTempo;
int NbLedInThisHold;
int StatusLed;
int Error;      /*used for loop*/
byte RG_Exchangetampon;
void setup() 
{
        Serial.begin(9600);                                  /*init Serial*/
	FastLED.addLeds(leds,NUM_LEDS);  /*init LedStrip*/

        Error = -1;
}

void loop() 
{
 
        while (Error == -1)         /* while read value not correct ....*/ 
        {
        CharRead ='?'; 
        Serial.println("Level 4b     : 0 ");
        Serial.println("Level 5a     : 1 ");
        Serial.println("Level 5b     : 2 ");
        Serial.println("Level 5b     : 3 ");
        Serial.println("Level 5c     : 4 ");
        Serial.println("Level 5c     : 5 ");
        Serial.println("Level 5c     : 6 ");
        Serial.println("Level 6a     : 7 ");
        Serial.println("Level 6a     : 8 ");
        Serial.println("Level 6b     : 9 ");
        Serial.print("Which Lane     [0..9]? :");
        while (CharRead == '?'){ 
        if (Serial.available() > 0) {                                /*char in buffer line ? */
                    CharRead = Serial.read();                        /*read the 1st one */     
                    while (Serial.available () > 0) Serial.read();   /*remove the char pending in stack*/
    
                                         
                     
                                           /* activate the change */
                                     }
        
                                }
        Serial.println(CharRead);          /* send back the typed char for view */
       

        switch (CharRead){                            /* validate the value */
          case '0' : MyLane = 0;Error = 0;break;
          case '1' : MyLane = 1;Error = 0;break;
          case '2' : MyLane = 2;Error = 0;break;
          case '3' : MyLane = 3;Error = 0;break;
          case '4' : MyLane = 4;Error = 0;break;
          case '5' : MyLane = 5;Error = 0;break;
          case '6' : MyLane = 6;Error = 0;break;
          case '7' : MyLane = 7;Error = 0;break;
          case '8' : MyLane = 8;Error = 0;break;
          case '9' : MyLane = 9;Error = 0;break;
          default : Error = -1;Serial.println("not in range");
                           }
        }
        
        CompteurAddressLED=0;                    /*convert table of holds into table of leds */     
        for (int i = 0; i < NUM_HOLDS; i++)
        { 
       
         StatusLed = Lane[MyLane][i];
         NbLedInThisHold = NbLedByHolds[i];
          
         
                for(int j=0 ; j < NbLedInThisHold ; j++ )
                {
                ActiveLane[CompteurAddressLED] = StatusLed;
                CompteurAddressLED++;
                } 
                
        }
        
        
        
        
        Error = -1;
        
        while (Error == -1)
        {
        Serial.println("Red      : 0 ");
        Serial.println("DarkRed  : 1 ");
        Serial.println("Orange   : 2 ");
        Serial.println("Yellow   : 3 ");
        Serial.println("Green    : 4 ");
        Serial.println("Purple   : 5 ");
        Serial.println("Blue     : 6 ");
        Serial.println("Cyan     : 7 ");
        Serial.println("Gray     : 8 ");
        Serial.println("White    : 9 ");
        Serial.print("Which Color  [0..9]? :");
        
        CharRead ='?';  
        while (CharRead == '?'){ 
        if (Serial.available() > 0) {
                                      CharRead = Serial.read();           /* read the 1st one*/
                                      while (Serial.available () > 0) Serial.read();   /*remove the char pending in stack*/ 
                                    }
        }
        Serial.println(CharRead);
        switch (CharRead){                                        /* validate the data and set the color */
          case '0': MyColor = CRGB::DarkRed; Error = 0;break;
          case '1': MyColor = CRGB::Red;   Error = 0;break;
          case '2': MyColor = CRGB::Orange;Error = 0;break;
          case '3': MyColor = CRGB::Yellow;Error = 0;break;
          case '4': MyColor = CRGB::Green; Error = 0;break;
          case '5': MyColor = CRGB::Purple;Error = 0;break;
          case '6': MyColor = CRGB::Blue;  Error = 0;break;
          case '7': MyColor = CRGB::Cyan;  Error = 0;break;
          case '8': MyColor = CRGB::Gray;  Error = 0;break;
          case '9': MyColor = CRGB::White; Error = 0;break;
          
          
         default : {Error = -1;Serial.println("not in range");}
                        }
        }                
        Error =-1;
       
       
       for (int i=0; i < NUM_LEDS; i++)
         {
          if ( ActiveLane[i] == 0 ) { leds[i] = CRGB::Black; }
                
          else     { if (i<36) { leds[i] = MyColor;    }
                   else { leds[i] = MyColor;
                          RG_Exchangetampon = leds[i].r;
                          leds[i].r = leds[i].g;
                          leds[i].g = RG_Exchangetampon;
                         }
                   
                   
                   
                   
                 
                   }
         }
       
       FastLED.show();                          /* activate the change */
 
}

                      

you can find the design files for JMDuino on my github repository :github


In paralelle I tested different methods to detect touch or proximity. Both Piezo and IR detection were working fine outside the hold but were inefficient inside.In addition An I2C network for the detection require too many componants by hold and the price will grow to fast.

Captor test

Cost and implications

Neil says that cost should be about 10$ to 100 Max, With the resin and leds I am already over the limit with 9 holds, So I decided to fund them and in order to do not build a full Climbing Wall, I will use another ledstrip to simulate the rest of the wall. I bought 100 leds strip, create a box using the shopbot and the laser cut to finally have a 10by10 matrix of leds over the 9 holds already done.

LedStripBox


you can find the rest of the project design files on my github repository :github

Final Video:


ClimbingWall from JM Durney on Vimeo.




What have I learned:


-atmel 328P micro controler.
-Led Strip Technology :WS2811,WS2812B.
-Mold and Casting.
-Shopbot usage. : I hope I will take the preFAB11 Shopbot course to increase my knowledge.
-Arduino IDE env for dev.


I have also learned to be patient,be optismit, to enjoy even during critical phase.

the Next Steps for the project:


- Leds in the screw not in the hold .
- Find a working way of detection.
- Increase my knowledge in C, Assembler, Python.
- Complete rebuild of program and interface.
- Optimize memory management.
- Add a SD card reader on JMduino for storage of lane.
- Create software interface to record lane.
- Create game for Climbing wall ..... It was just a beginning !!!!