Project tutorial
Aggro-Crag-in-Real-Life-Button and Leader Board

Aggro-Crag-in-Real-Life-Button and Leader Board © MIT

This is a cellular-connected internet button placed in a remote location. Hit the button, enter your initials, and get recognized.

  • 2,152 views
  • 0 comments
  • 8 respects

Components and supplies

Dash msysltxqg7
Hologram Dash
The Hologram Dash v1.1 is the brains and connectivity for this project
×1
Hologram%20dash
Hologram Global IoT SIM Card
M2M / IoT SIM that connects this device directly to the Hologram cloud
×1
Adafruit Massive Arcade Button
×1
Adafruit Bi-Directional Level Shifter
×1
A000066 iso both
Arduino UNO & Genuino UNO
Only used for a 5V output
×1
LCD Display - 16x2
Made by Osepp - works at 5V
×1
Mfr 25frf52 1k sml
Resistor 1k ohm
×1
11026 02
Jumper wires (generic)
×25

Necessary tools and machines

Multimeter
Only needed to help you troubleshoot

Apps and online services

About this project

Have you ever accomplished something on your own that's really cool and you're super excited about, but there's no one right there to share it with? Individually, you can take in the moment mentally and appreciate the serenity or excitement, but we at Hologram wanted to find a way for you to be recognized.

One day we were sitting around recounting fantastic childhood game shows. We talked about how awesome Nickelodeon's Global Guts was and how contestants smash a button after climbing to the top of the Aggro Crag, and tons of confetti would explode out of the side of the mountain.

How awesome would this be in real life?

Well we can't give you a confetti explosion right now (honestly, if you really want us to do this, we will), but we can give you a little recognition for your accomplishment and also an awesome button to smash.

I present to you the Aggro Crag Button in Real Life and Leaderboard hack. This internet connected button and LCD screen lets you punch in your initials for them to be sent to a leaderboard sitting on a web app so you can be recognized by all your friends.

On to the hack...

Overview:

  • The hardware: Dash development kit for cellular connectivity, big green button for a user to smash, LCD screen, 4-way buttons to enter your initial, Hologram SIM for cell service
  • The embedded code: Arduino library to run the LCD screen, custom code to generate the LCD messages and send messages to the cloud
  • The web app: Webhook from the Hologram cloud to a Rails server, a Rails server to display the leaderboard

Step 1: Get to know your hardware

The Dash v1.1 acts like Arduino in code, but uses an NXP Kinetis microcontroller based on an ARM M4 for your user code instead of an Atmel micro so there are definitely some hardware differences. The Dash is compatible with almost all Arduino library functions and even adds to the library with cellular-specific functions of its own such as this function which sends your data to the cloud:

SerialCloud.println();

And also these functions which allow for low power usage for battery-connected cellular devices:

Dash.snooze(milliseconds); 
Dash.sleep(); 
Dash.deepSleep(); 
Dash.shutdown();

So to integrate this with the Osepp 16x2 LCD screen, all I had to do was fire up the Arduino IDE, pull in the standard Arduino library for the LCD screen, change my pin configurations, and it worked!

References for getting the Arduino IDE up and running with the Dash are here: https://hologram.io/docs/guides-tutorials/getting-started/getting-started-with-the-hologram-dash-in-under-10-minutes

Here's an example of changing from Arduino pins to Dash pins:

//Pull in the LCD's Arduino Library
#include <LiquidCrystal.h> 
// select the pins used on the LCD panel 
//LiquidCrystal lcd(8, 9, 4, 5, 6, 7);  //original Arduino LCD library pins 
LiquidCrystal lcd(D17, D18, D11, D12, D13, D14); //Dash pins

The other big difference when going from using an Arduino to a Dash is the hardware voltage. The Dash works at 3.3V and the Arduino works at 5V. Since this Osepp LCD screen is made to be Arduino-compatible and also work at 5V, it's important we use a level shifter to step the voltage from 3.3V to 5V when we use the Dash to drive the LCD screen.

The final major piece of hardware is the BIG GREEN BUTTON:

Since I wanted this to be a visceral experience (you just freaking accomplished climbing a mountain!) we couldn't have people just pressing any small switch. This button just had to be big and well...SMASHABLE. After searching "BIG BUTTON" on Google, Adafruit had this awesome piece of hardware in stock. There's no special consideration to using this button and I used a pull-up resistor and a standard Arduino analogRead() function to get it working on the Dash:

big_button_in = analogRead(A03); 

Now that we understand the hardware, it's easiest to follow the wiring guides to hook everything up.

The LCD screen uses the same pins as the Arduino and the pinout can be found here: http://pighixxx.com/unov3pdf.pdf (warning, PDF!)

To wire the everything together, I connected:

  • Dash | Level Shifter Left Side | Level Shifter Right Side | LCD
  • D17 | A1 | B1 | D8
  • D18 | A2 | B2 | D9
  • D11 | A3 | B3 | D4
  • D12 | A4 | B4 | D5

And then using another level shifter I connected:

  • Dash | Level Shifter Left Side | Level Shifter Right Side | LCD
  • D13 | A1 | B1 | D6
  • D14 | A2 | B2 | D7

Additionally I needed to read the button configuration on the LCD:

  • Dash | LCD
  • A1 | A0

Be sure to provide 3.3V reference voltage to the VCCa side of the level shifter, a 5V reference voltage to the VCCb side of the level shifter, and provide a GND connection.

For the BIG GREEN BUTTON, I used a pull-up resistor (>1k) and read off the A03 pin on the Dash.

And here's where the Dash code can be found (uses the Arduino IDE): https://github.com/wolberine/aggrocragbutton

Step 2: Connect to the cloud

The best part about working on cellular products is the range and ubiquity of signal (as compared to Wifi). Hologram itself is an MVNO. This means if you pick up their SIM and almost any modem, you should have coverage in most populous areas of the US and well... the world! In total each SIM has access to 550 carrier networks.

Since we're working with cellular, the first thing we have to do to connect to the cloud is give the Dash service with the Hologram SIM. On Hologram's home page, there's a link to the registration page for the Dashboard where you can activate your SIM for service and also see any data devices on your account have sent.

They offer a couple of different plans: Pay-as-you-go (where you only pay for what you use) and Monthly (where you buy a set amount of data per month). For prototyping it's probably best to go with a Pay-as-you-go plan until you understand your data costs and then switch to Monthly to save on cost.

Once your SIM is activated via the Dashboard, your LCD and button wired to the Dash, and you've pulled in the LCD library, you should be able to run the code for this project to start sending your initials to the cloud. Read more information about activating Hologram SIMs here.

Here's what you should see as a user who hits the button:

Step 3A: Route your message to the Leaderboard

Yes it's cool that we can now send your initials to a cloud database, but I think we definitely want to display these initials on some sort of public web page. (I'm not a web developer so please bear with me on the UI for this web app!)

To route our data from the Hologram cloud to another endpoint, we use the Hologram webhook feature, which POSTs any incoming data from a device to an endpoint. You can find routing features in Hologram's Dashboard under 'Routes'.

To route your data from the Hologram cloud, you need to associate with your device to the URL you'd like to route to. Also note at the end of my route, I used Rails' format for setting a value to a parameter: yoururl.com/yourobject?yourattribute=<<decdata>>

<<decdata>> is the shorthand form of "the data your Dash sent to the cloud".

Here are some changes you should make for your own device:

  • Change Destination URL to your end point you want to POST to
  • Change /newchampion? to the variable in your database you want to post to
  • Add additional identifiers to the URL so no one else can post to your Leaderboard (official secure endpoint POSTing is still in the works at Hologram)

Here's a link to Hologram's docs on webhooks.

Step 3B: Receive POSTs from the Hologram cloud to your leaderboard

To spin up a server easily, I created a Ruby - Rails workspace using a free account at c9.io. I really like this service since it offers free tiers for prototyping and you don't have to tweak your configuration or set-up when moving to a new machine - the entire workspace (code editing, testing the server) is in the cloud!

Once you spin up your workspace, you can get a really simple server running by creating a scaffold Leaderboard initials:

rails generate scaffold Champion name:string 

This command builds a simple controller, model, and view for the Champion object.

Now just rake the database:

rake db:migrate

We have to also change the app's router to receive POST requests from the Hologram cloud. You can find these rules config>routes.rb. Add this route to the file and save:

post '/newchampion',  to: 'champions#create' 

This line allows a POST request to the app's /newchampion route to create a new database object (NOTE: This is pretty unsecure and should at least have some light security in the form of a passphrase required to POST to the endpoint)

Since we had used a scaffold to generate the model, view, and controller for the Champion, everything else is done for us. Note: for those troubleshooting, you may want to delete the Rails security parameters from your controller to see if you can get your own endpoint up and running

def champion_params 
    ##Consider deleting the line below this to troubleshoot
    params.require(:champion).permit(:name) 
end 

And that's it!

Run your server and test out your device to see that you can send initials to the cloud for your own leaderboard! (Note: for public-facing sites you should safely hide the methods which can directly effect your database)

Here's the link to the rest of the Rails web app: https://preview.c9users.io/wolberine/aggrocrag/

The hacketh endeth here.

-Derrick

PS: Questions? Comments? Ideas on where this project needs to go next?! Hit us up on our website chat at hologram.io. Someone usually responds within minutes unless we're all sleeping.

Code

Dash Sketch on Arduino IDEArduino
This is the main sketch for the Aggro-Crag-in-Real-Life-Button
//Sample using LiquidCrystal library
#include <LiquidCrystal.h>
 
// select the pins used on the LCD panel
//LiquidCrystal lcd(8, 9, 4, 5, 6, 7);  //original library pins
LiquidCrystal lcd(D17, D18, D11, D12, D13, D14);


//set up number of sends to cloud
int numSends = 0; // count number of sends

//set up the big green button
int big_button_in = 0;

//network connection boot sequence variables
int bootSequence = 0;
bool cloudReady = false;
char currChar;
String tempBuffer;
int plusSigns = 0;

 
// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5
 
// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(A0);      // read the value from the sensor
 // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
 // we add approx 50 to those values and check to see if we are close
 if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
 if (adc_key_in < 60)   return btnRIGHT; 
 if (adc_key_in < 200)  return btnUP;
 if (adc_key_in < 450)  return btnDOWN;
 if (adc_key_in < 640)  return btnLEFT;
 if (adc_key_in < 970)  return btnSELECT;  
 return btnNONE;  // when all others fail, return this...
}
 
//define alphabet array
char alphaArray[26] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
int currentAlphaPosition = 0;

int currentCursorPosition = 0;
char initials[3] = {'A','A','A'};

int cursorTracker[3] = {0,0,0};
int sentToCloudFlag = 0;

int nextAlphaPosition(int current, int lcd_key){
    int next = current;

    if(lcd_key == btnUP){
        next = current + 1;
    }
    else if(lcd_key == btnDOWN){
        next = current - 1;
    }
    
    if (next == 26){
        next = 0;
    }

    if (next == -1){
        next = 25;
    }

    return next;
    
}

int nextCursorPosition(int current, int lcd_key){
    int next = current;

    if(lcd_key == btnRIGHT){
        next = current + 1;
    }
    else if(lcd_key == btnLEFT){
        next = current - 1;
    }
    
    if (next == 3){
        next = 0;
    }

    if (next == -1){
        next = 2;
    }

    return next;
    
}

//debounce setup
// Variables will change:
int buttonState;             // the current reading from the input button
int lastButtonState = btnNONE;   // the previous reading from the input pin

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers


 //lcd state machine flags
 int scene1 = 0;
 int scene2 = 0;
 int scene3 = 0;
 int scene4 = 0;
 int scene5 = 0;

void setup()
{
 lcd.begin(16, 2);              // start the library
 lcd.setCursor(0,0);
 lcd.print("Booting..."); // print a simple message

 Serial.begin(9600);
 SerialCloud.begin(115200); 
}
  
void loop()
{

 //lcd.setCursor(9,1);            // move cursor to second line "1" and 9 spaces over
 //lcd.print(millis()/1000);      // display seconds elapsed since power-up

 //connection boot 
  if(bootSequence == 0){
    char currChar;
      
    while (SerialCloud.available()) {
          currChar = (char)SerialCloud.read();
          
          if (currChar == '+') {
              plusSigns++;
          } else {
              plusSigns = 0;
          }
  
          if (plusSigns == 3) {
              cloudReady = true;
              scene1=1;
              bootSequence=1;
              lcd.clear();
          }
          
          SerialUSB.write(currChar);
          Serial2.write(currChar);
      }
  }

 if (scene1 == 1){
    delay(1000);
    Serial.println("Entering Scene 1");
     lcd.setCursor(0,0);
     lcd.print("Hold down BUTTON");

     lcd.setCursor(0,1);
     lcd.print("to begin");

     big_button_in = analogRead(A03);
     
     if (big_button_in >  500){
      scene1 = 0;
      scene2 = 1;
      lcd.clear();
     }
 }

 if (scene2 == 1){
     lcd.setCursor(0,0);
     lcd.print("Congrats! Press");

     lcd.setCursor(0,1);
     lcd.print("SELECT");

     //just recopy a bunch of code because refactoring is hard...
    lcd_key = read_LCD_buttons();  // read the buttons


    // If the switch changed, due to noise or pressing:
    if (lcd_key != lastButtonState) {
        // reset the debouncing timer
        lastDebounceTime = millis();
    }

    if ((millis() - lastDebounceTime) > debounceDelay) {

        // if the button state has changed:
        if (lcd_key != buttonState) {
        buttonState = lcd_key;
        if (lcd_key == btnSELECT){
            scene2 = 0;
            scene3 = 1;
            lcd.clear();
            lcd.setCursor(0,0);
            Serial.println("Select pressed");
            }
        }
    } 

    // it'll be the lastButtonState:
    lastButtonState = lcd_key;
 }

if (scene3 == 1){
     lcd.setCursor(0,1);            // move to the begining of the second line
     lcd_key = read_LCD_buttons();  // read the buttons

     lcd.setCursor(0,0);
     lcd.print("Enter initials");


    // If the switch changed, due to noise or pressing:
    if (lcd_key != lastButtonState) {
        // reset the debouncing timer
        lastDebounceTime = millis();
    }

    if ((millis() - lastDebounceTime) > debounceDelay) {


            // if the button state has changed:
            if (lcd_key != buttonState) {

            //Serial.println("button state has changed");
            buttonState = lcd_key;


            currentCursorPosition = nextCursorPosition(currentCursorPosition, lcd_key);
            currentAlphaPosition = nextAlphaPosition(cursorTracker[currentCursorPosition], lcd_key);
            cursorTracker[currentCursorPosition] = currentAlphaPosition;

            initials[currentCursorPosition] = alphaArray[currentAlphaPosition];
            Serial.print(initials[0]);
            Serial.print(initials[1]);
            Serial.println(initials[2]);

            if (lcd_key == btnRIGHT){
                Serial.print("Right");
                Serial.println(currentCursorPosition);
                }
            else if (lcd_key == btnUP){
                Serial.print("Up");
                Serial.println(alphaArray[currentAlphaPosition]);
                }
            else if (lcd_key == btnDOWN){
                Serial.print("Down");
                Serial.println(alphaArray[currentAlphaPosition]);
                }
            else if (lcd_key == btnLEFT){
                Serial.print("Left");
                Serial.println(currentCursorPosition);
                }
            else if (lcd_key == btnSELECT){
                Serial.print("Select");
                scene3 = 0;
                scene4 = 1;
                }
            }


    } 



    // it'll be the lastButtonState:
    lastButtonState = lcd_key;

    lcd.setCursor(0,1);
    lcd.print(initials[0]);
    lcd.setCursor(1,1);
    lcd.print(initials[1]);
    lcd.setCursor(2,1);
    lcd.println(initials[2]);
}


 if (scene4 == 1){
     lcd.setCursor(0,0);
     lcd.print("Sending. Go to");

     lcd.setCursor(0,1);
     lcd.print("hologram.io");
     String cloudInitials = initials;

      // Try to send data 3 times to cloud
      if(numSends < 3) {
        SerialUSB.println("Sending a message to the Cloud...");
        SerialCloud.println(cloudInitials);
        SerialUSB.println(cloudInitials);
        SerialUSB.println("Message sent!");
        numSends++; // increase the number-of-sends counter
      }

      if(numSends >= 3){
         scene4 = 0;
         scene5 = 1;
      }

    // it'll be the lastButtonState:
    lastButtonState = lcd_key;
 }

   if (scene5 == 1){
     lcd.clear();
     lcd.setCursor(0,0);
     lcd.print("Sending. Go to");

     lcd.setCursor(0,1);
     lcd.print("hologram.io");

     delay(7500);

     lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Resetting...");

    lcd.setCursor(0,1);
    lcd.print("Goodbye!");
    delay(5000);
    lcd.clear();
    scene5 = 0;
    scene1 = 1;

    //Reset variables
    currentAlphaPosition = 0;
    
    currentCursorPosition = 0;
    initials[0]='A';
    initials[1]='A';
    initials[2]='A';
    
    cursorTracker[0]=0;
    cursorTracker[1]=0;
    cursorTracker[2]=0;
    sentToCloudFlag = 0;
    numSends = 0;

   }

 
}

Comments

Add projectSign up / Login