Sending serial data from within a loop

I have a problem. The application that I am using my arduino in requires me to send serial data from within a loop but the problem I have is that it doesn't just send data once because it is in a loop so it continually sends as long as the conditions are met. I am looking for a way to send the data once from within the loop until another different message needs to be sent. The current code is:

void homeAutomation(){
  if (digitalRead(alarmIn) == LOW){
    alarmInVal = HIGH;
    digitalWrite(chnlA, LOW);
    digitalWrite(strobe, alarmInVal);
    Serial.println("Emergency alarm activated!");
  }
  
  if(digitalRead(alarmSetButton) == LOW && digitalRead(doorSwitch) == LOW)
  {
    digitalWrite(alarmSiren, HIGH);
    alarmTime = millis();
    Serial.println("Burglar alarm activated!"); //Sends data once alarm conditions are met
  }
  
  if(currentMillis - alarmTime > 120000)//Compares time when alarm sounds to current time (120000 is 2 minutes)
  {
    digitalWrite(alarmSiren, LOW);
  }

    lightLevel = analogRead(lightSensor);
  if(lightLevel == lightThreshold){
    chnlAstat = HIGH;
    digitalWrite(chnlA, HIGH);
  }
}

I have tried to make a function which runs just once but it didn't work as it kept stopping the alarm from running which is critical. That code was:

void serialSend(char msg){
  char lastMsg; //Used to hold the last message sent
  if(msg != lastMsg){ //If it doesnt match the last message, send it
    Serial.println(msg);
    lastMsg = msg; //Set the current message as last message. Wash, rinse, repeat.
  }
}

This didn't work as the alarm failed to sound and it was throwing errors because I am trying to pass it a string. I have looked thru the reference and done some googling but I have found nothing that would tailor to my needs. It is almost run like an interrupt. It should run once and then wait for the next trigger. If anyone can help me achieve this, I would be very grateful. Regards, Callum

Use a boolean flag. Set the flag to false when conditions are not ripe for sending. Set it to true when they are. Set it to false again after sending data.

Each time through the loop, send only if the flag is true.

Actually, it looks like your tests are wrong. You need to detect transitions (alarm is tripped now but was not before or alarm is not tripped now but was before). When the transition occurs, you want to send data.

Thanks, I'll implement that once I've just finished adding a heartbeat LED to the system... Callum

Edit: After installing a heartbeat LED just to monitor the system which is just updated after each successful loop, it looks like the system hangs when the code that silences the alarm automatically is running. This bit in particular:

if(currentMillis - alarmTime > 120000)//Compares time when alarm sounds to current time (120000 is 2 minutes)
  {
    digitalWrite(alarmSiren, LOW);
  }

So it looks like I will have to set a variable once the alarm starts and then reset it once the serial send function is finished like Paul said...

Edit Pt.2: Wrote new code for the serial send routine which checks a boolean flag and I'm now getting this error relating to this line:

    serialMsg = "Burglar alarm!"; //Must be called before activating function!

The error is: Invalid conversion from 'const char*' to 'char'

How can I handle sending strings?! Someone please help before I blow a fuse!

Callum

How can I handle sending strings?!

Start by showing all of your code.

In the snippets you posted, you appear to be trying to write functions that take a string, but declaring them to take a char. A string is a NULL terminated ARRAY of chars, not a char.

Ok, be warned: its 264 lines long…

  /*----ARDUINO HOME AUTOMATION----
  Yes, you are free to modify and redistribute the code, just keep this disclaimer in!
  Disclaimer:
     I don't and won't be liable for any injury or losses associated with the use of this code!!
     It is probably riddled with bugs but you can solve them if you find them, also, if you
     notice any major problems that you have updated, please email a copy of the code to me
     uberdum05@gmail.com
     
     Code created: late 2011-early 2012
     by: Callum Snowden
     Feel free to redistribute but keep this credit in please :)
     
     If you have any questions, you can find me at:
     uberdum05@gmail.com
     -or-
     pluselectronics.wordpress.com
     
     Yes, I am only 14 and it took a lot of work to make this so can you just keep this credit please?
  */
  
  #include <LiquidCrystal.h>
  #include <Password.h>
  #include <Keypad.h>
  
  int strobe = 53; //Connected to relay and then to 12V strobe light
  int alarmIn = 52; //Emergency alarm input - switches everything off and activates alarm outputs
  int alarmInVal = LOW; //Set true if alarm input is triggered
  int alarmSetButton = 51; //Enable/disable the alarm subsystem (just a push on-off SPST button)
  int alarmSetVal; //Used for alarm setting/unsetting
  int doorSwitch = 50; //Pin for the door sensor (reed switch w/magnet taped to door)
  int alarmSiren = 40; //Output for the alarm siren
  
  int lightThreshold = 275; //Value when lamp needs to be turned on
  int lightLevel = 0; //Variable to hold the light level
  int lightSensor = 0; //Pin on which the LDR is on
  
  int interval = 1000; //Update interval for the LCD (most likely not needed once on beta 1.1)
  int previousMillis; //The last interval when the Millis() function was taken
  boolean lcdStat = true; //Hmmm, I can't remember what this was used for...
  int val = 0; //This may not be needed, just keep it though
  boolean pw_set; //Used to allow main code to run if password is ok
  Password password = Password("1337"); //Should be quite obvious!
  LiquidCrystal lcd(22,23,24,25,26,27); //Change to match your LCD pins
  
  unsigned long alarmTime = 0; //Time (millis()) when alarm was triggered for timout period
  unsigned long currentMillis = 0; //Keeps track of current time
  
  int heartbeatLED = 41; //LED Used for signalling that the system is still ok and hasn't hung
  
  //USED FOR SERIAL SEND ROUTINE!
  boolean sendSerialMessage = false; //Used for the sendSerial subroutine to check when conditions are ripe for sending
  char serialMsg; //Used to hold the message for sending via serial
  
  
  int chnlA = 13; //Channel A output
  int chnlAstat = LOW;
  
  /* * LCD RS pin to digital pin 22
   * LCD Enable pin to digital pin 23
   * LCD D4 pin to digital pin 24
   * LCD D5 pin to digital pin 25
   * LCD D6 pin to digital pin 26
   * LCD D7 pin to digital pin 27
   */
  
  const byte ROWS = 4; // Four rows
  const byte COLS = 4; //  columns
  // Define the Keymap
  char keys[ROWS][COLS] = {
    {'1','2','3', 'A'},
    {'4','5','6', 'B'},
    {'7','8','9', 'C'},
    {'*','0','#', 'D'}
  };
  byte rowPins[ROWS] = {28,29,30,31};
  byte colPins[COLS] = {32,33,34,35};
  
  Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
  KeypadEvent eKey;
  
  void setup(){
    Serial.begin(9600);
    Serial.println("#---------------------#");
    Serial.println("VOSHA HOME AUTOMATION");
    Serial.println("VERSION: 2.2B Parrot");
    Serial.println("#---------------------#");
    keypad.addEventListener(keypadEvent);
    lcd.begin(16, 4);
    lcd.print("VOSHA Home Auto");
    lcd.setCursor(0, 1);
    lcd.print("Version: 2.2");
    delay(500);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Starting.");
    delay(250);
    lcd.clear();
    lcd.print("Starting..");
    delay(250);
    lcd.clear();
    lcd.print("Starting...");
    delay(250);
    lcd.clear();
    lcd.print("Starting.");
    delay(250);
    lcd.clear();
    lcd.print("Starting..");
    delay(250);
    lcd.clear();
    lcd.print("Starting...");
    delay(250);
    lcd.clear();
    lcd.print("Starting.");
    delay(250);
    lcd.clear();
    lcd.print("Starting..");
    delay(250);
    lcd.clear();
    lcd.print("Starting...");
    delay(250);
    lcd.clear();
    pinMode(chnlA, OUTPUT); pinMode(strobe, OUTPUT); pinMode(alarmIn, INPUT); pinMode(alarmSetButton, INPUT); pinMode(doorSwitch, INPUT); pinMode(alarmSiren, OUTPUT); pinMode(heartbeatLED, OUTPUT);
    digitalWrite(alarmIn, HIGH); digitalWrite(alarmSetButton, HIGH); digitalWrite(doorSwitch, HIGH);
    lcd.print("Password:");
  }
  
  
  //SERIAL SEND ROUTINE FOR SENDING SERIAL DATA!
  void serialSend(){
    if(sendSerialMessage == true){
      Serial.println(serialMsg);
      sendSerialMessage = false;
    }
  }
  
  
  void loop(){
    currentMillis = millis();
    lcd.setCursor(0, 2);
    keypad.getKey();
    
    if(pw_set == true){ //Menu subsystem
      lcd.setCursor(0, 0);
      lcd.print("VOSHA Home Auto");
      lcd.setCursor(0, 1);
      lcd.print(chnlAstat);//Status of output channel A
  	switch (keypad.getKey()){
  	  case '#': //Lock routine
                    password.reset();
                    Serial.println("Reset!");
                    lcd.clear();
                    lcd.print("Password:");
                    val = 0;
                    pw_set = false; //Locks the menu subsystem
                  break;
            case '*':  //Once you are logged in, this will reset all the alarms unless they are still triggered
                    if (alarmInVal == HIGH){
                      alarmInVal = LOW;
                      digitalWrite(strobe, alarmInVal);
                    }
                    digitalWrite(alarmSiren, LOW);
                    Serial.println("All alarms reset!");
                  break;
            case 'A':
                              if (chnlAstat == LOW){
                chnlAstat = HIGH;
            }else{
                chnlAstat = LOW;
            }
           digitalWrite(chnlA, chnlAstat);
           Serial.println("Lamp: " + chnlAstat);
                  break;
  }
    } //End of menu subsystem
    homeAutomation();
    
  
  }
  
  void keypadEvent(KeypadEvent eKey){
    if(pw_set == false){
    switch (keypad.getState()){
      case PRESSED:
  	Serial.print("Pressed: ");
  	Serial.println(eKey);
  	switch (eKey){
  	  case '*': 
                  guessPassword(); //Calls password verification routine
                  break;
  	  case '#': //Clears the current password and displays msg
                  password.reset();
                  Serial.println("Reset!");
                  lcd.clear();
                  lcd.print("Password:");
                  val = 0;
                  break;
  	  default: //What normally happens. ie append the password
  		password.append(eKey);
                  lcd.setCursor(val, 1);
                  lcd.print("*");
                  val = val + 1;
       }
    }
    lcd.setCursor(0,1);
    }
  }
  
  void guessPassword(){  //Password verification routine
       Serial.print("Verifying password... ");
       if (password.evaluate()){
          Serial.println("OK");
          Serial.println("System logged in");
          lcd.clear();
          lcd.print("OK");
          password.reset(); 
          pw_set = true;   
       }else{
  	Serial.println("ERROR!");
          lcd.print("ERROR!!         ");
          delay(750);
          lcd.clear();
          password.reset();
          lcd.print("Password:");
          val = 0;
       }
  
  }
  
  void homeAutomation(){
    if (digitalRead(alarmIn) == LOW){
      alarmInVal = HIGH;
      digitalWrite(chnlA, LOW);
      digitalWrite(strobe, alarmInVal);
      Serial.println("Emergency alarm activated!");
    }
    
    if(digitalRead(alarmSetButton) == LOW && digitalRead(doorSwitch) == LOW)
    {
      digitalWrite(alarmSiren, HIGH);
      alarmTime = millis();
      
      //DEALS WITH SETTING UP FUNCTION AND CALLING FUNCTION!! 
      serialMsg = 'Alarm!'; //Must be called before activating function!
      sendSerialMessage = true; //Tells function OK to send
      serialSend; //Finally runs function itself
      
    }
    
    if(currentMillis - alarmTime > 120000)//Compares time when alarm sounds to current time (120000 is 2 minutes)
    {
      digitalWrite(alarmSiren, LOW);
    }
   
      lightLevel = analogRead(lightSensor);
    if(lightLevel == lightThreshold){
      chnlAstat = HIGH;
      digitalWrite(chnlA, HIGH);
    }
    heartbeat();
  }
      
  void heartbeat(){
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

I am trying to send over serial a full sentence ie ‘Burglar Alarm!’ but when I just used Serial.println(); to send that, it worked fine but it just overloaded the serial port which feeds straight into twitter. The function is only designed to run once but it won’t handle that string and its starting to cause problems associated with raising the alarm…

  char serialMsg; //Used to hold the message for sending via serial

Only if the message is only one character long. If it is to be longer than that, you need an array.

  void serialSend(){
    if(sendSerialMessage == true){
      Serial.println(serialMsg);
      sendSerialMessage = false;
    }
  }

This function should take an argument - the message to send - not use a global variable to contain that message:

  void serialSend(char *msgToSend)
  { // Down here where it belongs
    if(sendSerialMessage == true)
    { // Down here where it belongs
      Serial.println(msgToSend);
      sendSerialMessage = false;
    }
  }

Then, call it:

      sendSerialMessage = true; //Tells function OK to send
      serialSend("Alarm!"); //Finally runs function itself

Notice that 'Alarm!' that you had is a multibyte character which, while syntactically valid, has no place on an Arduino. A string, like "Alarm!" uses double quotes.

I'll edit the code now and upload it to my arduino when I get back home and let you know... Thanks for all the help :)

Callum

Edit: Just uploaded the sketch and its still running in a loop but I'm going to add another few variables and some logic that locks the routine from being run by that bit of code again until another call comes along and unlocks it...

uberdum50: Edit: Just uploaded the sketch and its still running in a loop but I'm going to add another few variables and some logic that locks the routine from being run by that bit of code again until another call comes along and unlocks it...

That's because you set your SendSerialMessage flag to true right before you call the function. That defeats the intended purpose of the flag.

The flag should be initialized to true. When you send an Alarm message, you set it to false. When you reset the alarm, you set it back to true.

I'll try initializing it the way you said and let you know...

Callum

Wow! Thanks a lot for all your help! The routine is working fine and I just need to sort out some bugs in the alarm setting code so I just give one push of a button and it arms...

A happy Callum!