littleBit Dashboards (without Cloud Bits)

littleBits is a set of electronic components that magnetically connect together. litteBits is geared towards the kids STEP market and it is available in many schools and libraries.

The littleBits company has done an excellent job making their product easy to use. There is a large variety of different “bit” modules and for Internet applications there is a Cloud Bit ($59).

I found that the Cloud Bit was very easy to get up and running, but I found it was expensive at $59 and somewhat limiting, for example you are only access 1-input and 1-output. So if you want to do 2 inputs/output you would need to purchase a second Cloud bit module.

In this blog I’d like to document how I used a $39 Arduino Bit to do 3-inputs and 3-outputs. I also had the code talk directly to a free Web Dashboard (AdaFruit).

littleBits Arduino Program

A set of commands needs to be setup between the littlebits Arduino module and the PC/Pi. In my Arduino program I referenced the ports A,B,C as inputs (on the left side), and D,E,F as outputs (on the right side).

The commands from the PC/Pi would be : reference_pin:value, for example D:255 would set the D (top left pin) at 100%. It’s important to note that Arduino inputs and outputs are scaled from 0-255.

For inputs the littleBits would send the results as pin: reference_pin:value, for example B:255 would be the result at full scale for the A0 input.

ard_abc

My  test setup had:

  • A fork bit – this meant I only needed 1 power input source
  • A slider bit (0-1) on bit D0 (A)
  • A dimmer bit (0-255) on bit A0 (B)
  • A temperature bit on bit A1 (C)
  • An LED on bit d1 (D)
  • A number bit on D5 (E)
  • a bargraph bit on D9 (F)

lb_ard_setup

Below is the littleBits Arduino program that managed the serial communications.

// littleBits_2_Dashboards - create a serial interface to read/write to a PC/Pi
//
// Command from the littleBits: (A,B,C are the left pins) 
//  A:value <- for example B:24, pin A0 (2nd input) is 24 

// Commands from the PC/Pi: (D,E,F are the right pins)
//  D:output <- for example E:128, set pin A0 to 50% (128/255)
//
String thecmd; 
String thevalue;
String theinput;
char outstring[3];

void setup() {
  //define the littleBits right side pins 1,5 and 9 
  pinMode(1, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(9, OUTPUT);
  // define the littleBits left side inputs
  pinMode(0, INPUT);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  
  Serial.begin(9600); // this needs to match the PC/Pi baud rate
}
void loop() { 
  if (Serial.available() > 0) {
    thecmd = Serial.readStringUntil("\n"); 
    if (thecmd.length() > 2) { // ensure the msg size is big enough
      thevalue = thecmd.substring(2);
      if (thecmd.startsWith("D")) { analogWrite(1,thevalue.toInt()); }
      if (thecmd.startsWith("E")) { analogWrite(5,thevalue.toInt()); }
      if (thecmd.startsWith("F")) { analogWrite(9,thevalue.toInt()); }
    }     
  }
  // Try 3 different inputs: d0 = on/off , A0 = pot, A1 = temp sensor

  sprintf(outstring,"%d",digitalRead(0));
  Serial.write("A:");
  Serial.write(outstring);
  Serial.write("\n");

  sprintf(outstring,"%d",analogRead(A0));
  Serial.write("B:");
  Serial.write(outstring);
  Serial.write("\n");

// A1 is an "i12" littleBits temperature sensor
  int temp = analogRead(A1);
  temp = map(temp,0,1023,0,99); //rescale. Sensor range is 0-99 C or F
  sprintf(outstring,"%d",temp);
  Serial.write("C:");
  Serial.write(outstring);
  Serial.write("\n");
  

  delay(5000);
}

The Arduino IDE “Serial Monitor” can be used to view the output and set values.

msgbox

Python on the PC or Raspberry Pi

The Arduino program will send input data for A,B,C every 5 seconds. This input can be seen in Python by:

#
# littleBits Read Test
#
import serial

ser = serial.Serial(port='/dev/ttyACM1', baudrate=9600) # format for Linux
#ser = serial.Serial(port='COM1', baudrate=9600) # format for Windows

while True:
    inline = ser.readline()
    inline = inline.decode() # make a string
    pin = inline[0:1] # the first character is the pin
    thevalue = inline[2:-1] # the value is between ":" and "\n"<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
    print(pin,thevalue)

The output will look something like:

A  1
B  1023
C  21

To write commands from Python:

Write
#
# littleBits Write Test
#
import serial

ser = serial.Serial(port='/dev/ttyACM2', baudrate=9600) # format for Linux
#ser = serial.Serial(port='COM1', baudrate=9600) # format for Windows

while True:
    print("\nWrite an output value to littleBit")
    out = input("Enter pin:value, pin=A,B,C example: 'E:255' : ")
    out = out.upper() + "\n"
    out2 = out.encode('utf_8')
    ser.write(out2)

Adafruit Dashboards

There are lots of good free dashboards. For this project I used the Adafruit site. To get started you will need to log in and create a free account.

I’ve bought a number of components from Adafruit. I think that they are an excellent company that goes out of their way to create great user guides and products.

To get started with Adafruit Dashboards see: https://github.com/adafruit/Adafruit_IO_Python

The first step is to add some Adafruit tags that the code can read/write to.

Ada_feeds

In the Python code a couple of dictionaries (lb_inputs, lb_outputs)  were created to link the littleBit references (A-F) with the Adafruit tags. Also two dictionaries (lb_inlast, lb_outlast) are used to minimize communications traffic so that only new values were written.

#
# Import standard python modules
import time, random
import serial

# import Adafruit IO REST client
from Adafruit_IO import Client, Feed, RequestError

ADAFRUIT_IO_USERNAME = "put_your_username_here"
ADAFRUIT_IO_KEY = "c039f24ecb6...xxxxx"

aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)

# Create dictionaries of inputs, output, and last values
lb_inputs = {"A":"lb-slide", "B":"lb-dimmer","C": "lb-temp"}
lb_inlast = {"A":0, "B":0,"C": 0}
lb_outputs = {"D":"lb-led", "E":"lb-number", "F":"lb-bar"}
lb_outlast = {"D":0, "E":0,"F": 0}

# Setup the serial port connection
ser = serial.Serial(port='/dev/ttyACM1', baudrate=9600)

while True:
    # Get values from littleBits and write to the dashboard
    inline = ser.readline()
    inline = inline.decode() #inline should look like: A:125\n
    pin = inline[0:1] # pin is the first character in string
    thevalue = inline[2:-1] # value is between ":" and "\n"
    if lb_inlast[pin] != thevalue: # Write only new values
        print(pin,thevalue, lb_inputs[pin])
        ada_item = aio.feeds(lb_inputs[pin])
        aio.send(ada_item.key,thevalue)
        lb_inlast[pin] = thevalue

    thetag = 'lb-slide'
    # Write new dash values to littleBits if they've changed
    for lbtag, dashtag in lb_outputs.items():
        print (lbtag,dashtag)
        thevalue = aio.receive(dashtag).value
        if lb_outlast[lbtag] != thevalue: # Write only new values
            outstr = lbtag + ":" + thevalue + "\n"
            print(outstr)
            ser.write(outstr.encode('utf_8'))
            lb_outlast[lbtag] = thevalue   

    time.sleep(2)

If everything is working correctly then new values should be written to in both directions. On the Adafruit Web site the Feed page should show the new values.

To make things look more presentable Adafruit Dashboards can be used.

ada_dash

Final Comments

In this project I used the Adafruit API, other good platforms would be IFTTT and Node-Red

Animated Bike Light

For this project we wanted to create a portable animated light that we could mount below our bike seat or attach it to a knapsack. The parts that we used were:

parts

We happened to have a Squarewear module from some past work. This is a nice module for wearable projects because it has a built-in rechargeable battery and easy to solder connections. However other small Arduino modules could also be used.

The case that we used we got from a local dollar store (for $1) and it had a hard back and neoprene front pocket.  The four wires that we soldered to the LED Matrix were fairly stiff so we bent them over the lip of the neoprene pocket. The Squarewear module was slipped in behind for easy access to its power switch.

mounting

The Adafruit 8×8 matrix has four connections: SDA, SCL, VCC and GND. In the arduino code you will need to include the libraries:

  • Adafruit_GFX.h
  • Adafruit_LEDBackpack.h

There are a lot of options on what you could display. Our first example used a set of squares that changed sizes.

Our second example used some “old school” animated alien images.

In our final project we used the button on the Squarewear to trigger four different modes:

  • Alien mode
  • Square mode (best for biking)
  • Scroll the temperature
  • Show some text

Have fun.

//****************************************************/
#include <Wire.h>
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
#include <PciManager.h>
#include <PciListenerImp.h>

#define ulong unsigned long
#define uchar unsigned char
#define pinButton 4
#define temp_sensor A1

PciListenerImp listener(pinButton, onPinButtonChange);

uchar buttonPressed = 10; //4 = onboard button
//-----------------------------------------------------------
void onPinButtonChange(byte changeKind) {
  static ulong t = 0;
  if (changeKind == 1 && millis() > t + 250) {
    t = millis();
    buttonPressed = 1;    
  }
}

Adafruit_8x8matrix matrix = Adafruit_8x8matrix();
//-----------------------------------------------------------
void setup() {
  pinMode(pinButton, INPUT);
  digitalWrite(pinButton, HIGH);
  PciManager.registerListener(pinButton, &listener); 
  
  
  matrix.begin(0x70);  // pass in the address
}
//-----------------------------------------------------------
void loop() {
  // Make 3 modes, 1) Temperature, 2) Bike, 3) Pictures
  
   buttonPressed = 0;
  loop_pic();
  
  buttonPressed = 0;
  loop_temp();

  buttonPressed = 0;
  loop_bike();
  
  buttonPressed = 0;
  loop_text();

}
//----------------------------------------
void loop_temp() {
  // Loop thru showing the temperature until the button is pressed
  
  float temp;
  
  matrix.setTextWrap(false); 
  matrix.setTextSize(0.5);
  matrix.setRotation(3);
  while(!buttonPressed) { 
    // convert to celcius
    temp = (float)analogRead(temp_sensor);
    temp = (temp * 3.3 / 1024 - 0.5) / 0.01;

    for (int8_t x=0; x>=-20; x--) {
      matrix.clear();
      matrix.setCursor(x,0);
      matrix.print(int(temp) );
      matrix.print("c");
      matrix.writeDisplay();
      delay(300);
      if(buttonPressed)  break;
    }
  }
}
//-------------------------------------------------------------
void loop_bike() {
  // Loop with a visible biking safe pattern until button pushed
  while(!buttonPressed) {
  
    matrix.clear();
    matrix.fillRect(3,3, 2,2, LED_ON);
    matrix.writeDisplay();  // write the changes we just made to the display
        if(buttonPressed)  break;
    delay(250);
    
    matrix.clear();
    matrix.fillRect(2,2, 4,4, LED_ON);
    matrix.writeDisplay();  // write the changes we just made to the display
    if(buttonPressed)  break;
    delay(250);
    
    matrix.clear();
    matrix.fillRect(1,1, 6,6, LED_ON);
    matrix.writeDisplay();  // write the changes we just made to the display
    if(buttonPressed)  break;
    delay(250);
    
    matrix.clear();
    matrix.fillRect(0,0, 8,8, LED_ON);
    matrix.writeDisplay();  // write the changes we just made to the display
    if(buttonPressed)  break;
    delay(250);
    
  }
} 
// ------------------------------------------------------------
const uint8_t PROGMEM alien[21][8] = {
{
  B00011000, // This is the first frame for alien #1
  B00111100, // If you squint you can kind of see the
  B01111110, // image in the 0's and 1's.
  B11011011,
  B11111111,
  B00100100,
  B01011010,
  B10100101	},

  {
  B00011000, // This is the second frame for alien #1
  B00111100,
  B01111110,
  B11011011,
  B11111111,
  B00100100,
  B01011010,
  B01000010 },

  {
  B00011000, B00111100, B01111110, B11011011, B11111111, B00100100, B01011010, B10100101 },
  {
  B00011000, B00111100, B01111110, B11011011, B11111111, B00100100, B01011010, B01000010 },

  {
  B00000000, // First frame for alien #2
  B00111100,
  B01111110,
  B11011011,
  B11011011,
  B01111110,
  B00100100,
  B11000011 },

  {
  B00111100, // Second frame for alien #2
  B01111110,
  B11011011,
  B11011011,
  B01111110,
  B00100100,
  B00100100,
  B00100100 },

  {
  B00000000, B00111100, B01111110, B11011011, B11011011, B01111110, B00100100, B11000011 },
  {
  B00111100, B01111110, B11011011, B11011011, B01111110, B00100100, B00100100, B00100100 },

  {
  B00100100, // First frame for alien #3
  B00100100,
  B01111110,
  B11011011,
  B11111111,
  B11111111,
  B10100101,
  B00100100},

  {
  B00100100, // Second frame for alien #3
  B10100101,
  B11111111,
  B11011011,
  B11111111,
  B01111110,
  B00100100,
  B01000010 },

  {
  B00100100, B00100100, B01111110, B11011011, B11111111, B11111111, B10100101, B00100100 },
  {
  B00100100, B10100101, B11111111, B11011011, B11111111, B01111110, B00100100, B01000010 },
  
  {
  B00111100, // First frame for alien #4
  B01111110,
  B00110011,
  B01111110,
  B00111100,
  B00000000,
  B00001000,
  B00000000 },

  {
  B00111100, // Second frame for alien #4
  B01111110,
  B10011001,
  B01111110,
  B00111100,
  B00000000,
  B00001000,
  B00001000 },

  {
  B00111100, // Third frame for alien #4 (NOT a repeat of frame 1)
  B01111110,
  B11001100,
  B01111110,
  B00111100,
  B00000000,
  B00000000,
  B00001000 },
 
  {
  B00111100, // Fourth frame for alien #4 (NOT a repeat of frame 2)
  B01111110,
  B01100110,
  B01111110,
  B00111100,
  B00000000,
  B00000000,
  B00000000},

  {
  B00111100, B01111110, B00110011, B01111110, B00111100, B00000000, B00001000, B00000000 },
  {
  B00111100, B01111110, B10011001, B01111110, B00111100, B00000000, B00001000, B00001000 },
  {
  B00111100, B01111110, B11001100, B01111110, B00111100, B00000000, B00000000, B00001000 },
  {
  B00111100, B01111110, B01100110, B01111110, B00111100, B00000000, B00000000, B00000000 },
  
};

 
 
//-------------------------------------------------------------
static const uint8_t PROGMEM
  smile_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10100101,
    B10011001,
    B01000010,
    B00111100 },
    
  wink_bmp[] =
  { B00111100,
    B01000010,
    B10100001,
    B10000001,
    B10100101,
    B10011001,
    B01000010,
    B00111100 },
    
  lsmile_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10100001,
    B10011001,
    B01000010,
    B00111100 },
    
   rsmile_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10000101,
    B10011001,
    B01000010,
    B00111100 };

//----------------------------------------
void loop_pic() {
  
  while(!buttonPressed) {
    matrix.clear();
    matrix.drawBitmap(0, 0, smile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1500);
    if(buttonPressed)  break;
    
    matrix.clear();
    matrix.drawBitmap(0, 0, wink_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(500);
    if(buttonPressed)  break;
    
    matrix.clear();
    matrix.drawBitmap(0, 0, smile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1500);
    if(buttonPressed)  break;
    
    matrix.clear();
    matrix.drawBitmap(0, 0, lsmile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1000);
    
    matrix.clear();
    matrix.drawBitmap(0, 0, rsmile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1000);
    if(buttonPressed)  break;
    
    matrix.clear();
    matrix.drawBitmap(0, 0, smile_bmp, 8, 8, LED_ON);
    matrix.writeDisplay();
    delay(1500);
    
    matrix.setRotation(3);
    for (int i=0; i< 21; i++) {
      matrix.clear();
      matrix.drawBitmap(0, 0, alien[i], 8, 8, LED_ON);
      matrix.writeDisplay();
      delay(250);
      if(buttonPressed)  break;
    }
  }
}
//----------------------------------------
void loop_text() {
  // Loop thru showing the temperature until the button is pressed
   
  matrix.setTextWrap(false); 
  matrix.setTextSize(1);
  matrix.setRotation(3);
  char thetext[] = "Hello";
  while(!buttonPressed) { 
 
    for (int8_t x=0; x>=-40; x--) {
      matrix.clear();
      matrix.setCursor(x,0);
      matrix.print(thetext);
       matrix.writeDisplay();
      delay(300);
      if(buttonPressed)  break;
    }
  }
}