Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Exact code demoed at 2014 Seattle Mini Maker Faire
- Loading branch information
zapmaker
committed
Apr 5, 2014
1 parent
6d2b6eb
commit 4782f80
Showing
3 changed files
with
322 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
Copyright 2014 zapmaker | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,43 @@ | ||
track_controller_mega | ||
===================== | ||
---- Train Track Controller --- | ||
|
||
Software to control and detect toy train track conditions using an Arduino Mega 2560 | ||
Software to control and detect toy train track conditions using an Arduino Mega 2560. | ||
|
||
A Mega is used to interface with the large number of track inputs and outputs, although | ||
any Arduino should be capable with the right external interfaces. | ||
|
||
As it stands, the track is a O-27 gauge track with two lines that connect together | ||
into a short straight track, i.e.: | ||
|
||
/------\ | ||
| | | ||
/----\ | | ||
| | | | ||
\----/ | | ||
| | | ||
\------/ | ||
|
||
The two points, or switches allow a train to change lines easily. Each line has | ||
six relays to control the power to a portion of a the track. Additionally, each | ||
approach to the points (of which there are four), has two sensors to detect a | ||
train approaching and determine its direction, for a total of eight analog inputs. | ||
The initial version of this software only displays the analog values, it does | ||
nothing with them. | ||
|
||
Speaking of displays - the design relies on a 2x16 LCD display for informing the | ||
user about the state of the system and displays prompts for commands. | ||
|
||
It also uses a 4x4 keyboard matrix to accept user input. The normal input scenario | ||
is to switch the points, thus a keyboard input of 2 followed by an up arrow would | ||
move the point 2 servo to switch the track so the train can go on the upper (outside) | ||
track. The red 2nd Function button turns on the entire track and the clear button | ||
turns off all the track. A number followed by either of those buttons enables | ||
or disables that track segment of which there are 12. | ||
|
||
Various libraries are used to provide the needed functionality. | ||
|
||
First checked-in code represents the same code used to demonstrate the | ||
train at the 2014 Seattle Mini Maker Faire. | ||
|
||
For more info zapmaker.org | ||
|
||
Code is Apache 2.0 license. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
// zapmaker.org (c) zapmaker 2014 | ||
// Apache 2.0 license | ||
#include <Keypad.h> | ||
#include <LiquidCrystal.h> | ||
#include <Servo.h> | ||
|
||
// initialize the library with the numbers of the interface pins | ||
LiquidCrystal lcd(4, 5, 6, 7, 8, 9); | ||
|
||
const byte ROWS = 4; //four rows | ||
const byte COLS = 4; //three columns | ||
char keys[ROWS][COLS] = { | ||
{'1','2','3','U'}, | ||
{'4','5','6','D'}, | ||
{'7','8','9','S'}, | ||
{'C','0','H','E'} | ||
}; | ||
byte rowPins[ROWS] = {36, 34, 32, 30}; //connect to the row pinouts of the keypad | ||
byte colPins[COLS] = {28, 26, 24, 22}; //connect to the column pinouts of the keypad | ||
|
||
#define MENU_START 0 | ||
#define MENU_WAIT_FOR_ENTER 1 | ||
|
||
#define MENU_INPUT_SIZE 10 | ||
char menuInput[MENU_INPUT_SIZE]; | ||
int menuInputPos = 0; | ||
int menuState = MENU_START; | ||
|
||
int lastAdcTime = 0; | ||
int servoAPin = 2; | ||
int servoBPin = 3; | ||
Servo servoA; | ||
Servo servoB; | ||
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); | ||
int relayPinStart = 31; | ||
boolean analogReadMode = false; | ||
|
||
void setup(){ | ||
Serial.begin(9600); | ||
// set up the LCD's number of columns and rows: | ||
lcd.begin(16, 2); | ||
// Print a message to the LCD. | ||
lcd.print("Train Controller"); | ||
lcd.setCursor(0, 1); | ||
lcd.print("zapmaker.org"); | ||
|
||
servoA.attach(servoAPin); | ||
servoB.attach(servoBPin); | ||
|
||
setServo(1, false); | ||
setServo(2, false); | ||
int pin = relayPinStart; | ||
for (int i = 0; i < 12; i++) | ||
{ | ||
pinMode(pin, OUTPUT); | ||
digitalWrite(pin, LOW); | ||
pin += 2; | ||
} | ||
|
||
delay(3000);// for sign on msg | ||
lcd.clear(); | ||
lcd.setCursor(0, 0); | ||
lcd.print("Waiting for cmd"); | ||
} | ||
|
||
|
||
void loop() | ||
{ | ||
int currtime = millis(); | ||
char key = keypad.getKey(); | ||
|
||
if (key) | ||
{ | ||
menuProcessor(key); | ||
} | ||
|
||
|
||
if (analogReadMode) | ||
{ | ||
if ((currtime - lastAdcTime) > 200) | ||
{ | ||
lcdClearLine(0); | ||
lcdClearLine(1); | ||
for (int i = 1; i <= 4; i++) | ||
{ | ||
int val = analogRead(i); | ||
lcd.setCursor((i - 1) * 4, 0); | ||
lcd.print(val); | ||
} | ||
for (int i = 5; i <= 8; i++) | ||
{ | ||
int val = analogRead(i); | ||
lcd.setCursor((i - 5) * 4, 1); | ||
lcd.print(val); | ||
} | ||
lastAdcTime = currtime; | ||
} | ||
} | ||
} | ||
|
||
|
||
void menuProcessor(char key) | ||
{ | ||
switch (menuState) | ||
{ | ||
case MENU_START: | ||
if (key == 'C') | ||
{ | ||
if (menuInputPos == 0) | ||
{ | ||
allRelays(LOW); | ||
lcdPrintLine(0, "All relays off"); | ||
} | ||
else | ||
{ | ||
int val = decodeMenuValue(); | ||
oneRelay(val, LOW); | ||
lcdPrintLine(0, "One relay off"); | ||
menuInputPos = 0; | ||
} | ||
} | ||
else if (key == 'S') | ||
{ | ||
if (menuInputPos == 0) | ||
{ | ||
allRelays(HIGH); | ||
lcdPrintLine(0, "All relays ON"); | ||
} | ||
else | ||
{ | ||
int val = decodeMenuValue(); | ||
oneRelay(val, HIGH); | ||
lcdPrintLine(0, "One relay ON"); | ||
menuInputPos = 0; | ||
} | ||
} | ||
else if (key == 'U') | ||
{ | ||
if (menuInputPos > 0) | ||
{ | ||
int val = decodeMenuValue(); | ||
setServo(val, true); | ||
if (val == 1) | ||
lcdPrintLine(0, "Servo Left Up"); | ||
else | ||
lcdPrintLine(0, "Servo Right Up"); | ||
menuInputPos = 0; | ||
} | ||
} | ||
else if (key == 'D') | ||
{ | ||
if (menuInputPos > 0) | ||
{ | ||
int val = decodeMenuValue(); | ||
setServo(val, false); | ||
if (val == 1) | ||
lcdPrintLine(0, "Servo Left Down"); | ||
else | ||
lcdPrintLine(0, "Servo Right Down"); | ||
menuInputPos = 0; | ||
} | ||
} | ||
else if (key >= '0' && key <= '9') | ||
{ | ||
if (menuInputPos < MENU_INPUT_SIZE) | ||
{ | ||
if (menuInputPos == 0) | ||
{ | ||
lcdResetLine(1); | ||
lcdPrintLine(0, "Waiting for #cmd"); | ||
} | ||
menuInput[menuInputPos] = key - '0'; | ||
lcd.setCursor(menuInputPos, 1); | ||
lcd.print(key); | ||
menuInputPos++; | ||
} | ||
} | ||
else if (key == 'H') | ||
{ | ||
if (analogReadMode) | ||
{ | ||
lcdResetLine(0); | ||
lcdResetLine(1); | ||
lcdPrintLine(0, "Waiting for cmd"); | ||
|
||
analogReadMode = false; | ||
} | ||
else | ||
analogReadMode = true; | ||
} | ||
|
||
break; | ||
} | ||
|
||
} | ||
int decodeMenuValue() | ||
{ | ||
if (!menuInputPos) | ||
return 0; | ||
int result = 0; | ||
int multiplier = 1; | ||
for (int i = (menuInputPos - 1); i >= 0; i--) | ||
{ | ||
result += menuInput[i] * multiplier; | ||
multiplier *= 10; | ||
} | ||
return result; | ||
} | ||
void setServo(int servo, boolean pos) | ||
{ | ||
if (servo < 1 || servo > 2) | ||
{ | ||
lcdPrintLine(0, "Servos 1 and 2 only"); | ||
return; | ||
} | ||
|
||
int val = pos ? 1 : 0; | ||
|
||
int mapResult = 0; | ||
if (servo == 1) | ||
{ | ||
mapResult = map(val, 0, 1, 0, 171); | ||
servoA.write(mapResult); | ||
} | ||
else | ||
{ | ||
mapResult = map(val, 0, 1, 173, 2); | ||
servoB.write(mapResult); | ||
} | ||
|
||
} | ||
void oneRelay(int index, int value) | ||
{ | ||
if (index < 1 || index > 12) | ||
{ | ||
lcdPrintLine(0, "Relays 1-12 only"); | ||
return; | ||
} | ||
|
||
int pin = relayPinStart + 2 * (index - 1); | ||
digitalWrite(pin, value); | ||
} | ||
void allRelays(int value) | ||
{ | ||
int pin = relayPinStart; | ||
for (int i = 0; i < 12; i++) | ||
{ | ||
digitalWrite(pin, value); | ||
pin += 2; | ||
} | ||
} | ||
void lcdPrintLine(int line, char *p) | ||
{ | ||
lcdResetLine(line); | ||
lcd.print(p); | ||
} | ||
void lcdResetLine(int line) | ||
{ | ||
lcdClearLine(line); | ||
lcd.setCursor(0, line); | ||
} | ||
void lcdClearLine(int line) | ||
{ | ||
lcd.setCursor(0, line); | ||
lcd.print(" "); | ||
} | ||
|