wordclock/wordclock/wordclock.ino

872 lines
23 KiB
C++

#include <stdio.h>
#include <string.h>
#include <DS1302.h> // I use the library that Matt Sparks created
#include <avr/interrupt.h>
#include <avr/io.h>
#include <math.h>
#include <pt.h>
#include "settings.h"
// uncomment the following to speed up the timer for testing
//#define TESTMODE
// Insert function prototypes -- Paul
void ledsoff(void);
void setup(void);
void SWversion(void);
/**************************************************************************
* *
* W O R D C L O C K - A clock that tells the time using words. *
* *
* Hardware: Arduino Dumelove with a set of individual LEDs under a word *
* stencil. *
* *
* Copyright (C) 2009 Doug Jackson (doug@doughq.com) *
* *
***************************************************************************
* *
* 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 2 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, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
* MA 02111-1307 USA *
* *
***************************************************************************
*
* Revision History
*
* Date By What
* 20010205 DRJ Initial Creation of Arduino Version
* - based on Wordclock.c - from PIC version
* 20100428 DRJ Added support for DS1302 RTC Chip - Code verifies
* device is present - and if so, uses it for timekeeping
* 20100511 DRJ Added ability to detect old vs new Arduino boards
* New board uses diferent time button sense.
* 20100511 DRJ Minute LED support on Analog5-1 pins.
* 20100927 DRJ Added brightness support by enabling interrupts and
* using an ISR to control the display
*/
#define INIT_TIMER_COUNT 6
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT
// there are only 20 brightness levels and 1024 lightsensor levels -> we need to
// scale the brightness levels to something closer to avoid having to work with
// floating point numbers
int hour=12, minute=0, second=0;
// incremented the second counter
int count;
int selftestmode; // 1 = in self test - flash display
int DS1302Present=0; // flag to indicate that the 1302 is there.. 1 = present
char Display1=0, Display2=0, Display3=0, Led1=0, Led2=0, Led3=0, Led4=0;
int OldHardware = 0; // 1 = we are running on old hardware
int BTNActive = 1; // the sense of the button inputs (Changes based on hardware type)
int timercount=10; // used for interrupt counting to determine the brightnes of the display
// hardware constants
int LEDClockPin=5; // Arduino Pin#11 - 4094 Pin 3 clock
int LEDDataPin=3; // Arduino Pin#5 - 4094 pin 2 Data
int LEDStrobePin=4; // Arduino Pin#6 - 4094 pin 1 Strobe
// buttons
#define FWD_BUTTON_PIN 6
#define REV_BUTTON_PIN 7
#define BUTTON_PORT PIND
#define FWD_BUTTON_MASK (1 << 6)
#define REV_BUTTON_MASK (1 << 7)
// delay in ms before repeating key
#define BUTTON_DELAY_LONG 1000
// delay in ms before repeating key a second time
#define BUTTON_DELAY_SHORT 200
// length of buffer where we receive data over uart
#define INPUTBUFFER_LENGTH 16
// 1302 RTC Constants
int DS1302IOPin=10;
int DS1302CEPin=8;
int DS1302CLKPin=9;
// Minute LED Pins
int LED1PIN=19; // Arduino analog 5
int LED2PIN=18; // Arduino analog 4
int LED3PIN=17; // Arduino analog 3
int LED4PIN=16; // Arduino analog 2
int current_brightness=0;
char ambient_light_to_brightness[1024];
String inputBuffer = ""; // input buffer for serial port
volatile boolean inputBufferComplete = false;
// define the language to be used for this project:
#include LANGUAGE // The language pack
/* Create buffers */
char buf[50]; // time output string for debugging
// create an object that talks to the RTC
DS1302 rtc(DS1302CEPin, DS1302IOPin, DS1302CLKPin);
// ProtoThread structures (one for each thread)
static struct pt blink_thread_pt, lightsensor_thread_pt, buttons_thread_pt,
uart_thread_pt, wordclock_thread_pt;
void print_DS1302time()
{
#if (PRINT_DEBUG_LEVEL <= 0)
/* Get the current time and date from the chip */
Time t = rtc.time();
/* Format the time and date and insert into the temporary buffer */
snprintf(buf, sizeof(buf), "D: DS1302 time: %02d:%02d:%02d",
t.hr, t.min, t.sec);
/* Print the formatted string to serial so we can see the time */
Serial.println(buf);
#endif
}
void setup()
{
int n;
// a, b, c and e are needed to compute lookup table for ambient light sensor
float a, b, c, e;
// initialise the hardware
// initialize the appropriate pins as outputs:
pinMode(LEDClockPin, OUTPUT);
pinMode(LEDDataPin, OUTPUT);
pinMode(LEDStrobePin, OUTPUT);
pinMode(FWD_BUTTON_PIN, INPUT);
pinMode(REV_BUTTON_PIN, INPUT);
// setup 1302
pinMode(DS1302IOPin, OUTPUT);
pinMode(DS1302CEPin, OUTPUT);
pinMode(DS1302CLKPin, OUTPUT);
// Minute LEDS
pinMode(LED1PIN, OUTPUT);
pinMode(LED2PIN, OUTPUT);
pinMode(LED3PIN, OUTPUT);
pinMode(LED4PIN, OUTPUT);
//analogReference(DEFAULT);
analogReference(INTERNAL);
//analogReference(EXTERNAL);
// known 'bug' in AVR ADC: after switching analog reference, first sample
// could be corrupt and should be ignored --> discard this sample now
analogRead(LIGHTSENSOR_INPUTPIN);
current_brightness=MAXBRIGHTNESS;
//Serial.begin(9600); // setup the serial port to 9600 baud
Serial.begin(115200); // setup the serial port to 115200 baud
inputBuffer.reserve(INPUTBUFFER_LENGTH);
SWversion(); // Display the version number of the software
// test whether the DS1302 is there
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.print("D: Verifying DS1302 ");
#endif
// start by verifying that the chip has a valid signature
if (rtc.read_register(0x20) == 0xa5) {
// Signature is there - set the present flag and mmove on
DS1302Present=1;
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("present - Valid Signature");
#endif
rtc.write_protect(false);
rtc.halt(false);
}
else
{
// Signature isnt there - may be a new chip -
// do a write to see if it will hold the signature
rtc.write_register(0x20,0xa5);
if (rtc.read_register(0x20) == 0xa5) {
// We can store data - assume that it is a new chip that needs initialisation
// Start by turning off write protection and clearing the clock halt flag.
rtc.write_protect(false);
rtc.halt(false);
// Make a new time object to set the date and time
Time t(2010, 04, 28, 10, 19, 00, 01);
// Set the time and date on the chip
rtc.time(t);
// set the DS1302 present flag
DS1302Present=1;
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("present - new chip initialised.");
#endif
}
else
{
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("absent");
#endif
}
}
// compute ambient light level to brightness level mapping
a = LIGHTSENSOR_ALPHA1 * LIGHTSENSOR_TOP / log(LIGHTSENSOR_ALPHA2 /
LIGHTSENSOR_ALPHA1);
b = LIGHTSENSOR_ALPHA1 / a;
c = MINBRIGHTNESS - a;
e = 2.718281828459045297;
for (n = 0; n < sizeof(ambient_light_to_brightness); n++)
{
if (n <= LIGHTSENSOR_BOTTOM)
ambient_light_to_brightness[n] = MINBRIGHTNESS;
else if (n >= LIGHTSENSOR_TOP)
ambient_light_to_brightness[n] = MAXBRIGHTNESS;
else
{
ambient_light_to_brightness[n] =
round(((float) a) * pow(e, ((float) b) *
((float) n)) + (float) c);
if (ambient_light_to_brightness[n] < MINBRIGHTNESS)
ambient_light_to_brightness[n] = MINBRIGHTNESS;
if (ambient_light_to_brightness[n] > MAXBRIGHTNESS)
ambient_light_to_brightness[n] = MAXBRIGHTNESS;
}
}
// determine whether we are running on old or new hardware
// old hardware tied the push buttons to ground using 4k7 resistors
// and relied on the buttons to pull them high
// new hardware uses internal pullups, and uses the buttons
// to pull the inputs down.
digitalWrite(FWD_BUTTON_PIN,HIGH); // Turn on weak pullups
digitalWrite(REV_BUTTON_PIN,HIGH); // Turn on weak pullups
OldHardware=0;
if ( digitalRead(FWD_BUTTON_PIN)==0 && digitalRead(REV_BUTTON_PIN)==0)
{
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("D: Detected Old Hardware");
#endif
OldHardware=1; // we have old hardware
BTNActive = 1; // True = active for old hardware
digitalWrite(FWD_BUTTON_PIN,LOW); // Turn off weak pullups
digitalWrite(REV_BUTTON_PIN,LOW); // Turn off weak pullups
}
else
{
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("D: Detected New Hardware");
#endif
OldHardware=0; // we have new hardware
BTNActive = 0; // True = active for old hardware
}
// Initialise Timer2: Timer Prescaler /64,
TCCR2A = 0;
TCCR2B |= (1<<CS22);
TCCR2B &= ~((1<<CS21) | (1<<CS20));
// Use normal mode
TCCR2B &= ~((1<<WGM21) | (1<<WGM20));
// Use internal clock - external clock not used in Arduino
ASSR |= (0<<AS2);
//Timer2 Overflow Interrupt Enable
TIMSK2 |= 1<<TOIE2;
RESET_TIMER2;
sei();
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("D: Finished setting up Timer2 Interrupt");
#endif
#if (SKIPSELFTEST != 1)
selftest(); // validate the hardware for the user
#endif
selftestmode=0;
if (DS1302Present==1) {
// Get the current time and date from the chip
Time t = rtc.time();
second=t.sec;
minute=t.min;
hour=t.hr;
}
displaytime(); // display the current time
}
void serialEvent()
{
while (Serial.available()) {
// get the new byte
char inChar = (char) Serial.read();
inputBuffer += inChar;
// 0x0A == line feet, 0x0D == carriage return
if ((inChar == 0x0A) || (inChar == 0x0D))
inputBufferComplete = true;
else
{
if (inputBuffer.length() >= INPUTBUFFER_LENGTH)
{
Serial.print("E: input buffer overflow; resetting buffer");
inputBuffer = "";
}
}
}
}
// Interrupt handler - Arduino runs at 16 Mhz, so we have 1000 Overflows per second...
// 1/ ((16000000 / 64) / 256) = 1 / 1000
ISR(TIMER2_OVF_vect) {
RESET_TIMER2;
if (timercount-- <= current_brightness)
{
// Now we write the actual values to the hardware
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, Display3);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, Display2);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, Display1);
digitalWrite(LEDStrobePin,HIGH);
//delay(2);
digitalWrite(LEDStrobePin,LOW);
digitalWrite(LED1PIN,Led1);
digitalWrite(LED2PIN,Led2);
digitalWrite(LED3PIN,Led3);
digitalWrite(LED4PIN,Led4);
}
else
{
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, 0);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, 0);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, 0);
digitalWrite(LEDStrobePin,HIGH);
//delay(2);
digitalWrite(LEDStrobePin,LOW);
digitalWrite(LED1PIN,0);
digitalWrite(LED2PIN,0);
digitalWrite(LED3PIN,0);
digitalWrite(LED4PIN,0);
}
if (timercount == 0)
timercount = N_PWM_STEPS - 1;
}
void ledsoff(void)
{
Display1=0;
Display2=0;
Display3=0;
Led1=0;
Led2=0;
Led3=0;
Led4=0;
}
void incrementtime(void)
{
// increment the time counters keeping care to rollover as required
second=0;
if (++minute >= 60) {
minute=0;
if (++hour == 25) {
hour=1;
}
}
/* // debug outputs
Serial.println();
if (DS1302Present==1) print_DS1302time();
else Serial.print("Arduino Time: " );
Serial.print(hour);
Serial.print(":");
Serial.print(minute);
Serial.print(":");
Serial.print(second);
Serial.print(" ");*/
}
void SWversion(void)
{
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("D:");
Serial.println("D: Wordclock -Arduino v3.0a - reduced brightness version");
Serial.print("D: "); Serial.print(LANGUAGE); Serial.println(" header file used");
Serial.println("D: (c)2009, 2010, 2011 Doug Jackson");
#endif
}
void process_buttons(unsigned char button_status)
{
char buttonPressed = 0, fwdButtonPressed = 0, revButtonPressed = 0, buttonStatus = 0;
static unsigned long buttonPressedMillis = 0;
static unsigned long button_delay = BUTTON_DELAY_LONG;
unsigned long millisNow = 0, new_button_delay;
if (BTNActive)
{
if ((button_status & FWD_BUTTON_MASK) > 0)
fwdButtonPressed = 1;
if ((button_status & REV_BUTTON_MASK) > 0)
revButtonPressed = 1;
}
else
{
if ((button_status & FWD_BUTTON_MASK) == 0)
fwdButtonPressed = 1;
if ((button_status & REV_BUTTON_MASK) == 0)
revButtonPressed = 1;
}
if (fwdButtonPressed && revButtonPressed)
{
selftestmode != selftestmode;
}
// test to see if both buttons are being held down
// if so - start a self test till both buttons are held
// down again.
//if ( digitalRead(FWD_BUTTON_PIN)==BTNActive && digitalRead(REV_BUTTON_PIN)==BTNActive)
if (selftestmode)
{
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("D: Selftest Mode TRUE");
#endif
for(int i=0; i<100; i++)
{
Display1=255; Display2=255; Display3=255; delay(101-i);
ledsoff();delay(101-i);
if (digitalRead(FWD_BUTTON_PIN)==1)
selftestmode=!selftestmode;
}
displaytime();
}
if (fwdButtonPressed)
{
// the forward button is down
// and it has been more than one second since we
// last looked
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("D: Forward Button Down");
#endif
incrementtime();
second++; // Increment the second counter to ensure that the name
// flash doesnt happen when setting time
if (DS1302Present==1) {
// Make a new time object to set the date and time
Time t(2010, 04, 28, hour, minute, second, 01);
// Set the time and date on the chip
rtc.time(t);
}
displaytime();
}
if (revButtonPressed)
{
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("D: Backwards Button Down");
#endif
minute--;
minute--;
second=0; // decrement the minute counter
if (minute<0) {
minute=58;
if (--hour <0) hour=23;
}
incrementtime();
second++; // Increment the second counter to ensure that the name
// flash doesnt happen when setting time
if (DS1302Present==1) {
// Make a new time object to set the date and time
Time t(2010, 04, 28, hour, minute, second, 01);
// Set the time and date on the chip
rtc.time(t);
}
displaytime();
}
// key repeat delay code:
millisNow = millis();
if (millisNow - buttonPressedMillis < BUTTON_DELAY_LONG)
new_button_delay = BUTTON_DELAY_SHORT;
else
new_button_delay = BUTTON_DELAY_LONG;
delay(button_delay);
button_delay = new_button_delay;
buttonPressedMillis = millis();
}
static int uart_thread(struct pt *pt)
{
char cmd, arg1, arg2, arg3;
int length;
PT_BEGIN(pt);
while(1)
{
PT_WAIT_UNTIL(pt, inputBufferComplete == true);
length = inputBuffer.length();
if (length == 0)
{
Serial.println("E: received 0 characters");
}
else
{
cmd = inputBuffer.charAt(0);
switch(cmd)
{
case 'H':
// process hello request
Serial.println("ELLO");
break;
case 'S':
// process set request
if (length < 4)
Serial.println("E: too few arguments");
else
{
#if (PRINT_DEBUG_LEVEL <= 0)
Serial.println("D: set request");
#endif
arg1 = inputBuffer.charAt(1);
arg2 = inputBuffer.charAt(2);
arg3 = inputBuffer.charAt(3);
switch(arg1)
{
case 'Y':
// set year
break;
case 'M':
// set month
break;
case 'D':
// set day of month
break;
case 'd':
// set day of week
break;
case 'h':
// set hour (24 hour format)
hour = 10 * (arg2 - 0x30) +
(arg3 - 0x30);
break;
case 'm':
// set minutes
minute = 10 * (arg2 - 0x30) +
(arg3 - 0x30);
break;
case 's':
// set seconds
second = 10 * (arg2 - 0x30) +
(arg3 - 0x30);
break;
default:
break;
}
// Set the time and date on the chip
Time t(2010, 4, 28, hour, minute,
second, 1);
rtc.time(t);
displaytime();
}
break;
case 'G':
// process get request
if (length < 2)
Serial.println("E: too few arguments");
else
{
arg1 = inputBuffer.charAt(1);
switch(arg1)
{
case 'Y':
// get year
break;
case 'M':
// get month
break;
case 'D':
// get day of month
break;
case 'd':
// get day of week
break;
case 'h':
// get hour (24 hour format)
Serial.print("g: h: ");
if (hour < 10)
Serial.print(0, DEC);
Serial.println(hour);
break;
case 'm':
// get minutes
Serial.print("g: m: ");
if (minute < 10)
Serial.print(0, DEC);
Serial.println(minute);
break;
case 's':
// get seconds
Serial.print("g: s: ");
if (second < 10)
Serial.print(0, DEC);
Serial.println(second);
break;
default:
break;
}
}
break;
default:
Serial.print("E: unknown command: 0x");
Serial.println(cmd, HEX);
break;
}
}
inputBuffer = "";
inputBufferComplete = false;
}
PT_END(pt);
}
static int buttons_thread(struct pt *pt)
{
PT_BEGIN(pt);
while(1)
{
if (BTNActive)
{
PT_WAIT_UNTIL(pt, ((BUTTON_PORT & FWD_BUTTON_MASK) > 0) ||
((BUTTON_PORT & REV_BUTTON_MASK) > 0));
process_buttons(BUTTON_PORT);
}
else
{
PT_WAIT_UNTIL(pt, ((BUTTON_PORT & FWD_BUTTON_MASK) == 0) ||
((BUTTON_PORT & REV_BUTTON_MASK) == 0));
process_buttons(BUTTON_PORT);
}
}
PT_END(pt);
}
static int lightsensor_thread(struct pt *pt)
{
static unsigned long msTick =0; // the number of Millisecond Ticks since we last
static unsigned long int lightlevel_avg;
unsigned long int lightlevel_sample;
static char millisWillOverflow = 0;
static char n_lightlevel_samples = 0;
PT_BEGIN(pt);
while(1)
{
PT_WAIT_UNTIL(pt, millisWillOverflow ? (millis() - msTick > 99) :
((millis() < 4294967295 - 98) && (millis() - msTick > 99)));
msTick=millis();
lightlevel_sample = analogRead(LIGHTSENSOR_INPUTPIN);
// update average
if (n_lightlevel_samples < (1 << LIGHTSENSOR_WEIGHT))
{
// add (1 << (LIGHTSENSOR_WEIGHT - 1)) to average before division so that
// the average is round()-ed instead of floor()-ed
lightlevel_avg = (n_lightlevel_samples * lightlevel_avg +
lightlevel_sample + (1 << (LIGHTSENSOR_WEIGHT - 1))) /
(n_lightlevel_samples + 1);
n_lightlevel_samples++;
}
else
{
// do not update n_lightlevel_samples to prevent overflow
// add (1 << (LIGHTSENSOR_WEIGHT - 1)) to average before division so that
// the average is round()-ed instead of floor()-ed
lightlevel_avg = ( ((1 << LIGHTSENSOR_WEIGHT) - 1) * lightlevel_avg +
lightlevel_sample + (1 << (LIGHTSENSOR_WEIGHT - 1))) >> LIGHTSENSOR_WEIGHT;
}
// compute new brightness level
/*
// linear method
if (lightlevel_avg <= LIGHTSENSOR_BOTTOM)
current_brightness = MINBRIGHTNESS;
else if (lightlevel_avg >= LIGHTSENSOR_TOP)
current_brightness = MAXBRIGHTNESS;
else
{
current_brightness = (brightness_per_unit_light * (lightlevel_avg -
LIGHTSENSOR_BOTTOM)) / (1 << LIGHTSENSOR_SCALE) + MINBRIGHTNESS;
} */
current_brightness = ambient_light_to_brightness[lightlevel_avg];
/*Serial.print("D: lightsensor: ");
Serial.print(lightlevel_sample);
Serial.print(", avg: ");
Serial.print(lightlevel_avg);
Serial.print(", brightness: ");
Serial.println(current_brightness);*/
}
PT_END(pt);
}
static int blink_thread(struct pt *pt)
{
static unsigned long msTick =0; // the number of Millisecond Ticks since we last
static char millisWillOverflow = 0;
PT_BEGIN(pt);
while(1)
{
PT_WAIT_UNTIL(pt, millisWillOverflow ? (millis() - msTick > 999) :
((millis() < 4294967295 - 998) && (millis() - msTick > 999)));
msTick=millis();
#if (PRINT_DEBUG_LEVEL <= 1)
Serial.println("D: blink thread is alive");
#endif
// Flash the onboard Pin13 Led so we know something is hapening!
digitalWrite(13,HIGH);
delay(50);
digitalWrite(13,LOW);
delay(50);
digitalWrite(13,HIGH);
delay(50);
digitalWrite(13,LOW);
}
PT_END(pt);
}
static int wordclock_thread(struct pt *pt)
{
static unsigned int counter = 0;
static unsigned long msTick =0; // the number of Millisecond Ticks since we last
static char millisWillOverflow = 0;
#ifdef TESTMODE
second=59;
#endif
//Serial.println("Loop Started");
PT_BEGIN(pt);
msTick=millis(); // Initialise the msTick counter
// heart of the timer - keep looking at the millisecond timer on the Arduino
// and increment the seconds counter every 1000 ms
while(1)
{
PT_WAIT_UNTIL(pt, millisWillOverflow ? (millis() - msTick > 999) :
((millis() < 4294967295 - 998) && (millis() - msTick > 999)));
msTick=millis();
if (msTick > 4294967295 - 998)
{
// millis() will overflow --> adjust msTick
msTick = 999 - (4294967295 - msTick);
millisWillOverflow = 1;
}
else
millisWillOverflow = 0;
second++;
//test to see if we need to increment the time counters
if (second==60)
{
incrementtime();
displaytime();
#if (PRINT_DEBUG_LEVEL <= 1)
Serial.print("D: time: ");
if (hour < 10)
Serial.print(0, DEC);
Serial.print(hour, DEC);
Serial.print(":");
if (minute < 10)
Serial.print(0, DEC);
Serial.print(minute, DEC);
Serial.print(":");
if (second < 10)
Serial.print(0, DEC);
Serial.println(second, DEC);
#endif
}
if (DS1302Present==1) {
// Get the current time and date from the chip
Time t = rtc.time();
second=t.sec;
minute=t.min;
hour=t.hr;
}
#if (USELIGHTSENSOR == 0)
// set the brightnes level based on the current hour - night=7pm - 6.59am
if ((hour < DAYLIGHTHOUR) | (hour >= NIGHTLIGHTHOUR))
current_brightness=MINBRIGHTNESS;
else
current_brightness=MAXBRIGHTNESS;
#endif
PT_END(pt);
}
}
void loop()
{
// call each thread continuously
wordclock_thread(&wordclock_thread_pt);
buttons_thread(&buttons_thread_pt);
#if (USELIGHTSENSOR == 1)
lightsensor_thread(&lightsensor_thread_pt);
#endif
blink_thread(&blink_thread_pt);
uart_thread(&uart_thread_pt);
}