SMTP Example

Top  Previous  Next

//-----------------------------------------------------------------------------
// smtp_example.vpl, created 2009-11-11 09:16
//
// An example of how SMTP can be used to monitor a liquid container 
//
// Illustration:
//
//             |o|   <- supply flow monitor
//             +-+   <- top valve
//        |    | |    |
//        |          -| <- High level
//        |           |
//        |          -| <- Low level
//        +----+ +----+
//             +-+   <- bottom valve
//             |o|   <- drain flow monitor
//
// If any 1-Wire temperature sensors is connected, data will be collected and
// send by email.
//-----------------------------------------------------------------------------
INCLUDE rtcu.inc
 
VAR_INPUT
   level_high   : BOOL; | liquid is above high level
   level_low    : BOOL; | liquid is above low level
   flow_supply  : INT;  | amount of liquid running into tank
   flow_drain   : INT;  | amount of liquid running out of tank
END_VAR;
 
VAR_OUTPUT
   valve_top    : BOOL; | active open
   valve_bottom : BOOL; | active open
END_VAR;
 
VAR
   // Mail configuration
   receiver     : STRING := "mail@domain.com";
 
   clock        : clockGet;           // Reads the builtin Clock
   cnv_time     : clockLinsecToTime;  // Converts number to date format
END_VAR;
 
//---------------------------------------------------------------------------
// THREAD : tb_SendDataFile
//---------------------------------------------------------------------------
THREAD_BLOCK tb_SendDataFile;
VAR_INPUT
   filename     : STRING;
END_VAR;
VAR_OUTPUT
   status       : INT; // mail send status
   mail         : INT;
END_VAR;
VAR
   rc           : INT;
END_VAR;
   status := 1000;
 
   // Start a new e-mail
   mail := smtpNew(Receiver := receiver, Subject := "Data collected.");
   // Add text to the e-mail
   smtpAddText(Handle := mail, Message := "Data has been collected from ");
 
   cnv_time(linsec := fsFileGetCreateTime(name := filename));
   // date
   smtpAddText(Handle := mail, Message := intToStr(:= cnv_time.day));
   smtpAddText(Handle := mail, Message := "-" + intToStr(:= cnv_time.month));
   smtpAddText(Handle := mail, Message := "-" + intToStr(:= cnv_time.year));
   // time
   smtpAddText(Handle := mail, Message := " " + intToStr(:= cnv_time.hour));
   smtpAddText(Handle := mail, Message := ":" + intToStr(:= cnv_time.minute));
   smtpAddText(Handle := mail, Message := ":" + intToStr(:= cnv_time.second));
 
   smtpAddText(Handle := mail, Message := " to ");
 
   cnv_time(linsec := clockNow());
   // date
   smtpAddText(Handle := mail, Message := intToStr(:= cnv_time.day));
   smtpAddText(Handle := mail, Message := "-" + intToStr(:= cnv_time.month));
   smtpAddText(Handle := mail, Message := "-" + intToStr(:= cnv_time.year));
   // time
   smtpAddText(Handle := mail, Message := " " + intToStr(:= cnv_time.hour));
   smtpAddText(Handle := mail, Message := ":" + intToStr(:= cnv_time.minute));
   smtpAddText(Handle := mail, Message := ":" + intToStr(:= cnv_time.second));
 
   // Attach a file to the e-mail
   smtpAddAttachment(Handle := mail, Filename := filename);
 
   // Send the e-mail
   rc := smtpSendX(Handle := mail);
   IF rc <> 0 THEN
      DebugFmt(message := "Failed to send data file, error code '\1'.", v1 := rc);
   ELSE
      // monitor process
      REPEAT
         status := smtpStatus(Handle := mail);
      UNTIL status < 0
      END_REPEAT;
      DebugMsg(message := "data mail send.");
   END_IF;
   // Delete file
   fsFileDelete(name := filename);
   DebugMsg(message := "file '" + filename + "' deleted.");
END_THREAD_BLOCK;
 
//---------------------------------------------------------------------------
// SendDataMail instant, used by temperature thread.
//---------------------------------------------------------------------------
VAR
   SendMail : tb_SendDataFile;
END_VAR;
 
//---------------------------------------------------------------------------
// THREAD : tb_temperature_monitor
//---------------------------------------------------------------------------
THREAD_BLOCK tb_temperature_monitor;
VAR
   next_check  : DINT := 0;
   fd          : FILE;
   fname       : STRING := "data.csv";
   next_send   : DINT;
   cur_row     : DINT := 0;
   owCount     : INT;
   value       : DINT;
   new_file    : BOOL := TRUE;
   i           : INT;
   str         : STRING;
END_VAR;
 
   DebugMsg(message:= "Temperature monitor started."); 
 
   WHILE TRUE DO
      // Create new data file, on new collection.
      IF new_file THEN
         new_file := FALSE;
 
         // Delete any old file
         fsFileDelete(name := fname);
 
         // Create file
         fd :=fsFileCreate(name := fname);
 
         // Write header row followed by a new line to file
         fsFileWriteStringNL(fd := fd, str := "$"Time$";$"Average$";$"Values ->$"");
 
         next_send := clockNow() + 300; // send each 5th minute
         cur_row := 2;
      END_IF;
 
      // Collect data
      IF next_check < clockNow() THEN
         next_check := clockNow() + 10; // monitor each 10 second.
 
         // Search for One-Wire devices
         owCount := owSearch(family := 1);
 
         // get system clock
         clock();
         // write check time in first column
         fsFileWriteString(fd := fd, str := "$"" + intToStr(:= clock.day));
         fsFileWriteString(fd := fd, str := "-" + intToStr(:= clock.month));
         fsFileWriteString(fd := fd, str := "-" + intToStr(:= clock.year));
         fsFileWriteString(fd := fd, str := " " + intToStr(:= clock.hour));
         fsFileWriteString(fd := fd, str := ":" + intToStr(:= clock.minute));
         fsFileWriteString(fd := fd, str := ":" + intToStr(:= clock.second) + "$"");
 
         // generate average cell recognized by Microsoft Excel and OpenOffice Calc
         str := strFormat(format := ";$"=SUM(C\4:O\4)/\1$"", v1 := owCount, v4 := cur_row);
         // write sum function 
         fsFileWriteString(fd := fd, str := str);      
 
         // Write found temperatures
         FOR i := 1 TO owCount DO
            value := owTempGet(device := SINT(i));
            // write data to file
            fsFileWriteString(fd := fd, str := ";$"");
            fsFileWriteString(fd := fd, str := dintToStr(:= value / 100));
            fsFileWriteString(fd := fd, str := ",");
            fsFileWriteString(fd := fd, str := dintToStr(:= value MOD 100));
            fsFileWriteString(fd := fd, str := "$"");
         END_FOR;
 
         // Write newline
         fsFileWriteString(fd := fd, str := "$N");
         cur_row := cur_row + 1;        
 
         // wait 5 second before next check
         Sleep(delay := 5000);
      END_IF;
 
      // Time to send data
      IF next_send < clockNow() THEN
         fsFileClose(fd := fd);
 
         IF SendMail._running THEN
            DebugMsg(message := "Waiting for last mail to finish.");
            WHILE SendMail.status >= 0 AND SendMail._running DO
               DebugFmt(message := "Progress : \1", v1 := SendMail.status);
               Sleep(delay := 500);
            END_WHILE;
         END_IF;
 
         // Rename file
         fsFileRename(name_old := fname, name_new := "temp.csv");
         // send the file as an attachment
         SendMail(filename := "temp.csv");
 
         new_file := TRUE;
      END_IF;
   END_WHILE;
END_THREAD_BLOCK;
 
//---------------------------------------------------------------------------
// THREAD_BLOCK : tb_level_monitor
//---------------------------------------------------------------------------
THREAD_BLOCK tb_level_monitor;
VAR
   trig_supply : RF_TRIG;
   trig_drain  : RF_TRIG; 
   trig_high   : RF_TRIG;
   trig_low    : RF_TRIG;
 
   message     : STRING;
   subject     : STRING;
   send_msg    : BOOL;
 
   rc          : INT;
END_VAR;
 
   DebugMsg(message := "Level monitor started.");
 
   // open vales according to current state
   IF NOT level_high THEN
      valve_top := ON;
   END_IF;
 
   IF level_low THEN
      valve_bottom := ON;
   END_IF;
 
   // skip initial level warnings
   trig_high(trig := level_high);
   trig_low(trig := level_low);
   trig_drain(trig := flow_drain > 10);
   trig_supply(trig := flow_supply > 10);
 
   WHILE TRUE DO
      send_msg := FALSE;
 
      trig_high(trig := level_high);
      trig_low(trig := level_low);
 
      trig_drain(trig := flow_drain > 10);
      trig_supply(trig := flow_supply > 10);
 
      // check high level
      IF trig_high.rq THEN
         subject := "WARNING: Liquid level critical high";
         message := subject + ", closing top valve.";
         valve_top := OFF;
         send_msg := TRUE;
      ELSIF trig_high.fq THEN
         subject := "NOTIFY: Liquid level below critical high";
         message := subject + ", opening top valve.";
         valve_top := ON;
         send_msg := TRUE;
      END_IF;
 
      // check low level
      IF trig_low.fq THEN
         subject := "WARNING: Liquid level critical low";
         message := subject + ", closing bottom valve.";
         valve_bottom := OFF;
         send_msg := TRUE;
      ELSIF trig_low.rq THEN
         subject := "NOTIFY: Liquid level above critical low";
         message := subject + ", opening bottom valve.";
         valve_bottom := ON;
         send_msg := TRUE;
      END_IF;
 
      IF send_msg THEN
         DebugMsg(message := message);
         // Send an e-mail
         rc := smtpSend(Receiver := receiver, Subject := subject, Message := message);
         IF rc <> 0 THEN
            DebugFmt(message := "Failed to send message, error code '\1'.", v1 := rc);
         END_IF;
      END_IF;
 
      // monitor supply and drain pipes
      IF trig_supply.fq AND valve_top THEN
         subject := "WARNING: supply pipe clogged";
         message := subject + ", no action taken.";
         DebugMsg(message := message);
         // Send an e-mail
         rc := smtpSend(Receiver := receiver, Subject := subject, Message := message);
         IF rc <> 0 THEN
            DebugFmt(message := "Failed to send message, error code '\1'.", v1 := rc);
         END_IF;
      END_IF;
      IF trig_drain.fq AND valve_bottom THEN
         subject := "WARNING: drain pipe clogged";
         message := subject + ", no action taken.";
         DebugMsg(message := message);
         // Send an e-mail
         rc := smtpSend(Receiver := receiver, Subject := subject, Message := message);
         IF rc <> 0 THEN
            DebugFmt(message := "Failed to send message, error code '\1'.", v1 := rc);
         END_IF;
      END_IF;
 
      // wait 2 seconds before next scan
      Sleep(delay := 2000);
   END_WHILE;
END_THREAD_BLOCK;
 
//---------------------------------------------------------------------------
// PROGRAM: smtp_example 
//---------------------------------------------------------------------------
PROGRAM smtp_example;
VAR 
   rc                : INT;
   last_signal_time  : DINT := 0;
   temp_monitor      : tb_temperature_monitor;
   level_monitor     : tb_level_monitor;
END_VAR;
 
   DebugMsg(message := "Initializing.");
   // Open media
   rc := fsMediaOpen(media := 0);
   DebugFmt(message := "fsMediaOpen = \1", v1 := rc);
 
   // Controls power to the Cellular module
   rc := gsmPower(power := ON);
   DebugFmt(message := "gsmPower = \1", v1 := rc);
 
   // Open/activate Cellular network connection.
   rc := netOpen(iface:=_NET_IFACE_CELLULAR);
   DebugFmt(message := "netOpen = \1", v1 := rc);
 
   // Set SMTP configuration
   smtpSetConfig(
      IP :="mail.domain.com",
      Port := 25,
      Sender := "tank_monitor@domain.com",
      Authenticate := OFF
   );
 
   // Open the smtp interface
   rc := smtpOpen();
   DebugFmt(message := "smtpOpen = \1", v1 := rc);
 
   DebugMsg(message := "Starting threads.");
   temp_monitor();
   level_monitor();
 
   DebugMsg(message := "Ready");
BEGIN
   IF last_signal_time < clockNow() THEN
      last_signal_time := clockNow() + 10; // check again in 10sec
 
      // Check if Cellular network connection is established
      IF NOT netConnected(iface:=_NET_IFACE_CELLULAR) THEN
         DebugMsg(message := "WARNING: no connection to Cellular network, sending mails not posible.");
      END_IF;
 
      IF NOT temp_monitor._running THEN
         DebugMsg(message := "ERROR: Temperature monitor not running, starting.");
         temp_monitor();
      END_IF;
 
      IF NOT level_monitor._running THEN
         DebugMsg(message := "ERROR: Level monitor not running, starting.");
         level_monitor();
      END_IF;
   END_IF;
END;
END_PROGRAM;