Monday, April 20, 2015

Using an Arduino to protect any electric circuit with a pin code

This tutorial will show you how to protect a circuit with a pin using a 4x4 membrane keypad for entering the pin, a LCD to communicate with the user, a relay to control the circuit and an arduino to run the program for all these components.
The program will ask for a pin when you want to enable the circuit and if entered correctly close the circuit via a relay, if false it will leave the relay open disabling the circuit.






Parts List

  • Arduino Mini Pro (5V 16Mhz but anyother Arduino will work as well)
  • USB-FTTL Breakout board to program the arduino with (only necessary when there is no onboard USB plug)
  • 4x4 Membrane keypad
  • 20x4 LCD
  • 1k Ohm resistor
  • 4x 10k Ohm resistor
  • 0,1nF Capacitor
  • 10k Ohm potentiometer
  • Relay board for switching highcurrent circuit (for this tutorial I'll be using a ready made board Keyes_SR1y)
  • 5V power supply (for this tutorial I'm using a 240V AC to 5V DC 420mA smartphone charger which I disassembled and connected directly to the power plug for the controlled circuit)
  • Jumper wires

For building the Hardware box (not necessary to complete this tutorial)

  • Generic Black Casing 
  • A plexi glas plate to cover the hole for the LCD
  • Power cable that drives the hardware to protect with the circuit locker
  • Prototype PCB board to solder everything onto

For Simulating the high current circuit

  • an LED (for this tutorial I'm using a big green LED)
  • 330 Ohm Resistor

Setting up the Circuit


Connecting the LCD

For easier connection I have soldered a 16 Pin header to the LCD. Connect the pins to the arduino according to the following list:
  • VSS - connect to GND
  • VDD - This supplies power to the LCD. Connect to the 5V VCC. Parallel we connect the 0,1nF capacitor to VCC and GND to smooth out any electric peaks when switching on the power.
  • V0 - this controls the contrast of the LCD. Connect it to the middle pin of the potentionmeter. The other two pins of the potentiometer are connected to 5V VCC and GND respectively.
  • RS - The register shift signal. It is used to signal the LCD to shift the written date to the display. Connect it to digital pin 2. 
  • RW - The read/write signal. We are only writing to the LCD and do not need to read from it so we simply set this pin to GND. (Signal Low)
  • E - this is the enable signal of the LCD. Connect it to digital pin 3.
  • D0-D3 - since we are only going to use 4bit character mode we will not need these pins.
  • D4-D7 - these connection transfer the data to the LCD. Connect these to digital pin 4-7.
  • A - this is the anode of the backlight LED. Connect this to the one end of the 1k Ohm resistor. Connect the other end of the resistor to the analog pin A2 which we will use as a digital output pin to control the backlight of the LCD via the Arduino.
  • K - this is the kathode of the backlight LED. Connect it to GND
Once you have setup the LCD you can test that everything is working by running the "Test the LCD" sketch from the sketches section.

Connecting the Keypad

Connect the keypads 8 connections to digital pins 8 to 13 and pins A0 and A1. Although A0 and A1 are documented to be analog pins we can still use them as digital pins. Connect one 10k Ohm resistor between each of the four pins 8-11 and GND. These resistor will function as pull down resistor for the four pins which we will be using as digital inputs.
A pull down resistor ensures that the signal on the pin gets pulled to 0V (LOW) even though there might be small currents induced by environment interference which can potentially shift the pins to HIGH. The pull down resistor uses the fact that electricity always flows along the path of least resistance. Since a pin that has no signal applied to it has a very high resistance the current flows through the pull down resistor to ground ensuring a level of 0V. When the pin is read its resistance is lower then that of the resistor and the current flows through the pin.
Once you have everything setup you can use the "Test the Keypad" sketch to test whether everything is working.

Connecting the Relay

Connect the "+" pin from the relay board to the 5V from the power supply and "-" to GND. The signal pin "S" is connected to pin A3. Although A3 is an analog pin it can still be used as a digital one. Instead of connecting an acutal high current circuit to the relay we will instead connect an LED to make the opening and closing visible.
The relay has two connection types NC (Normally Closed) and NO (Normally Open). To understand their meaning it is important to know that a relay is an electromagnetic switch which opens or closes depending on whether a signal is applied to it or not.

Take a look at the simplified schematic on the left. The relay consists of a spindle and a switch which gets triggered by the magnetic field induced by the spindle.
The circuit for the spindle is interrupted by a transistor. The transistor becomes conductive when a signal is applied to S. When the spindle becomes conductive the current flowing through it induces a magnetic field which flips the switch on the right side closing the circuit between NO and GND and at the same time opening the circuit between NC and GND.
This explains the two operation modes of the relay:
NC (Normally Closed) - The relay is closed when there is no signal on S. When the signal turns HIGH the relay opens the switch and the circuit is interrupted.
NO (Normally Open) - The relay is open when there is no signal on S. When a signal is applied to S the relay closes the circuit and current can flow.

Since I want to control when the circuit is closed I'm going to use NO mode. Connect the cathode (the shorter leg, the side that is flattened) of the LED to the NO connection of the relay and connect the GND of the relay to GND. Connect the anode of the LED  to the one end of the 330 Ohm resistor. The other end of the resistor you connect to 5V VCC. We have now a circuit which is open since the relay has not been switched on yet and we are in NO mode.

Do not use the above simplified schematic to setup your own relay circuit. If you do not protect against backflowing currents caused by the inductive nature of the spindle it might destroy your Arduino.  

To test that we have done our setup correctly run the "Test the Relay" sketch. If everything is correct the relay should switch every 5 seconds turning the LED on and off.

Arduino Sketches

Test the LCD

This sketch is intended to test whether the LCD is correctly connected to the Arduino. We will be using the LiquidCrystal library provided by the Arduino IDE. The libary will take care of the low level bits and bytes to send characters to the LCD.
The sketch sets up the necessary pins for the LCD and creates the LiquidCrystal object passing the pins in the constructor. It then prints out the message "Setup and Running" and prints the time since when the sketch started running. To calculate the time it uses the millies() seconds which returns the milliseconds since the sketch started running. Bare in mind since the time is given in milliseconds  and it is saved to an unsigned long it will eventually overflow and restart by zero. According to the documentation this will occur around the fifties day.

Create a new sketch, name it TestLCD and copy the following code to it.

/**
 * Copyright (C) 2015  Stefan Langer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * This program uses parts of the arduino core libraries published by Arduino LLC
 * which are released under the LGPL v2.1 or later.
 * http://www.gnu.org/licenses/lgpl
 *
 * All hardware, documentation and files distributed with this software that do
 * not fall under the GPL are distributed under the
 * Creative Commons Attribution-ShareAlike 4.0 International License
 * http://creativecommons.org/licenses/by-sa/4.0
 */
#include <LiquidCrystal.h>

// define the pins to use for the LCD 
#define RS_PIN        2
#define EN_PIN        3
#define D0_PIN        4
#define D1_PIN        5
#define D2_PIN        6
#define D3_PIN        7
#define LCD_BACKLED  16 // Analog pin A0

// CONSTANTS for managing time
#define SECOND    1000 // millis in one second
#define MINUTE   60000 // millis in one minute
#define HOUR   3600000 // millis in one hour
#define DAY   86400000 // millis in one day

// alias for datatpyes
#define ulong unsigned long

// initialize LCD with predefined pins
LiquidCrystal lcd(RS_PIN, EN_PIN, D0_PIN, D1_PIN, D2_PIN, D3_PIN);

// helper function to print time on the lcd.
void printActiveTime() {
  ulong time = millis();
  ulong days = time / DAY;
  ulong hours = (time / HOUR) % 24;
  ulong minutes = (time / MINUTE) % 60;
  ulong seconds = (time / SECOND) % 60;

  int dIdx = 1;
  if (days >= 10)
    dIdx = 0;
  lcd.setCursor(2 + dIdx, 2);
  lcd.print(days);
  lcd.print('d');
  if (hours < 10)
    lcd.print('0');
  lcd.print(hours);
  lcd.print('h');
  if (minutes < 10)
    lcd.print('0');
  lcd.print(minutes);
  lcd.print('m');
  if (seconds < 10)
    lcd.print('0');
  lcd.print(seconds);
  lcd.print('s');
}

// Setup backlight led pin and start LCD
// print status to LCD once we are done
void setup() {
  pinMode(LCD_BACKLED, OUTPUT);

  digitalWrite(LCD_BACKLED, HIGH);

  lcd.begin(20, 4);

  lcd.print("Setup and Running!");
  lcd.setCursor(0, 1);
  lcd.print("Running since:");
}

// print current time to the lcd
void loop() {
  printActiveTime();
  delay(200);
}

TestLCD sketch on GitHub

Output running the TestLCD sketch


Test the Keypad

This sketch provides the means to read a keypress from the keypad and tests that the setup of the keypad is correct.
The keypad is a simple grid of wires for rows and columns where each keypad lies on an intersection which is a button that closes the circuit when pressed.  
The columns are configured as input pins while the rows are configured as output pins. To find out which key was pressed we simply set each row pin to HIGH and loop through the column inputs to see which column gets a HIGH input signal. From the row and the column we can then determine the key that was pressed and return it.

Create a new sketch, name it TestKeypad and copy the following code to it.

/**
 * Copyright (C) 2015  Stefan Langer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses
 *
 * This program uses parts of the arduino core libraries published by Arduino LLC
 * which are released under the LGPL v2.1 or later.
 * http://www.gnu.org/licenses/lgpl
 *
 * All hardware, documentation and files distributed with this software that do
 * not fall under the GPL are distributed under the
 * Creative Commons Attribution-ShareAlike 4.0 International License
 * http://creativecommons.org/licenses/by-sa/4.0
 */
// define the pins used for the rows
#define ROW1 15
#define ROW2 14
#define ROW3 13
#define ROW4 12
// define the pins used to read the state of the columns
#define COL1 11
#define COL2 10
#define COL3 9
#define COL4 8

#define uint unsigned int

// helper constructs to easily manage the rows and columns
const uint ROWS[] = {ROW1, ROW2, ROW3, ROW4};
const uint COLS[]  = {COL1, COL2, COL3, COL4};
// matrix representation of the keypad
// the coordinate of each key is (row, column)
const char LETTERS[][4] = {{'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

/**
 * Returns the currently pressed key.
 *
 * @return the pressed key or -1 if 
 *         no key press was registered.
 */
char getPressedKey() {
  for (int ri = 0; ri < 4; ri++) {
    digitalWrite(ROWS[ri], HIGH);
    for (int c = 0; c < 4; c++) {
      if (HIGH == digitalRead(COLS[c])) {
        return LETTERS[ri][c];
      }
    }
    digitalWrite(ROWS[ri], LOW);
  }
  return -1;
}


void setup() {
  // setup row pins for output and col pins for input 
  // and set each one to LOW.
  for (int i = 0; i < 4; i++) {
    pinMode(ROWS[i], OUTPUT);
    pinMode(COLS[i], INPUT);
    digitalWrite(ROWS[i], LOW);
    digitalWrite(COLS[i], LOW);
  }
  // start serial montior for debugging purposes
  Serial.begin(9600);
  Serial.println("Initialized and ready to go!");
}

void loop() {
  // read the current key press and if
  // available print it to the serial
  // monitor
  char key = getPressedKey();
  if (key != -1) {
    Serial.print("Got key press: ");
    Serial.println(key);
  }
  // prevent double clicks
  delay(200);
}
TestKeypad sketch on GitHub

Video running the TestKeypad sketch










Test the Relay

This sketch tests the setup of the relay. When everything works the sketch will toggle the relay every 5 seconds turning the LED on or off depending whether signal is HIGH or LOW.
It does so by configuring the relay pin for output and then simply sets it to HIGH to close the relay and to LOW to open it. When the relay is closed the circuit with the LED is closed and the LED lights up. When the relay is open the LED circuit is interrupted and it turns off. If you listen closley you can hear a click sound coming from the relay when it flips the switch.

Create a new sketch, name it TestRelay and copy the following code to it.

/**
 * Copyright (C) 2015  Stefan Langer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses
 *
 * This program uses parts of the arduino core libraries published by Arduino LLC
 * which are released under the LGPL v2.1 or later.
 * http://www.gnu.org/licenses/lgpl
 *
 * All hardware, documentation and files distributed with this software that do
 * not fall under the GPL are distributed under the
 * Creative Commons Attribution-ShareAlike 4.0 International License
 * http://creativecommons.org/licenses/by-sa/4.0
 */
 
// define the relais pin
#define RELAIS_PIN   17

void setup() {
  pinMode(RELAIS_PIN, OUTPUT);
  digitalWrite(RELAIS_PIN, LOW);
}

// turn relais pin HIGH wait 5 seconds then turn it LOW 
// and wait another 5 seconds
void loop() {
  digitalWrite(RELAIS_PIN, HIGH);
  delay(5000);
  digitalWrite(RELAIS_PIN, LOW);
  delay(5000);
}
TestRelay sketch on GitHub

Video running the TestRelay sketch








Saving the PIN to EEPROM

The circuit locker app uses a changeable pin to prevent just anybody from triggering the relay. For this purpose the previously set secret is kept in memory and is compared to the entered pin. Whenever they match the circuit is unlocked. 
The problem is that if somebody cuts the electricity from the Arduino the secret in memory is lost and anybody could unlock the circuit. Fortunately the used Arduino Mini Pro has 512 bytes of EEPROM (electrically erasable programmable read-only memory). Data written to EEPROM will be persisted accross restarts. The draw back of EEPROM is that there isn't a lot of it and it has a limited number of write cycles which makes it a bad choice for frequently updated data. You should probably be using an external memory card for such data.

Side Note

Assuming that we set the pin each day once. This would mean we could write 100.000 times to the same address location in EEPROM until it fails.
Doing the math:
100.000 / 365 = 273,973 (rounded up)
this gives us 273 years until EEPROM fails. Since we are not expecting the pin to change that often we are more then fine to use EEPROM for storing the data.
For the Arduino Mini Pro the specs talk about a write cycle of about 100000. Considering that the pin will only be saved a couple of times this should give us plenty of reserve for the future.
In order to not burn through those precious cycles I recommend to do the actual writing in a separate function and simply implement a dummy code that simply puts debug output to the serial port during developement. Once you are confident that your code is doing what it is supposed to do I would replace this with the actual code writing to EEPROM reducing the needed cycles during development to a minimum. This can be fairly easy automated with #define and #ifdef, #else, #endif


#define DEBUG 
#ifdef DEBUG
Serial.println("Put code to execute during development here!");
#else
// Actual code to write to EEPROM
EEPROM.write(0, save_struct);
#endif

I will use the EEPROM library delivered with the Arduino IDE to write and read to and from this memory. The code to write to the EEPROM is very simply since I'm just going to write each character as a single byte to memory. 
To reduce the needed write cycles I'm going to use the update(int address, byte value) method for writing. This method only writes to EEPROM if the byte differs from the already written one. Untouched EEPROM addresses (those not containing a value) have the value 255. Since I do not have any pin value that is 255 I'm going to use this as the marker for the end of my pin. 
So the writing routine reduces itself to going through the password character by character and writing each character to EEPROM using update. I can use the index of the character in the pin string as the address into the EEPROM. (Addressing starts at 0.) You should also make sure that you are not writing to a higher address then the max size of your EEPROM.
This results in the following code:

/**
 * Writes the specified password to EEPROM.
 *
 * @param pwd Password to store in EEPROM
 */
void writeToEEPROM(String pwd) {
  // make sure you are not writing passwords longer
  // then 511 characters.
  if(pwd.length() >= 512) 
     return;
  // Debug output
  Serial.print("Writing ");
  Serial.print(pwd);
  Serial.println(" to EEPROM!");
  for(int i=0;i<pwd.length();i++) {
    EEPROM.update(i,pwd[i]);
  }
  EEPROM.update(pwd.length(),255);
}

Reading the EEPROM is the opposite. Read in all values from EEPROM using byte read(int address) until we reach a value that is equal to 255.

/**
 * Initialize the password from EEPROM.
 *
 * @return The password or empty string when no password has been stored yet.
 */
String initializePasswordFromEEPROM() {
  int value;
  String retVal = "";
  for(int i=0; i<512;i++) {
    value=EEPROM.read(i);
    if(value == 255) {
      break;
    }else {
      retVal += (char)value;
    }
  }
  // Debug output
  Serial.print("Read ");
  Serial.print(retVal);
  Serial.println(" from EEPROM!");
  return retVal;
}

Once that code is in place we simply have to initialize the secret from EEPROM in our setup routine and then always write it back to EEPROM when we change it.

The Circuit Locker

When everything is setup and working we bring it together to create the circuit locker application. This application pin protects the circuit which is interrupted by the relay. To close the relay the user has to enter a valid pin. If the pin is not valid the relay will not close.
The application uses the LCD to communicate with the user and the keypad for entering the pin and navigating the menus. The user can change the pin and it is written to the EEPROM of the microcrontroller to persist it across restarts.

Create a new sketch, name it CircuitLocker and copy the following code to the sketch. The code can also be downloaded from Github.
/**
 * Copyright (C) 2015  Stefan Langer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses
 *
 * This program uses parts of the arduino core libraries published by Arduino LLC
 * which are released under the LGPL v2.1 or later.
 * http://www.gnu.org/licenses/lgpl
 *
 * All hardware, documentation and files distributed with this software that do
 * not fall under the GPL are distributed under the
 * Creative Commons Attribution-ShareAlike 4.0 International License
 * http://creativecommons.org/licenses/by-sa/4.0
 */
#include <avr/pgmspace.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>

// pins for the LCD
#define RS_PIN        2
#define EN_PIN        3
#define D0_PIN        4
#define D1_PIN        5
#define D2_PIN        6
#define D3_PIN        7
#define LCD_BACKLED  16
// the relay pin
#define RELAIS_PIN   17

// aliases for datatypes
#define uint unsigned int
#define ulong unsigned long


// cycles to count befor shutting off display
#define TIMEOUT 150 // approximately 30 seconds

// CONSTANTS for time management
#define SECOND    1000 // millis in one second
#define MINUTE   60000 // millis in one minute
#define HOUR   3600000 // millis in one hour
#define DAY   86400000 // millis in one day

// keypad row pins
#define ROW1 15
#define ROW2 14
#define ROW3 13
#define ROW4 12

// keypad column pins
#define COL1 11
#define COL2 10
#define COL3 9
#define COL4 8

const uint ROWS[] = {ROW1, ROW2, ROW3, ROW4};
const uint COLS[]  = {COL1, COL2, COL3, COL4};
const char LETTERS[][4] = {{'D', 'C', 'B', 'A'},
  {'#', '9', '6', '3'},
  {'0', '8', '5', '2'},
  {'*', '7', '4', '1'}
};

// CONSTANTS used to store the customized lock characters to the LCD
#define LOCK_GLYPH   0
#define UNLOCK_GLYPH 1

// defines the locked lock character
byte lockGlyph[8] = {
  B00000,
  B01110,
  B10001,
  B10001,
  B11111,
  B11111,
  B11111,
  B00000
};

// defines the unlocked lock character
byte unlockGlyph[8] = {
  B00000,
  B01110,
  B00001,
  B00001,
  B11111,
  B11111,
  B11111,
  B00000
};

LiquidCrystal lcd(RS_PIN, EN_PIN, D0_PIN, D1_PIN, D2_PIN, D3_PIN);
String secret;
boolean circuitLocked, isDisplayOn;
int displayTimeout;

/**
 * Prints the lock state to the first character of the last line.
 * The lock state is represented by a lock which is either closed
 * or open depending on whether the circuit is locked or unlocked.
 * <em>This method is only executed when the display is turned on</em>
 */
void printLockState() {
  if (!isDisplayOn) return;

  lcd.setCursor(0, 3);
  if (circuitLocked) lcd.write(byte(LOCK_GLYPH));
  else lcd.write(byte(UNLOCK_GLYPH));
}

/**
 * Prints the status line <pre>
 * <LockStatus>   0d00h00m00s
 * </pre>
 * to the last line of the LCD.
 * <em>This method is only executed when the display is turned on</em>
 */
void printStatusLine() {
  if (!isDisplayOn) return;

  ulong time = millis();
  ulong days = time / DAY;
  ulong hours = (time / HOUR) % 24;
  ulong minutes = (time / MINUTE) % 60;
  ulong seconds = (time / SECOND) % 60;

  printLockState();
  int dIdx = 1;
  if (days >= 10)
    dIdx = 0;
  lcd.setCursor(8 + dIdx, 3);
  lcd.print(days);
  lcd.print('d');
  if (hours < 10)
    lcd.print('0');
  lcd.print(hours);
  lcd.print('h');
  if (minutes < 10)
    lcd.print('0');
  lcd.print(minutes);
  lcd.print('m');
  if (seconds < 10)
    lcd.print('0');
  lcd.print(seconds);
  lcd.print('s');
}

/**
 * Prints the option text <pre>
 * Options:
 *  C - Change Pin
 *  D - Un/Lock circuit
 * </pre>
 * plus the status line to the LCD.
 * <em>This method is only executed when the display is turned on</em>
 */
void printOptions() {
  if (!isDisplayOn) return;

  lcd.clear();
  lcd.print("Options");
  lcd.setCursor(0, 1);
  lcd.print(" C - Change Pin");
  lcd.setCursor(0, 2);

  lcd.print(" D - ");
  if (circuitLocked) lcd.print("Unlock circuit");
  else lcd.print("Lock circuit");
  printStatusLine();
}

/**
 * Prints the text <pre>
 * Enter pin:
 * Press D to enter!
 * </pre>
 * plus the status line to the LCD.
 */
void printEnterCmd(boolean newPin) {
  lcd.clear();
  lcd.print(newPin? "Enter new pin" : "Enter pin:");
  lcd.setCursor(0, 1);
  lcd.print("Press D to enter!");
  printStatusLine();
}

/**
 * Returns the currently pressed key.
 * To find out the current key press we iterate through the row pins
 * and set each pin to HIGH. While the pin is HIGH we iterate through
 * the column pins and read their signal. When we find the column
 * that reads HIGH we get the pressed letter by using the index of the
 * current row and the index of the current column.
 * <code>key = LETTERS[rowIdx][colIdx]</code>
 *
 * @return the pressed key or -1 if
 *         no key press was registered.
 */
char getPressedKey() {
  for (int ri = 0; ri < 4; ri++) {
    digitalWrite(ROWS[ri], HIGH);
    for (int c = 0; c < 4; c++) {
      if (HIGH == digitalRead(COLS[c])) {
        digitalWrite(ROWS[ri], LOW);
        delay(50);
        return LETTERS[ri][c];
      }
    }
    digitalWrite(ROWS[ri], LOW);
  }
  delay(50);
  return -1;
}

/**
 * Reads in a numeric password from the keypad using {@link getPressedKey}.
 * 'D' stops entry and returns the currently entered password-
 * '*' deletes the last entered character
 * '#' clears the complete entry
 * All other keys are append to the password.
 * Additionally updates the status in the LCD using {@link printStatusLine}.
 *
 * @param line the line in the LCD to echo the entered key presses to.
 *
 * @return the entered password
 */
String enterPassword(int line) {
  String pin = "";
  delay(100);
  for (;;) { /*ever*/
    char key = getPressedKey();
    switch (key) {
      case -1:
        break;
      case 'D':
        return pin;
      case '#':
        pin.remove(0);
        lcd.setCursor(0, line);
        lcd.print("                    ");
        break;
      case '*':
        if (pin.length() >= 1)
          pin.remove(pin.length() - 1);
        lcd.setCursor(pin.length(), line);
        lcd.print(" ");
        break;
      default:
        lcd.setCursor(pin.length(), line);
        pin += key;
        lcd.print(key);
        break;
    }
    printStatusLine();
    // prevent double clicks
    delay(200);
  }
}

/**
 * Asks for a password and validates it against the currently set one.
 *
 * @param line the line in the LCD to echo the password to
 *
 * @return <code>true</code> when password matches <code>false</code> otherwise.
 */
boolean enterAndValidate(int line) {
  String pin = enterPassword(line);
  return secret.compareTo(pin) == 0;
}

/**
 * Initiates the password change algorithm:
 * Check whether there is a currently set password
 * - Password is not set
 *   a)  Read the new password from keypad using {@link enterPassword(int)}
 *   b)  Store the password to EEPROM using {@linnk changeAndStorePassword(String)}
 *   c)  Print status message to LCD and exit
 * - A password is set
 *   a)  Ask for the set password and validate it using {@link enterAndValidate(int)}
 *   b)  Password is correct
 *       - Read the new password from keypad using {@link enterPassword(int)}
 *       - Store the password to EEPROM using {@linnk changeAndStorePassword(String)}
 *       - Print status message to LCD and exit
 *   c)  Password is incorrect
 *       - Print status message to LCD and exit without changing the pin
 */
void changePassword() {
  String newPin;
  lcd.clear();
  if (isSecretSet()) {
    printEnterCmd(false);
    if (enterAndValidate(2)) {
      lcd.clear();
      printEnterCmd(true);
      delay(300);
      changeAndStorePassword(enterPassword(2));
    } else {
      lcd.clear();
      lcd.print("Invalid pin entered!");
      lcd.setCursor(0, 1);
      lcd.print("Pin was not changed!");
    }
    printStatusLine();
    delay(5000);
  }
  else {
    printEnterCmd(true);
    changeAndStorePassword(enterPassword(2));
  }
}

/**
 * Set the specified pin as the new password and writes it to EEPROM
 * to ensure its persistents accross restarts.
 * Before writing it to EEPROM the user is asked whether he really wants to change
 * the password.
 * On success the circuit is locked.
 */
void changeAndStorePassword(String pwd) {
  lcd.clear();
  lcd.print("Change pin to");
  lcd.setCursor(0, 1);
  lcd.print(pwd);
  lcd.print("?");
  lcd.setCursor(0, 2);
  lcd.print("Yes - Press A");
  lcd.setCursor(0, 3);
  lcd.print("No - Press other key");
  delay(200);
  for (;;) { /*ever*/
    switch (getPressedKey()) {
      case 'A':
        secret = pwd;
        writeToEEPROM(pwd);
        lockCircuit();
        lcd.clear();
        lcd.print("New pin set and");
        lcd.setCursor(0, 1);
        lcd.print("written to memory!");
        printStatusLine();
        delay(5000);
        return;
      case -1:
        break;
      default:
        lcd.clear();
        lcd.print("Pin was not changed!");
        lcd.setCursor(0, 1);
        printStatusLine();
        delay(5000);
        return;
    }
  }
}

/**
 * Writes the specified password to EEPROM.
 *
 * @param pwd Password to store in EEPROM
 */
void writeToEEPROM(String pwd) {
  // Debug output
  Serial.print("Writing ");
  Serial.print(pwd);
  Serial.println(" to EEPROM!");
  for(int i=0;i<pwd.length();i++) {
    EEPROM.update(i,pwd[i]);
  }
  EEPROM.update(pwd.length(),255);
}

/**
 * Initialize the password from EEPROM.
 *
 * @return The password or empty string when no password has been stored yet.
 */
String initializePasswordFromEEPROM() {
  int value;
  String retVal = "";
  for(int i=0; i<512;i++) {
    value=EEPROM.read(i);
    if(value == 255) {
      break;
    }else {
      retVal += (char)value;
    }
  }
  // Debug output
  Serial.print("Read ");
  Serial.print(retVal);
  Serial.println(" from EEPROM!");
  return retVal;
}

/**
 * Returns whether a password has been set.
 * Password has not been set when the internal secret is the empty string.
 *
 * @return <code>true</code> when a secret is set <code>false</code> otherwise.
 */
boolean isSecretSet() {
  return secret.length() > 0;
}

/**
 * Opens the relay by setting relay pin to HIGH, unlocking the circuit.
 * Prints status to LCD.
 */
void unlockCircuit() {
  circuitLocked = false;
  digitalWrite(RELAIS_PIN, HIGH);
  lcd.clear();
  lcd.print("Circuit unlocked!");
  printStatusLine();
}

/**
 * Closes the relay by setting relay pin to LOW, locking the circuit.
 * Prints status to LCD.
 */
void lockCircuit() {
  circuitLocked = true;
  digitalWrite(RELAIS_PIN, LOW);
  lcd.clear();
  lcd.print("Circuit locked!");
  printStatusLine();
}

/**
 * Turns off the backlight led by setting the backlight pin to LOW
 * and turns off the display using {@link LiquidCrystal#noDisplay()}.
 */
void turnOffDisplay() {
  isDisplayOn = false;
  digitalWrite(LCD_BACKLED, LOW);
  lcd.noDisplay();
}

/**
 * Turns on the backlight led by setting the backlight pin to HIGH
 * and turns on the display using {@link LiquidCrystal#display()}.
 */
void turnOnDisplay() {
  isDisplayOn = true;
  displayTimeout = 0;
  digitalWrite(LCD_BACKLED, HIGH);
  lcd.display();
}

void setup() {
  // setup pins
  pinMode(LCD_BACKLED, OUTPUT);
  pinMode(RELAIS_PIN, OUTPUT);
  digitalWrite(RELAIS_PIN, LOW);

  for (int i = 0; i < 4; i++) {
    pinMode(ROWS[i], OUTPUT);
    pinMode(COLS[i], INPUT);
    digitalWrite(ROWS[i], LOW);
    digitalWrite(COLS[i], LOW);
  }
  circuitLocked = true;
  displayTimeout = 0;
  // read previously stored password from EEPROM
  secret = initializePasswordFromEEPROM();
  // create the special lock symbols for LCD
  lcd.createChar(LOCK_GLYPH, lockGlyph);
  lcd.createChar(UNLOCK_GLYPH, unlockGlyph);
  // Initialize serial monitor for debugging
  Serial.begin(9600);
  // Initialize lcd and turn on display
  lcd.begin(20, 4);
  turnOnDisplay();
  // print status to lcd
  lcd.print("Initialized and");
  lcd.setCursor(0, 1);
  lcd.print("ready to go!");
  // print debug information to serial monitor
  Serial.println("Initialized and ready to go!");
  // wait 3 seconds and print the option menu
  delay(3000);
  printOptions();
}

void loop() {
  // when display is on check whether the display timeout counter has 
  // reached TIMEOUT if it has reached TIMEOUT turn off the display 
  // when not then increment the display timeout counter.
  if (isDisplayOn) {
    if (displayTimeout >= TIMEOUT) {
      turnOffDisplay();
    } else {
      displayTimeout++;
    }
  }
  // see if we have a key press 
  // when C is pressed initiate changePassword procedure.
  // when D is pressed initiate un/lock procedure.
  // if display is off activate it on any keypress
  char key = getPressedKey();
  switch (key) {
    case 'C':
      if (!isDisplayOn) turnOnDisplay();
      changePassword();
      printOptions();
      break;
    case 'D':
      if (!isDisplayOn) turnOnDisplay();
      if (circuitLocked) {
        printEnterCmd(false);
        if (enterAndValidate(2)) {
          unlockCircuit();
        } else {
          lcd.clear();
          lcd.print("Invalid pin entered!");
          lcd.setCursor(0, 1);
          lcd.print("Circuit remains");
          lcd.setCursor(0, 2);
          lcd.print("locked!");
          printStatusLine();
        }
      }
      else {
        lockCircuit();
      }
      delay(5000);
      printOptions();
      break;
    case -1:
      break;
    default:
      if (!isDisplayOn) turnOnDisplay();
  }
  // print status to last line of LCD and wait a 
  // short while to prevent double key presses
  printStatusLine();
  // Debug output
  Serial.print("EEPROM password: "); 
  Serial.println(initializePasswordFromEEPROM());
  delay(200);
}
CircuitLocker sketch on GitHub

Creating the Hardware

As a last step I soldered the breadboard arrangement onto a prototype board and hooked up the keypad and the LCD to it. Then I bought a generic casing made a hole for the LCD, the potentiometer, the keypad cable and the power supply cable for the protected circuit. For protection and to make it visually more interesting I added a plexi glas cover over the LCD. 
Putting everything together gives me my completed circuit locker app.

The Circuit Locker in Action


Resources

This is a non exhaustive list of tools and libraries used in the process of this tutorial. 
  • Arduino IDE the IDE to develop Arduino programs with
  • LiquidCrystal library available as part of the Arduino IDE
  • EEPROM library available as part of the Arduino IDE
  • Fritzing an application developed by the University of Applied Science Potsdam to document circuits. 
  • CircuitLocker Github repository containing all the code from this tutorial.

Summary

You should now be capable of

  • using a keypad to get HEX input, 
  • print output to an LCD using the LiquidCrystal library, 
  • use the EEPROM library to write and read from EEPROM 
  • use a relay to control high current circuits from your Arduino.

Feel free to use the circuit locker app for your own projects but please be so kind to give me proper credit. 
If you have further questions, found an error, have ideas for improvement or simply enjoyed the tutorial drop me a note in the comments.

I hope you enjoyed it

Yours truely
Stefan Langer
Creative Commons License
Circuit Locker and Tutorial by Stefan Langer is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Sunday, February 9, 2014

A Mission to Android - Registering the Widget

If you recall I have created the classes for my widget and have created a basic layout. Although the code in github already contains all necessary code snippets for running the widget I still have not officially added the widget provider meta data and registered the widget in the android manifest. So lets dive into today's post and complete the walk through of the basics for a widget.

Overview

In today's post we will provide the meta data for our widget and register the widget with android. After this post all elements for a widget should be in place and you should have a very basic widget running. You should have a clear understanding of the metadata for the widget and how widgets are registered in the manifest file.

The Widget Provider Meta File

Before adding the widget to the manifest android has to know what my widget looks like and where it can find it. This is done with the appwidget-provider file. This file describes to android how the widget is to be initially displayed and what dimension the widget has. Further it provides an icon to be displayed in the widget selection screen. This is also the place to describe where the widget can be added.

Let's have a look at a basic file and go over it step for step.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
                    android:minHeight="120dp"
                    android:minWidth="120dp"
                    android:previewImage="@drawable/ic_launcher"
                    android:updatePeriodMillis="0"
                    android:initialLayout="@layout/clock_layout"
                    android:widgetCategory="home_screen|keyguard"
                    android:initialKeyguardLayout="@layout/clock_layout">
</appwidget-provider>

In line 2 and 3 the minimum height and minimum width of the widget is described. This will determine how many cells the widget takes up on the screen. The size of a cell is determined by the size of the screen and its density. To be compatible on all android devices your widget should never be bigger then 4x4 cells as this is the bare minimum each android device has to support. From there you can always make your widget scalable (Android SDK Level > 13). If you use higher sizes your widget might not be available on all devices so make sure to check your target devices. Although not done in the above example it is a good idea to externalize the dimension to the dimens.xml file and provide adapted ones for different target devices.

In line 4 I provide the launcher icon for my widget. This is an iconic view displayed in the widget selection activity. It should indicate to the user what kind of widget he is about to add to his desktop. Additionally this launcher icon's outline is used to show the user where the widget is placed on the screen.

Line 5 specifies the update interval at which your widget is automatically updated by the OS. Once this period has passed android will send an update event to your widget. This interval is not intended to do short term updates and allows as a minimum value 30 minutes. Any update intervals below this have to be done manually via the AlarmManager or by using a Handler in your code.

Line 6 specifies the initial layout for your widget. Android will inflate this layout and use it to depict your widget until your classes have been initialized and custom code has been executed. So be sure to have a nice default layout or present a "widget loading" layout until the widget has been initialized and your code can present the actual widget.

Line 7 specifies where your widget can be added. At the current time the options are:

  • home_screen - the widget can be added to the home screen
  • keyguard - the widget can be added to the lock screen.
You can combine the two options using the "|" operator.

If you specify keyguard you can specify a separate initial layout for the lock screen using android:initialKeyguardLayout. For now I simply use the same layout for the lock screen as I use for the home screen (Line 8).

Registering the Widget

With the app widget provider file in place I'm all set to add the widget to the android manifest. As we recall from the beginning a widget is nothing else but a special intent receiver. So in order to get the widget selectable through android we need to register it in the AndroidManifest.xml as a receiver and specify that it will receive widget update events. These update events will cause the widgets onUpdate method to be called and trigger our layout logic. Taking our manifest file from "A Mission to Android - Creating a Widget" we simply add the receiver as follows:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="de.element34.e34clock"
    android:versionCode="1"
    android:versionName="1.0">

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        
        <receiver
            android:name=".E34ClockWidget"
            android:label="@string/widget_label">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/e34clock_widgetprovider" />
        </receiver>
    </application>

</manifest>

Intents

Intents are the cardiovascular system of android. They provide the means to communicate between components (activities, services receivers) or to setup a tight bindings between processes. As they are a huge topic on their own I will provide a separate post about them in the future. For now we can think of an Intent as a container to carry data to components.

Lines 17 and 18 specify the class representing the widget and the label it will appear under. As a good android developer I have externalized the label of course ;-). For the class we can either specify the complete class name or when the package of the class is the same as the package specified in the manifest (Line 2) we can simply leave that part of and specify the rest of the package and the class name starting with a dot. Android will automatically add the appropriate package when looking up the class.

Line 22 to 24 tell android where it can find the app widget provider file. This data is used by android to add the widget to the widget selection dialog and make it available to the user. If you do not specify this section the widget will never be displayed in the widget selection dialog.

Line 19 to 21 register the receiver for the APPWDIGET_UPDATE intent which provides the glue between android and our code. Whenever somebody adds a widget to the home screen or the OS needs to update the widget a APPWIDGET_UPDATE event is sent. Our class will then convert this event into a call to the appropriate on-method call allowing customization in code.

Summary

First we created the necessary classes. Then we created the layout and now we have create the widget provider file and added it to the manifest. All parts have been put in place and the widget can now be deployed to a device. Running it on the emulator results in

Resources

You can find further and more detailed information about developing widgets at App Widgets | Android Developers. The section on AppWidgetProviderInfo describes the widget meta data in details.

Outlook

In future posts I will go into details about certain aspects of my widget. The things I plan on covering are using services to update widget informations, creating custom intents to transfer this information between the different widgets. Making sure that the widget doesn't drain battery by switching it off when it is not displaying.

As always if you like this post or you have criticism, found errors or have suggestions feel free to contact me. I'm always happy to hear from you.

Yours truely

Stefan Langer

Creative Commons License

Saturday, November 23, 2013

A Mission to Android -- Creating the Layout

Welcome back to my blog on my journey to my own clock widget. Writing this post about layouts and resources was way more difficult then I anticipated. Resource handling and internationalization and localization (i18n and l8n) are such broad topics that I can barely scratch the surface of it all in a single post. Still I hope that I can give you a small inside into the process. So lets dive into today's post and talk about the layout.

Overview

Today I will explain the layout and along the way talk a bit about how android handles resources like images and strings. Further I will try to show how the declarative approach allows to define different resources depending on the country, the screen size and many more things. After this post you should be able to create your own layouts for different languages and screen sizes and understand how to interact with your layout from code.

Resource Selectors

The android world consists of many different devices with different screen densities, sizes and memory footprint. Further more if you release an app into this ecosystem and you want to compete on a global market with hundreds of different languages and type faces you need a way to define your UIs in a way that allows to compensate for these different configurations to bring the best user experience to your app. In android this is realized by externalizing resources like strings, dimensions, images, animation, etc. into separate files and providing mechanisms to select them based on qualifiers defining the different configurations.

Android distinguishes between default and alternative resources. Default resources are either device independent or represent the least common denominator for all platforms. Alternative resources are grouped according to the specific configuration they are designed for. When a resource is requested from the OS it is looked up in the folder with the most specific matching configuration falling back to the lesser specific once. If no match is found the default resource is used.

Resources are put under the res folder in one of the sub folders depending on the type of the resource:

  • layouts go into the layout sub folder
  • the values subfolder takes strings, colors and dimension files
  • the menu sub folder takes menu definitions
  • drawable takes image resources
  • the xml sub folder takes arbitrary xml files containing meta data like app widget provider meta data
For a complete listing of known subfolders take a look at Providing Resources|Android Developers.

To specify a configuration a qualifier designating the configuration is appended with a hyphen to the folder name: <folder name>-<qualifier>. Qualifiers can be composed to create more specific configuration. Android always selects the most specific configuration based on the context of the used resource. A complete list of available qualifiers can be found in table 2 at Providing Resources|Android Developers. The order in which the qualifiers are composed has to correspond to the order they are given in the table or else Android will ignore the given folder. You will see the usage of qualifiers in the context of using image resources.

A special case for the use of the qualifiers is internationalization (I18N) and localisation (L10N). I18N and L10N is realized by appending the country code and/or the language code to the values sub folder. I will give an example in the "Defining Text" section on their usage.

The Layout

Layouts

As android has to support many different screen densities and sizes it is essential that the layout system can adapt. For this purpose android provides a predefined set of Layouts. These are exposed either through code or in the form of xml tags. The xml tags are inflated to the corresponding classes before display. No matter how you define the layout it is the job of the layout to adapt its elements to the screen. Depending on the layout the strategy for this adaption is different.

Layouts can be nested to produce more complex layouts but be careful the more complex the hierarchy gets the longer it takes android to display it. Always try to keep the hierarchy as flat as possible to improve performance and keep your UI responsive.

Some of the layouts available are

  • LinearLayout - displays its content as a horizontal or vertical single row
  • GridLayout - displays its content in a grid
  • FrameLayout - displays its content a single frame with predefined anchor points. This layout is intended to display only one child but it can be used to stack children on top of each other.
  • RelativeLayout - displays its content in accordance to their relative layout declarations.
The layout of the widget UI is defined declarative in a xml file. Although you can create a view completely in code the android way of doing things is doing it declarative using layouts. Don't worry you can interact with the view during inflation and, as you will see, only change the necessary parts in code.

The xml file contains the used layout as the top element and the child elements that make up your screen. For now I will simply define one layout and size it in a way so that it works on the screen of a Nexus 4. (The Nexus 4 has a 4,7" display with a pixel density of 320dpi)

The first thing to decide is which layout to use for the UI. As I'm planning a complex UI with elements that overlay on top of each others I either have to use a frame layout or a relative layout. I chose the relative layout which allows me to position individual elements in relation to each other making it unnecessary to use nested layouts while giving me complete control over each element's position. The disadvantage of this approach is that I have to specify the sizes so that the overlay works properly.

To make things easy I start with just the analog clock element added to the layout. The analog clock element provides the means to show a completely functional analog clock with customized hours, minutes arms and a custom dial. If not specified android uses the system default drawable resources for minute, hand and dial.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" 
    android:layout_height="wrap_content">
    
    <TextView
        android:id="@+id/clockLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:text="@string/clock_label" />

    <AnalogClock
        android:id="@+id/analogClock"
        android:layout_width="@dimen/analogclock_layout_width"
        android:layout_height="@dimen/analogclock_layout_height"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/clockLabel"
        android:dial="@drawable/dial"
        android:hand_hour="@drawable/hour_hand"
        android:hand_minute="@drawable/minute_hand"
        android:paddingLeft="1dp" />    

</RelativeLayout>

The above layout positions label along the top edge of the layout (line 12) the analog clock below the clock label (line 19) and aligning the label and the clock element with the parent element (line 11,18) resulting in the schematic layout:

When the layout is resized the elements keep their relative position and grow according to their width and height. If the elements do not fit into the current layout size then the layout is cropped. The definition of the different elements is read from top to bottom. Elements that overlap are drawn on top off each other according to this order.

Besides its relative position each element has a margin and a padding which are considered during positioning and added to the space the element occupies.

The following box model shows the different positioning attributes that can be specified for each element.

You can either specify a relative position like android:layout_toLeftOf="@id/...", android:layout_toEndOf="@id/..." or you can align the edges of the elements using android:layout_alignBottom="@id/..." or android:layout_alignStart="@id/...". You can also specify whether the element should align with the edges of the parent android:layout_alignParentTop="true". For a complete list of possible attributes read RelativeLayout.LayoutParams|Android Developers.

Providing Image Resources

Since I do not want a standard clock I exported my design of the hour, minute and dial from my SVG file and added them to the platform as drawable resources. To do this you simply copy the images, preferrebly as pngs, into one of the drawable sub-folder. The drawable can then be referenced in the layout by prefixing its name with @drawable/ in the src attribute.

<AnalogClock
        android:id="@+id/analogClock"
        android:layout_width="@dimen/analogclock_layout_width"
        android:layout_height="@dimen/analogclock_layout_height"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/clockLabel"
        android:dial="@drawable/dial"
        android:hand_hour="@drawable/hour_hand"
        android:hand_minute="@drawable/minute_hand"
        android:paddingLeft="1dp" />    

It is also possible to reference the drawable from code. Android generates a static id named according to the image file name in the drawable section of the R file. This id can then be used to maniuplate the image in code.

Example showing how to set a image using a resource id

ImageView imageView = ...;
imageView.setImageResource(R.drawable.dial);
This code snippet will pick the image named dial.png and add it to the imageView.

The R File

Android generates ids for all resources so that they can be referenced easily in code. These ids are put in the R file which provides static fields for each resources in a namespace representing the resource type.

The res file structure:

res/
 |-- layout/
 |    |- clock_layout.xml
 |
 |-- drawable/
 |    |- dial.png 
 |    |- hours.png
 |     
 |-- values/
 |    |- string.xml 
 |    |   |-> contains key:
 |    |         clock_label 
 |    |
 |    |- dimen.xml 
          |-> contains key: 
                clock_width, 
                clock_height

will result in the following R file:

public final class R {
    public static final class attr {
    }
    public static final class dimen {
        public static final int clock_height=0x7f050000;
        public static final int clock_width=0x7f050001;
    }
    public static final class drawable {
        public static final int dial=0x7f020000;
        public static final int hour=0x7f020001;
    }
    public static final class id {
        public static final int analogClock=0x7f080001;
        public static final int clockLabel=0x7f080000;
    }
    public static final class layout {
        public static final int clock_layout=0x7f030000;
    }
    public static final class string {
        public static final int clock_label=0x7f060002;
    }
    public static final class style {
        // ... 
    }
    public static final class xml {
        // ... 
    }
}

Supporting different Screen Sizes

Which drawable is loaded depends on the screen metrics of your device and the available resource selectors. Android will try to find the best match and scale it appropriately. Depending on which version of the SDK you are using you have different possibilities to provide the qualifiers for your drawable resources.

  • drawable-ldpi designates a low density screen with 120dpi. This attribute was introduced in SDK Level 4.
  • drawable-mdpi designates a medium density screen with 160dpi. This attribute was introduced in SDK Level 4.
  • drawable-hdpi designates a high denisity screen with 240dpi. This attribute was introduced in SDK Level 4.
  • drawable-xhdpi designates a very high denisity screen with 320 dpi. This attribute was introduced in SDK Level 9.
  • drawable-xxhdpi designates screen densities with 480dpi. This attribute was introduced in SDK Level 16.
  • drawable-xxxhdpi designates screen densities with 640dpi. This attribute was introduced in SDK Level 18.
  • drawable-nodpi non scalable
  • drawable-tvdpi from SDK Level 13 213dpi. This attribute was introduced with SDK Level 13.
each of the qualifier defines a standard screen size for known android devices. When you request a drawable android finds the best match and scales the drawable to the actual size. The scaling factor of ldpi:mdpi:hdpi:xhdpi is 3:4:6:8. Providing appropriately scaled images prevents scaling artifacts in the UI. The problem with this approach is that with tablets and the new 5" and 7" displays the categories do not always scale properly. To fix this android added new qualifiers with SDK level 13 to enable the definition of folders for all sorts of screen sizes.
  • drawable-h<height>dp height of the screen in dp
  • drawable-w<width>dp width of the screen in dp
  • drawable-sw<height>dp the size of the shortest screen width in dp
The new qualifiers define the width (w), height (h) or the shortest screen size (sw) of the screen in dp allowing to specify a concrete size for which the image is optimized. As a fallback you simply put a default image in the drawable folder if no qualifiers matches android will simply pick the image defined there.

Example file structure

|-drawable
|   |- test.png
|
|-drawable-w820dp
|   |- test.png
|
|-drawable-h240dp
    |- test.png

Defining Dimensions

The clock should have a certain height and width. I can specify this directly in my layout file but this would mean if I have to support other layouts where the height might have to change I have to provide an alternative layout file which only differs in the dimension of the analog clock. To make it possible to reuse the layout structure we defer the dimension declaration into the dimension resource which can then be provided for different layouts countries etc. This allows me to separate the structure of the UI from the actual dimensions.

<resources>
    <dimen name="analogclock_layout_width">120dp</dimen>
    <dimen name="analogclock_layout_height">120dp</dimen>
</resources>

To reference the dimension in your layout you simply use the prefix @dimen/ and specify the name of the dimension to use.

<AnalogClock
        android:id="@+id/analogClock"
        android:layout_width="@dimen/analogclock_layout_width"
        android:layout_height="@dimen/analogclock_layout_height"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/clockLabel"
        android:dial="@drawable/dial"
        android:hand_hour="@drawable/hour_hand"
        android:hand_minute="@drawable/minute_hand"
        android:paddingLeft="1dp" />    

When specifying dimensions always try to be pixel independent. To ease this android provides device independent pixels dp for specifying pixel sizes and scalable points sp for specifying text sizes in a screen independent way. You might also see dip as the unit for device independent pixel but the preferred way is to use dp. dp and sp are always calculated relative to the actual pixel size so that the final dimensions remain the same relative to each other. On a high density display the actual size of 1dp can be 2.5px or 3px while on a low density device the same 1dp equals 1px. The android documentation says that on a 160dpi device 1dp = 1px. Always keep this in mind when designing your UIs and make your UI adaptive to different screen sizes to cover a broad range of devices.

Be careful when checking the design on the emulator the actual screen always looks slightly different from the emulator due to the scaling factor on the emulator in relation to the screen of your development machine. As an example the text size on my actual phone looks slightly smaller then on the emulator making some of my text sizes too small on the real device.

Defining Text

Just as dimension are extracted into a separate file so are strings used in the layout. This allows to overwrite the strings for different languages without having to provide a language specific layout. For the current state of my widget I have provided the following string.xml file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">E34Clock</string>
    <string name="clock_label">Current Time is:</string>
</resources>
These labels are applied in the layout by referencing the corresponding string resource by its name prefixed by @string/.
<TextView
        android:id="@+id/clockLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:text="@string/clock_label" />
To provide a translation for the strings I simply have to provide a new resource folder with the appropriate ISO 639-2 language code attached with a hyphen. Since I'm providing a German translation I have to append the language code de to the folder name: values-de. I then simply add the string.xml file to this folder and translate all necessary values.
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="clock_label">Die aktuelle Uhrzeit ist:</string>
</resources>

When android inflates the layout it will try to find the best matching strings.xml and use its values. In case there is no specific folder it falls back to the default which in this case contains the English translation. This way I can internationalize my application by simply providing different strings.xml for the different language.

We can expand on this to incorporate L10N. For this we have to append the qualifier <r3166-alpha-2 ISO Region Code> to the folder name. Lets assume we want to provide a currency value for the United States, the United Kingdom, Germany and Switzerland. First we define the default strings.xml which in this case would be for Germany.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="currency">100,00€ (EUR)</string>
</resources>
We can then localize this by specifying the folder for each country and providing the appropriate content for currency.
 |- values-rDE
 |    |- currency = 100,00€ (EUR)
 |
 |- values-rUS
 |    |- currency = $134.70 (USD)
 |
 |- values-rGB
 |    |- currency = £83.38 (GBP)
 | 
 |- values-rCH
      |- currency = 123,21 SFr (CHF)

When necessary you can combine these qualifiers to specify localized translated values.

 |- values-en
 |     |- english translations
 |
 |- values-en-rUS
       |- english translations specific to the United States of America

Summary

The resource selection mechanism allows to specify different layouts for different orientations, screen sizes and languages. By separating the different aspects, - layout, text, dimensions, etc... - you can customize only the necessary parts of your UI. Although I have not shown it it is possible to use this for other resources (e.g. colors, menus) as well. You are not limited to using the selectors in layouts you can also refer to the resources in your code using the ids generated in the R file. All this together provides a powerful mechanism to publish your app for all android devices out there.

Running the represented code available from the FirstStep branch on Github results in

I hope you enjoyed this post and learned some stuff along the way. If you find any errors leave a comment or if you have questions just ask them. I would also appreciate it if you just give me some feedback in the comment section.

Outlook

Next post I will explain how we enable the widget in android and which meta data is necessary to activate the widget.

Resources

Yours truely

Stefan Langer

Creative Commons License

Thursday, November 14, 2013

Mission to Android - Tools and Helpers

Today I would like to take a short break from writing about coding and talk a bit about some tools I have come across to ease the life of android developers and designers. I would be more then happy if you guys could give some more tips and tricks and add to this list of tools.

Faster Emulater

When I first started developing on android the most frustrating factor was the poor performance of the emulator. If you are not capable to debug and develop on the real device you are in for some frustrating moments. Just read my blog post on the topic if you want to hear my rant. Anyway +Zachary Dreeman suggested to try out Genymotion. I did and I must say the performance boost is magnificent. Genymotion provides a Android test system on a virtual box image with some additional tools for simulating battery status, GPS, etc ... After installing the tooling itself you need to download the images and can then start them. After the vm starts up, which takes a minute or two, it is quick and responsive. Currently there are only downloadable images up to Android 4.2.2. But considering the much better performance compared to the stock emulator it is well worth the effort and since it is open source it should only be a matter of time until somebody provides more images.

Layout Grid

Android provides some nice debugging options to help you in designing your UI. One of them is the ability to show all layout boundaries. To enable this setting first turn on the developer options on your device by pressing several times on the build number in the About phone section in the settings. This will make the developer options appear in the settings and there you can check the "Show layout bounds" section.

The problem is once you see the layout boundaries it tends to be tedious to fix layout issues by changing dimension, recompiling and deploying to update the app. Wouldn't it be nice to have a grid of a predetermined size in order to have some clue on the dimensions of things. Well I stumbled across GridWichterle in a G+ post.

It is a simple app which allows you to overlay a grid over your app to show how your elements align. The distance of a grid element can be configured in dp and the color of the grid can be changed. The app seems simple but I found it of great help to have this grid to see how elements align and to have a measurement of how big elements have to be.

Summary

Now I'm waiting for your list of tools which ease development and design. I will try to give a follow up post with a compiled list of all the cool tools you add to this list. I'm eager to hear from you...

Yours truely

Stefan Langer

Creative Commons License

Thursday, October 31, 2013

A Mission to Android - Creating a Widget

So my mission continues and it's time to get my hands dirty and start coding the widget. I think everybody knows that a widget is a small program which can be placed on the home or the lock screen. A widget can show information or provide functions to interact with the system. To summarize a widget is just another means to interact with an app running in the background.

Overview

In today's post I will create the base class for my widget. During this post I hope to give you some inside on how a widget provider works and how it interacts with the android system. You will see how a view is prepared for display in a widget and on a side note you will get an overview into the log system of android.

Coding the AppWidgetProvider

Widget Lifecycle

  1. Widget is added for the first time to an AppWidgetHost like the home or lock screen. This is the time to allocate resources and start services needed for the operation of the widget.
  2. Widget needs to be updated or a new instance of the widget is added to an AppWidgetHost. This method is not called on the first addition of a widget instance. So make sure that any preparation for layouting is also done in the onEnabled() method.
  3. Widget is removed from an AppWidgetHost.
  4. Last instance of a widget is removed from an AppWidgetHost and the AppWidgetProvider is no longer needed. This is the right time to cleanup resources and stop any sticky services.
  5. Called when the widget is displayed or when it is resized. This is where you should calculate sizes and adjust views accordingly. Only available in API level 16 and higher.

All widgets are specialized broadcast receivers which execute stuff depending on special intents they receive from Android. Some of these intents are ACTION_APPWIDGET_UPDATE, ACTION_APPWIDGET_DELETED, ACTION_APPWIDGET_ENABLED, ACTION_APPWIDGET_DISABLED and more. You can get an overview taking a look at the AppWidgetManager documentation.

To ease creation and handling of widgets android provides a specialized class AppWidgetProvider which extends BroadcastReceiver and provides methods to hook into the life cycle of a widget. All you have to do is extend it and implement the methods.
If you take a look at the sources for AppWidgetProvider you will see that all life cycle methods are called from within the onReceive method by handling intents send from the OS.

So here is the code for my widget

/*
 * (c) Copyright 2013 Stefan Langer
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package de.element34.e34clock;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.util.Log;
import android.widget.RemoteViews;

public class E34ClockWidget extends AppWidgetProvider {
    private static final String TAG = "e34clock.E34ClockWidget";

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        Log.d(TAG, "E34ClockWidget created!");
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        Log.d(TAG, "E34ClockWidget completely removed!");
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
        Log.d(TAG,"Deleting an instance of widget!");
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        Log.d(TAG, "Updating widgets!");
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.clock_layout);
        appWidgetManager.updateAppWidget(appWidgetIds, views);
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }
}

The code is pretty straight forward and doesn't do anything seriouse yet. First thing to notice is that we extend AppWidgetProvider and as discussed earlier this gives us a couple of methods to interact with the lifecycle of the widgets. Important to note here is that this class will not handle a specific instance of your widget but all instances. The idea is that all widgets will present the same state of the application so there is no need for individual control of a single widget.

When the first instance of your widget is created the OS calls the onEnabled method giving you the chance to setup any resources you may need to interact with the backend process. For each new instance being created or whenever your widget needs to be updated do to events in the OS the onUpdate method is called. When an instance is deleted from the home- or lockscreen the onDeleted method is called. When the last instance is deleted and the OS no longer needs to interact with your widget the onDisabled method is called giving you a chance to cleanup and release all resources.

For now I simply log a message using the log system from android. This is exposed through the static object Log.

The log system supports 5 log levels. The list shows the log levels in descending order from most verbose to least verbose. When the log level is set all log levels that are lower in the list are also included, e.g. when the level is INFO the levels WARN and ERROR are also printed.

  • VERBOSE - exposed through the v method
  • DEBUG - exposed through the d method
  • INFO - exposed via the i method
  • WARN - exposed via the w method
  • ERROR - exposed via the e method
Each log method is prefixed with a tag. This tag can later be used to filter the log and see only messages relevant to your code. The tag is a simple string. It does not imply any hierarchy in the log system as does the jdk or log4j logging system. The string is simply prefixed to the log message to ease grepping of log output. A good convention is to save the prefix in a static final variable and reuse this throughout the class. I use TAG throughout my code as the name for this variable.

For the widget to be able to display something I have to setup the initial view. The place to do this is in the onUpdate method as this gets called whenever the screen needs to be updated. Be aware that the onUpdate method is not called when the widget is first enabled. If you need to do view customization which divert from your layout you have to also call your update code from onEnabled.

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        Log.d(TAG, "Updating widgets!");
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.clock_layout);
        appWidgetManager.updateAppWidget(appWidgetIds, views);
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }

RemoteViews

A class that describes a view hierarchy that can be displayed in another process. The hierarchy is inflated from a layout resource file, and this class provides some basic operations for modifying the content of the inflated hierarchy.

Unlike in an Activity I do not have direct access to the View instead I have to create an instance of RemoteViews. This RemoteViews reads the layout xml and inflates it into a view hierarchy. I then use the AppWidgetManager to dispatch the views to all instances of the widget. You can steer which widgets are updated by specifying only the ids of the widget to update. Each id represents one instance of the widget. In my code I always treat all instances the same way and update every single one.

Resources

You can find further and more detailed information about developing widgets at App Widgets | Android Developers. For widget design take a look at Widgets | Android Developers and for design guide lines got to App Widget Design Guidelines

Outlook

In the next post I will post about the layout for the widget and I will talk about how android handles resources like images, strings and dimensions.

As always if you have any feedback please leave a comment. If you found errors or stuff I should do differently please tell me as this is also an endeavor for me to learn android.

Yours truely

Stefan Langer

Creative Commons License