This week we answer a number of viewer suggestions and questions. How do I make a
remote scoreboard? How does 2-way communication work? Using a keypad to enter data? How do I control a sensor remotely?
The episode includes a scheme to verify that the data sent was received correctly.
Downloads
Components:
Arduino Uno
XBEE 2.5 Datasheet (see below for setup): https://www.sparkfun.com/datasheets/Wireless/Zigbee/XBee-2.5-Manual.pdf
25-Key membrane keypad (purchased at local electronics store, but with no datasheet). Here is where we found a web page about it: http://www.rasmicro.com/25_keypad+Arduino.htm
Library used with this keypad can be downloaded at: http://www.rasmicro.com/Keypad.zip
LCD Screen (used on previous episodes): http://www.sainsmart.com/sainsmart-iic-i2c-twi-serial-2004-20x4-lcd-module-shield-for-arduino-uno-mega-r3.html
74HC595 Shift registers (used on previous episodes): http://www.ti.com/lit/ds/symlink/sn74hc595.pdf
Original Shifter library: http://www.sainsmart.com/sainsmart-iic-i2c-twi-serial-2004-20x4-lcd-module-shield-for-arduino-uno-mega-r3.html
The shifter library is modified for this Demo. Here is the shifter.h file:
#ifndef Shifter_h
#define Shifter_h
// Include the standard types
#include <Arduino.h>
// Define the Shifter class
class Shifter
{
public:
// Constructor
Shifter(int SER_Pin, int RCLK_Pin, int SRCLK_Pin, int Number_of_Registers);
void write();
void setPin(int index, boolean val);
void setAll(boolean val);
void clear();
void setDigit(int index, int val);
private:
int _SER_Pin;
int _RCLK_Pin;
int _SRCLK_Pin;
int _Number_of_Registers;
byte _shiftRegisters[25];
};
#endif //Shifter_h
Here is the shifter.cpp file:
// Include the standard types
#include <Arduino.h>
#include <Shifter.h>
// Constructor
Shifter::Shifter(int SER_Pin, int RCLK_Pin, int SRCLK_Pin, int Number_of_Registers){
_SER_Pin = SER_Pin;
_RCLK_Pin = RCLK_Pin;
_SRCLK_Pin = SRCLK_Pin;
_Number_of_Registers = Number_of_Registers;
pinMode(_SER_Pin, OUTPUT);
pinMode(_RCLK_Pin, OUTPUT);
pinMode(_SRCLK_Pin, OUTPUT);
clear(); //reset all register pins
write();
}
void Shifter::write(){
//Set and display registers
//Only call AFTER all values are set how you would like (slow otherwise)
digitalWrite(_RCLK_Pin, LOW);
//iterate through the registers
for(int i = _Number_of_Registers - 1; i >= 0; i--){
//iterate through the bits in each registers
for(int j = 8 - 1; j >= 0; j--){
digitalWrite(_SRCLK_Pin, LOW);
int val = _shiftRegisters[i] & (1 << j);
digitalWrite(_SER_Pin, val);
digitalWrite(_SRCLK_Pin, HIGH);
}
}
digitalWrite(_RCLK_Pin, HIGH);
}
void Shifter::setPin(int index, boolean val){
int byteIndex = index/8;
int bitIndex = index % 8;
byte current = _shiftRegisters[byteIndex];
current &= ~(1 << bitIndex); //clear the bit
current |= val << bitIndex; //set the bit
_shiftRegisters[byteIndex] = current; //set the value
}
void Shifter::setAll(boolean val){
//set all register pins to LOW
for(int i = _Number_of_Registers * 8 - 1; i >= 0; i--){
setPin(i, val);
}
}
void Shifter::clear(){
//set all register pins to LOW
for(int i = _Number_of_Registers * 8 - 1; i >= 0; i--){
setPin(i, LOW);
}
}
// Added by Bob Powell for demo on Lets Make It (www.letsmakeit.tv)
void Shifter::setDigit(int index, int val){
// return without doing anything if its not a valid shift-register number
if (index >= _Number_of_Registers)
return;
bool value;
// sets the first pin of shift register indicated
int sr = index * 8;
for(int i = 0; i < 8; i++){
value = val & 1;
setPin(i+sr,value);
val >>= 1;
}
}
For help adding libraries to Arduino, check the manual section of this page: http://arduino.cc/en/Guide/Libraries
Don’t forget to exit the Arduino IDE, then start it again for your changes to take place.
The X-CTU programmer was used to program the XBEE radio. The programmer can be found at: http://www.digi.com/support/productdetail?pid=3352&osvid=57&type=utilities
When using this software, and programming these radios, remember that you will need lots of patience and testing. It was difficult for both Mike and Bob to properly setup the XBEE radios.
Here is the setup for the home or base radio. Note that is hardcoded with the serial number of the remote radio, and the remote radio setup is hardcoded with the serial number of the home radio. This means that they will talk only to each other (if you were so inclined, you could also hack into them know this setup).
Home radio setup:
XB24-B_ZigBee_1047.mxi
80
0
251
1047
0
[A]ID=424
[A]SC=1FFE
[A]SD=3
[A]NJ=FF
[A]DH=13A200
[A]DL=408B725F
[A]ZA=0
[A]SE=E8
[A]DE=E8
[A]CI=11
[A]NI=HOME
[A]BH=0
[A]AR=FF
[A]DD=20000
[A]NT=3C
[A]NO=0
[A]PL=4
[A]PM=1
[A]EE=0
[A]EO=0
[A]BD=3
[A]NB=0
[A]RO=3
[A]D7=1
[A]D6=0
[A]SP=20
[A]D0=1
[A]D1=0
[A]D2=0
[A]D3=0
[A]D4=0
[A]D5=1
[A]P0=1
[A]P1=0
[A]P2=0
[A]LT=0
[A]RP=28
[A]PR=1FFF
[A]IR=0
[A]IC=0
[A]V+=0
[A]CT=64
[A]GT=3E8
[A]CC=2B
Remote radio setup:
XB24-B_ZigBee_1247.mxi
80
0
251
1247
0
[A]ID=424
[A]SC=1FFE
[A]SD=3
[A]NJ=FF
[A]JV=0
[A]DH=13A200
[A]DL=408B723A
[A]ZA=0
[A]SE=E8
[A]DE=E8
[A]CI=11
[A]NI=REMOTE
[A]BH=0
[A]AR=FF
[A]DD=20000
[A]NT=3C
[A]NO=0
[A]PL=4
[A]PM=1
[A]EE=0
[A]EO=0
[A]BD=3
[A]NB=0
[A]RO=3
[A]D7=1
[A]D6=0
[A]SM=0
[A]ST=1388
[A]SP=20
[A]SN=1
[A]SO=0
[A]D0=1
[A]D1=0
[A]D2=0
[A]D3=0
[A]D4=0
[A]D5=1
[A]P0=1
[A]P1=0
[A]P2=0
[A]LT=0
[A]RP=28
[A]PR=1FFF
[A]IR=0
[A]IC=0
[A]V+=0
[A]CT=64
[A]GT=3E8
[A]CC=2B
Blink Demo - Home radio code:
This is the standard “Blink” demo from the Arduino IDE with 3 lines added for serial output to the XBEE.
/*
Blink
Turns on an LED on for one second, then off for one second, repeatedly.
This example code is in the public domain.
*/
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
// Setup the serial connection
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
Serial.println('1'); // Send a '1' out
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
Serial.println('0'); // Send a '0' out
delay(1000); // wait for a second
}
Blink Demo - Remote radio code:
/*
Blink
Turns on an LED on for one second, then off for one second, repeatedly.
This example code is in the public domain.
*/
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
// Setup the serial connection
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
// Setup the variable to read the incoming stream
char tempchar;
if (Serial.available() > 0){
// Since the home radio is only sending one char,
// we need read only one character.
tempchar = Serial.read();
// If we recieve a '1' then turn on our LED and turn it
// off if a '0' is received. Anything else is ignored.
if(tempchar == '1'){
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
}
else if (tempchar == '0'){
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
}
}
}
LCD_XBEE_Keypad_4Digit_Home - Home radio code:
/**************************************************************
Name LCD_XBEE_Keypad_4Digit_Home
Author Bob Powell
texanfromiowa@gmail.com
Copyright (C) 2012-2014, Parallelus Automation, Inc.
Date Febuary 3, 2014
Modified Febuary 3, 2014
Version 1.0.0
Arduino 1.0.5
Notes This demo shows multiple examples of how to control a remote display.
The primary example is to remotely control a scoreboard.
Old comments from previous demonstrations:
This is a demo of a LM35 temp sensor, with output on an LCD screen.
Datasheet for the sensor used: http://www.mouser.com/ds/2/282/snis159b-186967.pdf
The sensor is connected to the 5V supply and ground from the Arduino, and analog
input pin A1.
The temperature formula used came from here: http://playground.arduino.cc/Main/LM35HigherResolution
As the note in this article reminds us, the range in this configuration is
from 0 to 110 degrees.
The LCD used: http://www.sainsmart.com/sainsmart-iic-i2c-twi-serial-2004-20x4-lcd-module-shield-for-arduino-uno-mega-r3.html
Default I2C setup used: LCD pins - GRD = ground
- VCC = 5V from Arduino
- SDA = Arduino A4 - This is the default pin
- SCL = Arduino A5 - This is the default pin
Warning: Be sure to have an additional power supply
connected to your Arduino. The LCD requires
enough power that it can overload the USB
hub on your computer.
Keypad library copied from http://www.rasmicro.com/Keypad.zip
Legal Stuff:
============
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/>.
Personal note:
==============
If you do something interesting with this code, expand it, or just
have a question, please email me at the address above.
I hope you find this example helpful. Enjoy.
Bob
****************************************************************/
/***************************************************************
The LiquidCrystal_I2C library has been modified. The library
used was first modified by Mike Myers on the show Lets Make It,
(www.letsmakeit.tv). One additional, convenience function was
added from Mike's modifications. The .h and .cpp files are
included with this file on GitHub.
****************************************************************/
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Keypad.h>
// Set default number of columns and rows
int COLUMNS = 20;
int ROWS = 4;
// set the LCD address to 0x27 for a 20 chars and 4 line display
// With no changes from the standard I2C setup, no extra pin
// definitions are required.
LiquidCrystal_I2C lcd(0x3F, COLUMNS, ROWS);
char BUFFER[10];
const bool FALSE = 0;
const bool TRUE = 1;
// used for debugging when using serial monitor
const bool DEBUG = FALSE;
int TRANSMISSIONS = 0;
int TOTALNUMBERS = 0;
bool F0 = FALSE;
bool F1 = FALSE;
bool F2 = FALSE;
bool F3 = FALSE;
bool ENTER = FALSE;
bool DISPLAYED = FALSE;
int STARTTIME = 0;
int ELAPSEDTIME = 0;
bool SENT = FALSE;
int ASCORE = 0;
int BSCORE = 0;
// Keypad setup
const byte KROWS = 5; //five rows
const byte KCOLS = 5; //five columns
//define the symbols on the buttons of the keypads
char hexaKeys[KROWS][KCOLS] = {
{132,'0','1','2','3'}, // Back arrow
{133,'4','5','6','7'}, // DEL (BS)
{134,'8','9','A','B'}, // Arrow
{135,'C','D','E','F'}, // Enter
{' ',128,129,130,131} // F0 to F3
};
byte rowPins[KROWS] = {7, 8, 9, 10, 11}; //connect to the row pinouts of the keypad
byte colPins[KCOLS] = {2, 3, 4, 5, 6}; //connect to the column pinouts of the keypad
//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, KROWS, KCOLS);
/* Added constants for a 7 segment, common cathode single digit
alphanumeric display. In this example, a NTE3079 was used.
The datasheet can be found at: http://www.datasheetcatalog.org/datasheet/nte/NTE3078.
pdf
The shift register is connected as:
Qa = Anode A
Qb = Anode B
Qc = Anode C
Qd = Anode D
Qe = Anode E
Qf = Anode F
Qg = Anode G
Qh = Anode D.P. (decimal point)
This quick example is for a single LED display with a single 74HC595.
The same basic idea applies for multiple LED's and displays.
Yes, a more effcient way of doing this would be with hex codes, but
I wanted to show how the pieces could be put together to create the
desired output. This code should also be included in a .h file as well.
A typical 7-Segment display is typically
A
-----
F | G | B
-----
E | | C
-----
D
So, the varible SEGA refers to segment A above, SEGB is segment B, etc.
*/
const int ALL = 255;
const int SEGA = 1;
const int SEGB = 2;
const int SEGC = 4;
const int SEGD = 8;
const int SEGE = 16;
const int SEGF = 32;
const int SEGG = 64;
const int SEGH = 128;
int SEGMENTS[] = {SEGA, SEGB, SEGC, SEGD, SEGE, SEGF, SEGG, SEGH};
const int NUM0 = SEGA + SEGB + SEGC + SEGD + SEGE + SEGF;
const int NUM1 = SEGB + SEGC;
const int NUM2 = SEGA + SEGB + SEGD + SEGE + SEGG;
const int NUM3 = SEGA + SEGB + SEGC + SEGD + SEGG;
const int NUM4 = SEGB + SEGC + SEGF + SEGG;
const int NUM5 = SEGA + SEGC + SEGD + SEGF + SEGG;
const int NUM6 = SEGA + SEGC + SEGD + SEGE + SEGF + SEGG;
const int NUM7 = SEGA + SEGB + SEGC;
const int NUM8 = SEGA + SEGB + SEGC + SEGD + SEGE + SEGF + SEGG;
const int NUM9 = SEGA + SEGB + SEGC + SEGD + SEGF + SEGG;
int NUMBERS[] = {NUM0, NUM1, NUM2, NUM3, NUM4 , NUM5, NUM6, NUM7, NUM8, NUM9};
// Characters based on:
// http://en.wikipedia.org/wiki/Seven-segment_display_character_representations
const int A = SEGA + SEGB + SEGC + SEGE + SEGF + SEGG;
const int B = SEGC + SEGD + SEGE + SEGF + SEGG;
const int C = SEGD + SEGE + SEGG;
const int D = SEGB + SEGC + SEGD + SEGE + SEGG;
const int E = SEGA + SEGD + SEGE + SEGF + SEGG;
const int F = SEGA + SEGE + SEGF + SEGG;
const int G = SEGA + SEGC + SEGD + SEGE + SEGF;
const int H = SEGC + SEGE + SEGF + SEGG;
const int I = SEGC;
const int J = SEGB + SEGC + SEGD + SEGE;
const int K = SEGA + SEGC + SEGE + SEGF + SEGG;
const int L = SEGD + SEGE + SEGF;
const int M = SEGA + SEGB + SEGC + SEGE + SEGF;
const int N = SEGC + SEGE + SEGG;
const int O = SEGC + SEGD + SEGE + SEGG;
const int P = SEGA + SEGB + SEGE + SEGF + SEGG;
const int Q = SEGA + SEGB + SEGD + SEGE + SEGF + SEGG;
const int R = SEGE + SEGG;
const int S = SEGA + SEGC + SEGD + SEGF + SEGG;
const int T = SEGD + SEGE + SEGF + SEGG;
const int U = SEGC + SEGD + SEGE;
const int V = SEGB + SEGC + SEGD + SEGE + SEGF;
const int W = SEGB + SEGC + SEGD + SEGE + SEGF + SEGG;
const int X = SEGB + SEGC + SEGE + SEGF + SEGG;
const int Y = SEGB + SEGC + SEGD + SEGF + SEGG;
const int Z = SEGA + SEGB + SEGD + SEGE;
const int SPACE = 0;
const int PERIOD = SEGH;
int LETTERS[] = {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z};
/**************************************************************
Function: setup
Purpose: set up Arduino
Args: none
Returns: nothing
Notes: This function is required by the Arduino
***************************************************************/
void setup(){
// Initialize the LCD
lcd.init(); // initialize the lcd
// Reset the LCD
lcd.reset();
// turn on the backlight lcd.noBacklight(); will turn it off
lcd.backlight();
// Initial screen for this demo
initialDemoScreen();
lcd.clear();
analogReference(INTERNAL);
Serial.begin(9600);
if(DEBUG)
Serial.println("Started");
}
/**************************************************************
Function: loop
Purpose: loop funtion for Arduino
Args: none
Returns: nothing
Notes: This function is required by the Arduino, and the
Arduino will loop through this function indefinately.
Only the F0 to F3 function keys, and the Enter key
are active in loop(). Any other input is ignored.
***************************************************************/
void loop()
{
// Serial variables
int idx, number, comma, comma2, total;
String inData = "";
char inChar;
bool done = FALSE;
// Create variables
int reading;
// int printDelay = 150;
char customKey;
if((ENTER) && (!DISPLAYED)){
printCentered(0, "Score:");
sprintf(BUFFER, "A: %i", ASCORE);
printLeft(1, BUFFER);
sprintf(BUFFER, "B: %i", BSCORE);
printLeft(2, BUFFER);
printCentered(3,"Press a Function");
DISPLAYED = TRUE;
}
if(F0){
enterNewScore();
}
else if(F1){
sendLetsMakeIt();
F1 = FALSE;
}
else if(F2){
transmitData(8,255);
F2 = !F2;
lcd.clear();
printCentered(1, "Displaying");
printCentered(2, "Temperature");
}
// Wait and start loop again
customKey = customKeypad.getKey();
switch (customKey) {
case (128):
F0 = TRUE;
DISPLAYED = FALSE;
break;
case (129):
F1 = !F1;
DISPLAYED = FALSE;
break;
case (130):
F2 = TRUE;
F1 = FALSE;
ENTER = FALSE;
DISPLAYED = FALSE;
break;
case (131):
lcd.clear();
DISPLAYED = FALSE;
F3 = !F3;
if(F3)
printCentered(1, "Data on");
else
printCentered(1, "Data off");
break;
case (135): // Enter
lcd.clear();
ENTER = !ENTER;
DISPLAYED = FALSE;
break;
default:
break;
}
} // end of loop
/**************************************************************
Function: enterNewScore
Purpose: Interface to add, or subtract, from a teams score.
Args: none
Returns: nothing
Notes: This could be rewritten to be more user friendly
if this is the Arduino's only function.
**************************************************************/
void enterNewScore(){
bool done = FALSE;
char customKey;
char team;
DISPLAYED = FALSE;
transmitData(1, NUMBERS[(int)ASCORE%10]);
if(((int)(ASCORE/10)%10) > 0)
transmitData(0, NUMBERS[(int)(ASCORE/10)%10]);
else
transmitData(0,0);
transmitData(9,255);
transmitData(3, NUMBERS[(int)BSCORE%10]);
if(((int)(BSCORE/10)%10) > 0)
transmitData(2, NUMBERS[(int)(BSCORE/10)%10]);
else
transmitData(2,0);
transmitData(9,255);
lcd.clear();
printLeft(0, "Team A or B?");do{
customKey = customKeypad.getKey();
if (customKey == 'A')
done = TRUE;
else if (customKey == 'B')
done = TRUE;
} while(done == FALSE);
done = FALSE;
int score;
if(customKey == 'A'){
score = ASCORE;
team = 'A';
}
else{
score = BSCORE;
team = 'B';
}
sprintf(BUFFER, "Score: %i", score);
printLeft(1, BUFFER);
printCentered(2, "Add: ==> Sub: <==");
printCentered(3, "Press Enter to Send");
do{
customKey = customKeypad.getKey();
switch (customKey) {
case (134): // ===> key
score = score++;
sprintf(BUFFER, "Score: %i", score);
printLeft(1, BUFFER);
break;
case (132): // <=== key
score = score--;
sprintf(BUFFER, "Score: %i", score);
printLeft(1, BUFFER);
break;
case (135): // Enter
done = TRUE;
break;
default:
break;
}
} while(done == FALSE);
if(team == 'A'){
ASCORE = score;
transmitData(1, NUMBERS[(int)ASCORE%10]);
if(((int)(ASCORE/10)%10) > 0)
transmitData(0, NUMBERS[(int)(ASCORE/10)%10]);
else
transmitData(0,0);
transmitData(9,255);
}
else{
BSCORE = score;
transmitData(3, NUMBERS[(int)BSCORE%10]);
if(((int)(BSCORE/10)%10) > 0)
transmitData(2, NUMBERS[(int)(BSCORE/10)%10]);
else
transmitData(2,0);
transmitData(9,255);
}
F0 = FALSE;
lcd.clear();
}
/**************************************************************
Function: transmitData
Purpose: Transmits data and waits for return of the checksum.
Args: int digit - the digit #
int number - the number to be displayed on the digit
Returns: nothing
Notes: If the XBEE radios were working right and not dropping
data, then this would not be needed. However, in
testing, between 25% and 30% of transmitted data
was being lost, so a scheme to verify good data
transmission was needed.
**************************************************************/
void transmitData(int digit, int number){
char inChar;
String inData = "";
bool goodTrans = FALSE;
int total = digit + number;
int
tf = 0;
sprintf(BUFFER, "%i,%i,%i", digit, number, total);
Serial.println(BUFFER);
char message[20];
TOTALNUMBERS++;
do{
delay(25);
if(F3){
sprintf(message, "Sent: %s", BUFFER);
printLeft(0, message );
}
inData = "";
TRANSMISSIONS++;
Serial.flush();
while(Serial.available() > 0) {
inChar = Serial.read();
inData.concat(inChar);
}
int retTotal = inData.toInt();
// Display to the lcd screen if F3 was selected
if(F3){
sprintf(message, "Checksum: %i", inData.toInt() );
printLeft(1, message);
sprintf(message, "# Trans : %i", TOTALNUMBERS);
printLeft(2, message);
sprintf(message, "# Error : %i", TRANSMISSIONS-TOTALNUMBERS);
printLeft(3, message);
}
if(retTotal == total){
goodTrans = TRUE;
}
else
Serial.println(BUFFER);
}while(goodTrans == FALSE);
}
/**************************************************************
Function: sendLetsMakeIt
Purpose: Sends "Lets Make It TV" to the display
Args: none
Returns: nothing
Notes:
**************************************************************/
void sendLetsMakeIt(){
// Display message
lcd.clear();
printCentered(1,"Sending");
printCentered(2,"Lets Make It");
printCentered(3,"Keys disabled");
transmitData(0,L);
transmitData(1,E);
transmitData(2,T);
transmitData(3,S);
transmitData(9,ALL);
delay(1000);
transmitData(0,M);
transmitData(1,A);
transmitData(2,K);
transmitData(3,E);
transmitData(9,ALL);
delay(1000);
transmitData(0,0);
transmitData(1,I);
transmitData(2,T);
transmitData(3,0);
transmitData(9,ALL);
delay(1000);
transmitData(0,0);
transmitData(1,T);
transmitData(2,V);
transmitData(3,0);
transmitData(9,ALL);
delay(1000);
transmitData(0,0);
transmitData(1,0);
transmitData(2,0);
transmitData(3,0);
transmitData(9,ALL);
// Turns off the message
lcd.clear();
}
/**************************************************************
Function: initialDemoScreen
Purpose: Initial message for this demo
Args: none
Returns: nothing
Notes:
**************************************************************/
void initialDemoScreen(){
lcd.clear();
printCentered(0, "LCD XBEE Demo");
printCentered(2, "As seen on:");
printCentered(3, "www.letsmakeit.tv");
delay(5000);
lcd.clear();
printLeft(0,"Code is at:");
printCentered(1, "www.github.com/");
printCentered(2, "texanfromiowa");
printRight(3,"or the show notes.");
delay(5000);
}
/**************************************************************
Function: printCentered
Purpose: Centers text on a given row
Args: int row - which row to display text
String string - text to display
Returns: nothing
Notes:
**************************************************************/
void printCentered(int row, String string){
//int columns = 20;
int strLen = string.length();
int position = (int)((COLUMNS - strLen)/2);
clearRow(row);
lcd.setCursor(position, row);
lcd.print(string);
}
/**************************************************************
Function: printRight
Purpose: Right justifies text
Args: int row - which row to display text
String string - text to display
Returns: nothing
Notes:
**************************************************************/
void printRight(int row, String string){
int strLen = string.length();
int position = COLUMNS - strLen;
clearRow(row);
lcd.setCursor(position, row);
lcd.print(string);
}
/**************************************************************
Function: printLeft
Purpose: Left justifies text
Args: int row - which row to display text
String string - text to display
Returns: nothing
Notes:
**************************************************************/
void printLeft(int row, String string){
clearRow(row);
lcd.setCursor(0, row);
lcd.print(string);
}
/**************************************************************
Function: clearRow
Purpose: clears a row
Args: int row - which row to clear
Returns: nothing
Notes:
**************************************************************/
void clearRow(int row){
lcd.setCursor(0, row);
for(int i = 0;i < COLUMNS;i++){
lcd.print(" ");
}
}
/**************************************************************
Function: sdelay
Purpose: delay function that accounts for timer interupts
Args: int msec - number of milliseconds of delay
Returns: none
Notes: This function was created because while testing,
timer interupts were effecting the delays.
**************************************************************/
void sdelay(int msec)
{
if(msec > 15){
long time = millis();
long delaytime = time + msec;
long timenow = millis();
while(timenow <= delaytime)
timenow = millis();
}
else{
msec = msec * 1000;
long time = micros();
long delaytime = time + msec;
long timenow = micros();
while(timenow <= delaytime)
timenow = micros();
}
}
LCD_XBEE_Keypad_4Digit_Home - Remote radio code:
/**************************************************************
Name XBEE_4Digit_Receiver
Author Bob Powell
texanfromiowa@gmail.com
Copyright (C) 2012-2014, Parallelus Automation, Inc.
Date Febuary 3, 2014
Modified Febuary 3, 2014
Version 1.0.0
Arduino 1.0.5
Notes This example receives data from the host radio, and displays the
appropriate data on the four 7-segment displays, using 74HC595
shift-registers, that are attached.
The program expect one of several codes. Codes 0 to 3 are for the
four displays, 8 turns on the temperature display from the LM35
sensor that is attached, and a 9 sends the current data out the
shift-registers.
Other than the codes, this Arduino does nothing but send data
to the displays.
Legal Stuff:
============
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/>.
The comments are shortened in a number of places since all of this
has been demonstated in previous Lets Make It episodes. However,
you should be able to follow what is happening.
Personal note:
==============
If you do something interesting with this code, expand it, or just
have a question, please email me at the address above.
I hope you find this example helpful. Enjoy.
Bob
****************************************************************/
#include <Shifter.h>
const bool TRUE = 1;
const bool FALSE = 0;
// Used for debugging the code when connected to the serial monitor
const bool DEBUG = FALSE;
const int NUM_REG = 4; // number of shift registers
// Pin connected to RCLK, pin 12, of ALL the 74HC595s
// ===
const int latchPin = 10;
// Pin connected to SRCLK, pin 11, of ALL the 74HC595s
// ===
const int clockPin = 11;
// Pin connected to SER, pin 14, of the FIRST 74HC595s
// =====
// All of the serial data will be going to this SER (data in)
// pin to all the register(s) you have connected.
const int dataPin = 12;
// Shifter constructor
Shifter shifter(dataPin, latchPin, clockPin, NUM_REG);
// LM35 input pin
int lm35Pin = A1;
bool DISPLAYTEMP = FALSE;
// These numbers are needed to display the temp from the LM35
const int SEGA = 1;
const int SEGB = 2;
const int SEGC = 4;
const int SEGD = 8;
const int SEGE = 16;
const int SEGF = 32;
const int SEGG = 64;
const int SEGH = 128;
int SEGMENTS[] = {SEGA, SEGB, SEGC, SEGD, SEGE, SEGF, SEGG, SEGH};
const int NUM0 = SEGA + SEGB + SEGC + SEGD + SEGE + SEGF;
const int NUM1 = SEGB + SEGC;
const int NUM2 = SEGA + SEGB + SEGD + SEGE + SEGG;
const int NUM3 = SEGA + SEGB + SEGC + SEGD + SEGG;
const int NUM4 = SEGB + SEGC + SEGF + SEGG;
const int NUM5 = SEGA + SEGC + SEGD + SEGF + SEGG;
const int NUM6 = SEGA + SEGC + SEGD + SEGE + SEGF + SEGG;
const int NUM7 = SEGA + SEGB + SEGC;
const int NUM8 = SEGA + SEGB + SEGC + SEGD + SEGE + SEGF + SEGG;
const int NUM9 = SEGA + SEGB + SEGC + SEGD + SEGF + SEGG;
int NUMBERS[] = {NUM0, NUM1, NUM2, NUM3, NUM4 , NUM5, NUM6, NUM7, NUM8, NUM9};
char BUFFER[20];
/**************************************************************
Function: setup
Purpose: set up Arduino
Args: none
Returns: nothing
Notes: This function is required by the Arduino
***************************************************************/
void setup() {
// Set up the output pins.
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
// Set up the shifter output
shifter.clear();
shifter.write();
Serial.begin(9600);
if (DEBUG)
Serial.println("Started");
analogReference(INTERNAL);
}
/**************************************************************
Function: loop
Purpose: loop funtion for Arduino
Args: none
Returns: nothing
Notes: This function is required by the Arduino, and the
Arduino will loop through this function indefinately.
***************************************************************/
void loop() {
// Serial variables
int idx, number, comma, comma2, total;
String inData = "";
char inChar;
int reading;
// For a more precise display, use floats for the temperature variables,
// or use the int for a display with whole numbers.
//int tempinC;
//int tempinF;
float tempinC;
float tempinF;
// Skip past this code if the temp is not being displayed.
if (DISPLAYTEMP){
// Read the input pin and calculate temps
reading = analogRead(lm35Pin);
// The LM35 is calibrated for C, so we calculate temp in C
// Note: Since this demo using the basic setup of the sensor,
// 0 to 110 degrees, this is the calculation needed.
// For setups with greater range, different calculations
// are needed.
tempinC = reading / 9.31;
// Convert C to F
tempinF = ((tempinC*9)/5) + 32 ;
displayTemp(tempinF);
shifter.write();
// If the temp data is being sent back to the home radio,
// then the code could be added here.
}
// Get the incoming string
while(Serial.available()) {
inChar = Serial.read();
inData.concat(inChar);
}
// The incoming data should be in this format:
// index,number,total
// In this example, the serial data will not be
// any longer than 9 bytes.
comma = inData.indexOf(',');
comma2 = inData.indexOf(',',comma+1);
// indexOf return a -1 if it fails, otherwise it
// returns the location of the first comma.
// If the data is bad, we ignore it.
if(comma != -1){
idx = inData.substring(0, comma).toInt();
number = inData.substring(comma+1, comma2).toInt();
total = inData.substring(comma2+1, inData.length()).toInt();
if (DEBUG){
Serial.println(inData);
sprintf(BUFFER, "comma = %i", comma);
Serial.println(BUFFER);
sprintf(BUFFER, "comma2 = %i", comma2);
Serial.println(BUFFER);
sprintf(BUFFER, "idx = %i", idx);
Serial.println(BUFFER);
sprintf(BUFFER, "number = %i", number);
Serial.println(BUFFER);
sprintf(BUFFER, "total = %i", total);
Serial.println(BUFFER);
}
// In this example, there are four shift registers connected to
// four 7 segment displays. This means that only index numbers
// from 0 to 3 and numbers between 0 and 255 are valid. All
// other values are rejected.
if((idx >= 0) && (idx <= NUM_REG-1) && (number >= 0) && (number <=255) ){
if((idx+number) == total){
DISPLAYTEMP = FALSE;
shifter.setDigit(idx, number);
Serial.println(total);
Serial.flush();
}
else
Serial.println(FALSE);
}
// A 8 indicates that the current temp be displayed.
// It will stay displayed until other data is sent.
else if ((idx == 8) && (number == 255)){
if((idx+number) == total){
DISPLAYTEMP = TRUE;
displayTemp(tempinF);
shifter.write();
Serial.println(total);
Serial.flush();
}
else
Serial.println(FALSE);
}
// A 9 indicates that the data held in Shifter is written.
else if ((idx == 9) && (number == 255)){
if((idx+number) == total){
shifter.write();
Serial.println(total);
Serial.flush();
}
else
Serial.println(FALSE);
}
// The code above can easily be rewritten to send a pin number and a boolean (on or off)
// the rather than shifter.setDigit, the shifter.setPin method could be used.
}
// We shouldn't need this, but it runs better with it.
delay(25);
} // End of Arduino Loop
void displayTemp(float temp){
// Since the LM35 reads from 0 to 110 degrees,
// this code assumes that the lowest reading is at
// least zero (no negative numbers) and that there
// will never be a reading of 1000 in the first digit.
shifter.setDigit(3, NUMBERS[(int)temp%10]);
if(temp >= 10)
shifter.setDigit(2, NUMBERS[(int)(temp/10)%10]);
else
shifter.setDigit(2, 0);
if(temp >= 100)
shifter.setDigit(1, NUMBERS[(int)(temp/100)%10]);
else
shifter.setDigit(1, 0);
shifter.setDigit(0, 0);
}