The “Oh God, Something Happened” Light

It's not pink in real life, the camera just makes it look that way.

If you’ve ever watched the original series of Star Trek, you’ve probably noticed that whenever something bad happens – an enemy ship appears, the ship gets damaged, the transporter malfunctions – a big, red light on the nearest panel starts flashing. It’s not labelled, it’s not a tiny screen that says what’s wrong; it’s just a big, red, panic light. Well after watching a few episodes of the original series, I felt compelled to build an “Oh God: Something Happened!” light (or, as I prefer to call it, the “Oh Sh&$! light”) of my own. I’ll admit that it’s not very useful, but that’s not the point, is it? The point is to know when to panic.

You’ll need several things for this project. First, an Arduino, and the IDE that goes with it. Second, a computer running Unix and access to a Bash shell. And finally, a red LED, and some sort of covering for your panic light. I used some thin white paper that I found. Tracing paper would probably work too. You might also want some sort of thin paper or tissue to put over the LED if it has a clear lens.

First let’s look at the Arduino code. I won’t cover the details here, since I’ve commented it fairly well, but here’s how it basically works. In the setup section, we define the pin mode of the light_pin, and open the serial connection. When you first connect to the Arduino, since I used pin 13, the panic light will flash a few times. If you don’t want this behaviour, set light_pin to something else (like 11.) We have to flush the serial buffer, because there seem to be odd troubles if we don’t. We also print “Ready…” to the serial line for debugging; it doesn’t affect the function of the light. In the loop, we first check for new serial data. If there is any, we’ll read it into a temporary char variable, and then add it to the panic_status variable. Then we check to see if the panic_status variable says “PANIC” which is clearly the signal to panic, or “Stop” which is the signal to stop panicking, and set the panicking boolean accordingly. This statement also sets the last_read variable to the current number of milliseconds since the Arduino was turned on, so that in the next statement, if we haven’t received a new character in 500ms, we can empty panic_status. This is mostly for human error or connection errors, so that if an incomplete signal is sent to the Arduino, it won’t just keep adding to the panic_status and get confused. For example, if you accidentally sent “PANI”, realized your mistake and then sent “PANIC”, you wouldn’t want the panic_status to be “PANIPANIC” because it wouldn’t do anything. The last statement will check to see if panicking is true, and then if it’s been more than 700 ms since the light was last toggled. If it has, then it will turn the light on if the on variable is true, off if it’s false, and toggle the on variable. If we’re not panicking, the code turns the light off to be sure that it’s turned off after we’ve been panicking.

Now the interesting part: the bash script. This is also pretty well commented, but in case you’re not familiar with bash scripting, I’ll cover this in a bit more detail. In the first statement after the shebang line, we set file descriptor 3 to point to /dev/ttyUSB0. Huh-duh-wuh? In bash there are these things called file descriptors, which you can use to pipe input from places and output to places. For example, file descriptor 0 pipes from /dev/stdin, 1 to /dev/stdout, and 2 to /dev/stderr, which is why you can pipe stderr to a file with 2>. If you setup a third file descriptor to point to /dev/ttyUSB0, you can send stdout to /dev/ttyUSB0 using 1>&3. To open the file descriptor do we use the command:

exec 3>/dev/ttyUSB0

There are two reasons for using a file descriptor. First, it enables the user to change the port very easily; by simply changing one line to point to whatever device to which you want to write. Second, when you open a file descriptor, bash opens the file and keeps it open until you close the file descriptor, which is important because the Arduino will reset every time a serial connection is made or broken, and if the file weren’t held open by the file descriptor, bash would open the connection, write to it, and close it again, causing the Arduino to reset, panic for a few milliseconds, and then reset again. Not very useful.

Next the script sleeps for seven seconds to allow the Arduino to do what it needs to do and calm down. The bootloader does a lot of things when the Arduino resets, and they take time. We can wait. The third line does whatever you want the script to do. In this case, I used chmod because I know it will generate errors without any arguments. Ideally you would put something more useful here; maybe a script that compiles something like easy_e17.sh. Unfortunately, as the large comment in the middle of the script says, there appears to be no way to pipe stderr to a variable without having stdout go to the variable as well. So we have to pipe stderr to a temporary file, read it after we’re finished, and then delete the file. This should work fairly well, unless a program generates errors but does not terminate. If I find a way around this, I’ll post it.

If the error variable isn’t empty, then something went wrong, and we panic. The “PANIC” signal is sent to the Arduino, and then we use the read command to wait for input. The panic light will keep flashing until you press enter. When you do, the “Stop” signal will be sent, the file descriptor will be closed, and any errors that were caught will be reported to you.

I glossed over the Arduino setup because it's really simple: one leg of the LED in header 13, the other in ground. Done.

I used a piece of tracing paper to diffuse the light from the LED. Despite the appearance, it works quite well.

Note: For some reason, I’ve noticed that the panic light will skip every so often – it’ll stay on too long, or turn off two early. I’m assuming that this is caused by the way I wrote the blinking code, and that something in the statement is messing up the timing. If I had more time to fuss with it, I’d probably use the Watchdog Timer to create interrupts and trigger the blinking that way, but that has it’s own problems, and I don’t have time to mess with it right now. As it is, the light works quite well.
Unfortunately, I can’t figure out a good way to provide a link to the source for this project, and I can’t get my McGill student page working, so I’ll have to just post the code in plaintext here. Once I figure everything out I’ll delete it and post a real link. Suggestions are welcome.

Arduino sketch
/*
Panic Light Source Code.
by Peter Davoust (worldgnat@gmail.com)
For details, please see:
https://worldgnat.wordpress.com/2011/01/24/the-oh-god-som…happened-light/
*/
String panic_status;
char inchar;
boolean panicking;
boolean on;
int light_pin;
unsigned long last_millis;
unsigned long last_read;

void setup() {
//Open the serial connection
Serial.begin(9600);

//Setup the LED pin.
light_pin=13;
pinMode(light_pin, OUTPUT);

panic_status = "";
panicking = false;
last_millis = millis();

//Transmit that we're ready. This code isn't used, it's just useful for debugging.
Serial.println("Ready...");
//If we don't flush the Serial buffer, it creates some weird garbage.
Serial.flush();
}

void loop() {
//If anything has been transmitted, let's do something with it.
if (Serial.available() > 0) {
inchar=Serial.read();
if (inchar != '\n') panic_status+=inchar; //Return characters ar garbage; get rid of them.

if (panic_status.trim().equals("PANIC")) {
//We've been told to panic, so let's panic!
panicking = true;
//The following are for debugging purposes
//Serial.print("I will ");
//Serial.println(panic_status);
//Reset the panic status
panic_status="";
}
else if (panic_status.trim().equals("Stop")) {
//We've been told to stop panicking, so let's stop
panicking = false;
//The following are for debugging purposes
//Serial.print("I will ");
//Serial.println(panic_status);
//Reset the panic status
panic_status="";
}
//Record when we received the last character; it'll be useful later
last_read = millis();
}
//If it's been more than 500ms since we got the last character, reset the panic status
if (millis()-last_read > 500) {
panic_status="";
}

if (panicking) {
//If it's been more than 700 ms since we toggled the light, toggle it
if (abs((millis() - last_millis)) >= 700) {
if (on) digitalWrite(13, LOW);
else digitalWrite(13, HIGH);
on = !on;
last_millis = millis();
}
}
else digitalWrite(light_pin, LOW); //Make sure that the light is off if we're not panicking
}

Bash Script:
#!/bin/bash
exec 3>/dev/ttyUSB0
echo "We'll begin in 7 seconds."
#We have to wait for the Arduino to start up and stuff
sleep 7
echo "Running..."
#perform some operation
chmod 2> /tmp/oslighttmperr

#AAAAHHHHH!!!! Apparently there is no way to pipe stderr to a
#variable and stdout to... stdout!! This is outrageous!!!
#We have to pipe stderr to the tmp directory and read it later.
#NOT the most elegant solution.

#recover the saved errors
error=$(&3 #Send the signal to PANIC!
echo "Something went wrong! Panicking! Press ENTER to stop."
read
echo "Stop" >&3 #Enough panicking; let's rest.
fi

#We've finished whatever task we need to do, let's quit
exec 3>&- #Close the file descriptor, thereby closing the connection to the Arduino.
echo "Done."
[[ error ]] && echo -e "The error was:\n$error" #If there were errors, display them.

Advertisements

One thought on “The “Oh God, Something Happened” Light

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s