/* * Copyright (c) 2004-2005, Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file is part of the protothreads library. * * Author: Adam Dunkels * * $Id: example-codelock.c,v 1.5 2005/10/06 07:57:08 adam Exp $ */ /* * * This example shows how to implement a simple code lock. The code * lock waits for key presses from a numeric keyboard and if the * correct code is entered, the lock is unlocked. There is a maximum * time of one second between each key press, and after the correct * code has been entered, no more keys must be pressed for 0.5 seconds * before the lock is opened. * * This is an example that shows two things: * - how to implement a code lock key input mechanism, and * - how to implement a sequential timed routine. * * The program consists of two protothreads, one that implements the * code lock reader and one that implements simulated keyboard input. * * */ #ifdef _WIN32 #include #else #include #include #endif #include #include "pt.h" /*---------------------------------------------------------------------------*/ /* * The following definitions are just for the simple timer library * used in this example. The actual implementation of the functions * can be found at the end of this file. */ struct timer { int start, interval; }; static int timer_expired(struct timer *t); static void timer_set(struct timer *t, int usecs); /*---------------------------------------------------------------------------*/ /* * This example uses two timers: one for the code lock protothread and * one for the simulated key input protothread. */ static struct timer codelock_timer, input_timer; /*---------------------------------------------------------------------------*/ /* * This is the code that has to be entered. */ static const char code[4] = {'1', '4', '2', '3'}; /*---------------------------------------------------------------------------*/ /* * This example has two protothread and therefor has two protothread * control structures of type struct pt. These are initialized with * PT_INIT() in the main() function below. */ static struct pt codelock_pt, input_pt; /*---------------------------------------------------------------------------*/ /* * The following code implements a simple key input. Input is made * with the press_key() function, and the function key_pressed() * checks if a key has been pressed. The variable "key" holds the * latest key that was pressed. The variable "key_pressed_flag" is set * when a key is pressed and cleared when a key press is checked. */ static char key, key_pressed_flag; static void press_key(char k) { printf("--- Key '%c' pressed\n", k); key = k; key_pressed_flag = 1; } static int key_pressed(void) { if(key_pressed_flag != 0) { key_pressed_flag = 0; return 1; } return 0; } /*---------------------------------------------------------------------------*/ /* * Declaration of the protothread function implementing the code lock * logic. The protothread function is declared using the PT_THREAD() * macro. The function is declared with the "static" keyword since it * is local to this file. The name of the function is codelock_thread * and it takes one argument, pt, of the type struct pt. * */ static PT_THREAD(codelock_thread(struct pt *pt)) { /* This is a local variable that holds the number of keys that have * been pressed. Note that it is declared with the "static" keyword * to make sure that the variable is *not* allocated on the stack. */ static int keys; /* * Declare the beginning of the protothread. */ PT_BEGIN(pt); /* * We'll let the protothread loop until the protothread is * expliticly exited with PT_EXIT(). */ while(1) { /* * We'll be reading key presses until we get the right amount of * correct keys. */ for(keys = 0; keys < sizeof(code); ++keys) { /* * If we haven't gotten any keypresses, we'll simply wait for one. */ if(keys == 0) { /* * The PT_WAIT_UNTIL() function will block until the condition * key_pressed() is true. */ PT_WAIT_UNTIL(pt, key_pressed()); } else { /* * If the "key" variable was larger than zero, we have already * gotten at least one correct key press. If so, we'll not * only wait for the next key, but we'll also set a timer that * expires in one second. This gives the person pressing the * keys one second to press the next key in the code. */ timer_set(&codelock_timer, 1000); /* * The following statement shows how complex blocking * conditions can be easily expressed with protothreads and * the PT_WAIT_UNTIL() function. */ PT_WAIT_UNTIL(pt, key_pressed() || timer_expired(&codelock_timer)); /* * If the timer expired, we should break out of the for() loop * and start reading keys from the beginning of the while(1) * loop instead. */ if(timer_expired(&codelock_timer)) { printf("Code lock timer expired.\n"); /* * Break out from the for() loop and start from the * beginning of the while(1) loop. */ break; } } /* * Check if the pressed key was correct. */ if(key != code[keys]) { printf("Incorrect key '%c' found\n", key); /* * Break out of the for() loop since the key was incorrect. */ break; } else { printf("Correct key '%c' found\n", key); } } /* * Check if we have gotten all keys. */ if(keys == sizeof(code)) { printf("Correct code entered, waiting for 500 ms before unlocking.\n"); /* * Ok, we got the correct code. But to make sure that the code * was not just a fluke of luck by an intruder, but the correct * code entered by a person that knows the correct code, we'll * wait for half a second before opening the lock. If another * key is pressed during this time, we'll assume that it was a * fluke of luck that the correct code was entered the first * time. */ timer_set(&codelock_timer, 500); PT_WAIT_UNTIL(pt, key_pressed() || timer_expired(&codelock_timer)); /* * If we continued from the PT_WAIT_UNTIL() statement without * the timer expired, we don't open the lock. */ if(!timer_expired(&codelock_timer)) { printf("Key pressed during final wait, code lock locked again.\n"); } else { /* * If the timer expired, we'll open the lock and exit from the * protothread. */ printf("Code lock unlocked.\n"); PT_EXIT(pt); } } } /* * Finally, we'll mark the end of the protothread. */ PT_END(pt); } /*---------------------------------------------------------------------------*/ /* * This is the second protothread in this example. It implements a * simulated user pressing the keys. This illustrates how a linear * sequence of timed instructions can be implemented with * protothreads. */ static PT_THREAD(input_thread(struct pt *pt)) { PT_BEGIN(pt); printf("Waiting 1 second before entering first key.\n"); timer_set(&input_timer, 1000); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('1'); timer_set(&input_timer, 100); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('2'); timer_set(&input_timer, 100); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('3'); timer_set(&input_timer, 2000); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('1'); timer_set(&input_timer, 200); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('4'); timer_set(&input_timer, 200); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('2'); timer_set(&input_timer, 2000); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('3'); timer_set(&input_timer, 200); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('1'); timer_set(&input_timer, 200); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('4'); timer_set(&input_timer, 200); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('2'); timer_set(&input_timer, 100); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('3'); timer_set(&input_timer, 100); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('4'); timer_set(&input_timer, 1500); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('1'); timer_set(&input_timer, 300); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('4'); timer_set(&input_timer, 400); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('2'); timer_set(&input_timer, 500); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); press_key('3'); timer_set(&input_timer, 2000); PT_WAIT_UNTIL(pt, timer_expired(&input_timer)); PT_END(pt); } /*---------------------------------------------------------------------------*/ /* * This is the main function. It initializes the two protothread * control structures and schedules the two protothreads. The main * function returns when the protothread the runs the code lock exits. */ int main(void) { /* * Initialize the two protothread control structures. */ PT_INIT(&input_pt); PT_INIT(&codelock_pt); /* * Schedule the two protothreads until the codelock_thread() exits. */ while(PT_SCHEDULE(codelock_thread(&codelock_pt))) { PT_SCHEDULE(input_thread(&input_pt)); /* * When running this example on a multitasking system, we must * give other processes a chance to run too and therefore we call * usleep() resp. Sleep() here. On a dedicated embedded system, * we usually do not need to do this. */ #ifdef _WIN32 Sleep(0); #else usleep(10); #endif } return 0; } /*---------------------------------------------------------------------------*/ /* * Finally, the implementation of the simple timer library follows. */ #ifdef _WIN32 static int clock_time(void) { return (int)GetTickCount(); } #else /* _WIN32 */ static int clock_time(void) { struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } #endif /* _WIN32 */ static int timer_expired(struct timer *t) { return (int)(clock_time() - t->start) >= (int)t->interval; } static void timer_set(struct timer *t, int interval) { t->interval = interval; t->start = clock_time(); } /*---------------------------------------------------------------------------*/