Home Contact us


Multithreading Example



//-----------------------------------------------------------------------------
// thread example.vpl, created 2004-10-15 10:23
// 
// Simple program to demonstrate the use of threads, mutexes and I-Button.
// The program creates 3 threads, 1 to read the GPS position and store it
// in persistent memory, 1 to read an I-Button and control I-Button LED and 1 to
// Parse incoming SMS messages.
// By using an I-Button or sending a SMS the program will monitor the 4 inputs
// and send a SMS when one of them is activated. The monitoring must restart
// before another alarm is send.
// A tracking function is also included, to be activated by a SMS.
// 
// The following SMS commands are supported:
//  OWI#,nnnnnnnnnnnn: Register an I-Button ID for validation. #=Index(1..5), nnnnn=I-Button ID
//  AON: Activate alarm monitoring
//  AOFF: Deactivate alarm monitoring
//  TON: Start tracking
//  TOFF: Stop tracking
//  POS: Get position
//  PHONEnnnnnnnn: Tracking and Alarm response phone number
//-----------------------------------------------------------------------------
INCLUDE rtcu.inc
INCLUDE thread.inc

VAR_INPUT
   in   : ARRAY[1..4OF BOOL;
END_VAR;

VAR_OUTPUT
   led : BOOL;
END_VAR;

VAR
   AlarmState : BOOL := FALSE;
   TrackState : BOOL := FALSE;

   mxStateAl  : MUTEX;
   mxStateTr  : MUTEX;

   NextGps    : DINT;
END_VAR;

//----------------------------------------------------------------------------
// SetAlarm
// Set the state of the AlarmState flag.
//----------------------------------------------------------------------------
FUNCTION SetAlarm
VAR_INPUT
   state : BOOL;
END_VAR;
   mxLock(mx:=mxStateAl);
      AlarmState := state;
   mxUnlock(mx:=mxStateAl);
END_FUNCTION
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// TestAlarm
// Test the state of the AlarmState flag.
//----------------------------------------------------------------------------
FUNCTION TestAlarm : BOOL
   mxLock(mx:=mxStateAl);
      TestAlarm := AlarmState;
   mxUnlock(mx:=mxStateAl);
END_FUNCTION
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// SetTracking
// Set the state of the TrackState flag.
//----------------------------------------------------------------------------
FUNCTION SetTracking
VAR_INPUT
   state : BOOL;
END_VAR;
   mxLock(mx:=mxStateTr);
      TrackState := state;
   mxUnlock(mx:=mxStateTr);
END_FUNCTION
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// TestTracking
// Test the state of the TrackState flag.
//----------------------------------------------------------------------------
FUNCTION TestTracking : BOOL
   mxLock(mx:=mxStateTr);
      TestTracking := TrackState;
   mxUnlock(mx:=mxStateTr);
END_FUNCTION
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// SetId
// Register or Unregister an I-Button ID
//----------------------------------------------------------------------------

FUNCTION SetId
VAR_INPUT
   index  : INT;
   ID     : STRING;
END_VAR;
VAR
   oldIDs : STRING;
   newIDs : STRING;
   i      : INT;
END_VAR;
   // Verify index
   IF index > 0 AND index < 6 THEN
      // Read Registered IDs
      oldIDs := LoadStringF(index:=3);
      // Insert IDs before new
      IF index > 1 THEN
         FOR i := 1 TO index - 1 BY 1 DO
            newIDs := strConcat(str1:=newIDs,str2:=strToken(str:=oldIDs,delimiter:=",",index:=i));
            newIDs := strConcat(str1:=newIDs,str2:=",");
         END_FOR;
      END_IF;
      // Insert new ID
      newIDs := strConcat(str1:=newIDs,str2:=ID);
      newIDs := strConcat(str1:=newIDs,str2:=",");
      // Insert IDs after new
      IF index < 5 THEN
         FOR i := index + 1 to 5 BY 1 DO
            newIDs := strConcat(str1:=newIDs,str2:=strToken(str:=oldIDs,delimiter:=",",index:=i));
            newIDs := strConcat(str1:=newIDs,str2:=",");
         END_FOR;
      END_IF;
      // Save Registered IDs
      SaveStringF(index:=3,str:=newIDs);
   END_IF;
END_FUNCTION
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// SendPos
// Sends the last position
//----------------------------------------------------------------------------

FUNCTION SendPos
VAR_INPUT
   phone : STRING;
END_VAR;
VAR
   message : STRING;
END_VAR;
   // Retrieve last valid position from FRAM
   message := LoadStringF(index:=1);
   // Send position
   gsmSendSMS(phonenumber:=phone,message:=message);
END_FUNCTION
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// OWIBUTTON
// Thread that reads and validates I-Button IDs, for alarm activation/
// deactivation.
//----------------------------------------------------------------------------

THREAD_BLOCK OWIBUTTON
VAR
   strID  : STRING;
   Valid  : BOOL;
   Count  : INT := 0;
   Blink  : INT := 0;
END_VAR;
   // Init
   OWiButtonEnableLED(enable:=TRUE);
   OWiButtonSetLED(state:=OFF);
   // Do forever
   WHILE TRUE DO
      // Read I-Button ID
      strID := OWiButtonGetID();
      // Is there an I-Button present?
      IF strLen(str:=strID) > 0 THEN
         // Reset timer
         Count := 20;
         Blink := 10;
         // Repetition?
         IF NOT Valid THEN
            // Lookup ID
            IF strFind(str1:=LoadStringF(index:=3),str2:=strID) > 0 THEN
               // Change alarm state
               SetAlarm( state := NOT TestAlarm() );
            END_IF;
            // I-Button has been read
            OWiButtonSetLED(state:=ON);
            Valid := TRUE;
         END_IF;
      ELSE
         Valid := FALSE;
         // Delay for I-Button removed from reader
         IF Count > 0 THEN
            // Delay
            Count := Count - 1;
            Sleep(delay:=100);
         // Is alarm survailance activated?
         ELSIF TestAlarm() THEN
            // Led := ON for 100 ms.
            IF Blink = 0 THEN
               // Reset timer
               Blink := 10;
               // Turn LED on
               OWiButtonSetLED(state:=ON);
            ELSIF Blink = 9 THEN
               // Turn LED off
               OWiButtonSetLED(state:=OFF);
            END_IF;
            // Delay
            Blink := Blink - 1;
            Sleep(delay:=100);
         ELSE
            OWiButtonSetLED(state:=OFF);
            Sleep(delay:=100);
         END_IF;
      END_IF;
   END_WHILE;
END_THREAD_BLOCK;
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// GPS
// Thread that reads valid GPS positions and save them in FRAM
//----------------------------------------------------------------------------

THREAD_BLOCK GPS
VAR
   gpspos    : gpsFix;
   str       : STRING;
END_VAR;
   // Init
   gpsPower(power:=ON);
   // Do forever
   WHILE TRUE DO
      // Get GPS position
      gpspos();
      // Valid Position?
      IF gpspos.mode > 1 THEN
         // String
         str := strFormat(format:="Time: \1.\2.\3, ",v1:=gpspos.day,v2:=gpspos.month,v3:=gpspos.year);
         str := strConcat(str1:=str,str2:=strFormat(format:="\1:\2:\3 ",v1:=gpspos.hour,v2:=gpspos.minute,v3:=gpspos.second));
         IF gpspos.latsouth THEN
            str := strConcat(str1:=str,str2:=strFormat(format:="Lat: S\1*\2.\3 ",v1:=gpspos.latdeg,v2:=gpspos.latmin,v3:=gpspos.latdecmin));
         ELSE         
            str := strConcat(str1:=str,str2:=strFormat(format:="Lat: N\1*\2.\3 ",v1:=gpspos.latdeg,v2:=gpspos.latmin,v3:=gpspos.latdecmin));
         END_IF;
         IF gpspos.lonwest THEN
            str := strConcat(str1:=str,str2:=strFormat(format:="Long: W\1*\2.\3 ",v1:=gpspos.londeg,v2:=gpspos.lonmin,v3:=gpspos.londecmin));
         ELSE         
            str := strConcat(str1:=str,str2:=strFormat(format:="Long: E\1*\2.\3 ",v1:=gpspos.londeg,v2:=gpspos.lonmin,v3:=gpspos.londecmin));
         END_IF;
         SaveStringF(index:=1,str:=str);
      END_IF;
      Sleep(delay:=1000);
   END_WHILE;
END_THREAD_BLOCK;
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// SMS
// Thread that reads incoming SMS messages and executes commands
//----------------------------------------------------------------------------
THREAD_BLOCK SMS
VAR
   incoming  : gsmIncomingSMS;
END_VAR;
   // Init
   gsmPower(power:=ON);
   DebugMsg(message:="Ready to receive SMS");
   // Do forever
   WHILE TRUE DO
      incoming();
      // GSM connection status
      led := gsmConnected();
      // Check for incoming SMS
      IF incoming.status > 0 THEN
         DebugMsg(message:=strConcat(str1:="SMS recieved => ",              str2:=strConcat(str1:=incoming.phonenumber,str2:=strConcat(str1:=",",str2:=incoming.message))));
         // Set I-Button ID
         IF strCompare(str1:="OWI",str2:=strLeft(str:=incoming.message,length:=3)) = 0 THEN
            // Insert new ID
            SetId( index := strToInt(str:=strMid(str:=incoming.message,start:=4,length:=1)), ID := strMid(str:=incoming.message,start:=6) );
            gsmSendSMS(phonenumber:=incoming.phonenumber,message:=strConcat(str1:=strMid(str:=incoming.message,start:=4,length:=1),str2:=strMid(str:=incoming.message,start:=6)));
         // Activate Alarm
         ELSIF strCompare(str1:="AON",str2:=incoming.message) = 0 THEN
            // Change alarm state
            SetAlarm(state:=ON);
            gsmSendSMS(phonenumber:=incoming.phonenumber,message:="Alarm=ON");
         // Deactivate Alarm
         ELSIF strCompare(str1:="AOFF",str2:=incoming.message) = 0 THEN
            // Change alarm state
            SetAlarm(state:=OFF);
            gsmSendSMS(phonenumber:=incoming.phonenumber,message:="Alarm=OFF");
         // Activate Tracking
         ELSIF strCompare(str1:="TON",str2:=incoming.message) = 0 THEN
            // Syncronize tracking timer
            NextGps := clockNow();
            // Change tracking state
            SetTracking(state:=ON);
            gsmSendSMS(phonenumber:=incoming.phonenumber,message:="Tracking=ON");
         // Deactivate Tracking
         ELSIF strCompare(str1:="TOFF",str2:=incoming.message) = 0 THEN
            // Change tracking state
            SetTracking(state:=OFF);
            gsmSendSMS(phonenumber:=incoming.phonenumber,message:="Tracking=OFF");
         // Retrieve Position
         ELSIF strCompare(str1:="POS",str2:=incoming.message) = 0 THEN
            // Send last position
            SendPos(phone:=incoming.phonenumber);
         // Set reply phone number
         ELSIF strCompare(str1:="PHONE",str2:=strLeft(str:=incoming.message,length:=5)) = 0 THEN
            SaveStringF(index:=4,str:=strMid(str:=incoming.message,start:=6));
            gsmSendSMS(phonenumber:=incoming.phonenumber,message:=strConcat(str1:="Phone=",str2:=strMid(str:=incoming.message,start:=6)));
         END_IF;
      END_IF;
      Sleep(delay:=2000);
   END_WHILE;
END_THREAD_BLOCK;
//----------------------------------------------------------------------------

PROGRAM thread_example;
VAR
   thIbutton  : OWIBUTTON;
   thGps      : GPS;
   thSms      : SMS;

   Valid      : BOOL;
END_VAR;

   // Init MUTEX
   DebugMsg(message:="Initializing Mutex");
   mxStateAl := mxInit();
   mxStateTr := mxInit();
   IF mxStatus(mx:=mxStateAl) = 1 THEN DebugMsg(message:="mxStateAl failed to init!"); END_IF;
   IF mxStatus(mx:=mxStateTr) = 1 THEN DebugMsg(message:="mxStateTr failed to init!"); END_IF;

   // Init THREAD
   DebugMsg(message:="Starting Threads");
   thIbutton();
   thGps();
   thSms();
   IF NOT thIbutton._running THEN DebugMsg(message:="thIbutton failed to start!"); END_IF;
   IF NOT thGps._running THEN DebugMsg(message:="thGps failed to start!"); END_IF;
   IF NOT thSms._running THEN DebugMsg(message:="thSms failed to start!"); END_IF;

BEGIN
   // Are we monitoring Alarm inputs?
   IF TestAlarm() THEN
      // Test alarm inputs
      IF (in[1OR in[2OR in[3OR in[4]) AND NOT Valid THEN
         gsmSendSMS(phonenumber:=LoadStringF(index:=4),message:="ALARM!");
         Valid := TRUE;
      END_IF;
   ELSE
      Valid := FALSE;
   END_IF;

   // Are we tracking unit?
   IF TestTracking() THEN
      // Time for next position?
      IF clockNow() >= NextGps THEN
         // Send last position
         SendPos(phone:=LoadStringF(index:=4));
         // Time for next position
         NextGps := clockNow() + 60// 1 Min.
      END_IF;
   END_IF;
END;

END_PROGRAM;