9 years, 7 months ago.

Trouble with display time and something else

Hi.

I`m trying to create a simple home security system.

I`m using STM32 Nucleo F401-RE with HD44780 20x4 LCD display with I2C and matrix keypad 4x3. In first line of the display I`m presenting state of the alarm(armed, disarmed, alarm triggered). Second line is used to display " * " when user input code to arm or disarm the alarm. This functions are working well, but if I want to display time in fourth line(I`m using this simple program http://developer.mbed.org/handbook/Time ) program is not working properly. " * " shows on fourth line not second as it has to be, and not every time when I press button on keypad the " * " appears. Also sometimes on the screen appears strange symbols.

I was trying to use Ticker and even the RTOS but there is no result and always when I`m trying to display on LCD state of alarm and the time I`ve got problem as above.

Please helm, if Anyone know where I`m making a mistakes.

Sorry for my poor english, I hope that You understand what is my problem.

main.cpp

#include "mbed.h"
#include "TextLCD.h"
#include "PinDetect.h"
#include "keycode.h"
#include "rtos.h"

PinDetect sensor( PA_4 );


bool state = false;

void clock(void const *args)
{
    while(1) {
        lcd.locate(0,3);
        time_t seconds = time(NULL);
        //lcd.printf("Time = %s", ctime(&seconds));

        char buffer[9];
        strftime(buffer, 9, "%H:%M:%S", localtime(&seconds));
        lcd.printf("%s", buffer);
        lcd.locate(0,1);
        Thread::wait(1000);
    }
}

void alarm_state()       
{



        int pass = secure();
        if(pass == 0 && !state) {
            state = true;
            lcd.locate(0,0);
            lcd.printf("  armed  ");
            lcd.locate(0,1);
            lcd.printf("                    ");
        } else if(pass == 0 && state) {
            state = false;
            lcd.locate(0,0);
            lcd.printf("  disarmed   ");
            lcd.locate(0,1);
            lcd.printf("                    ");
        } else if(pass != 0) {
            lcd.locate(0,1);
            lcd.printf("      wrong code       ");
            wait(1);
            lcd.locate(0,1);
            lcd.printf("                    ");
        }

}       


void alarm()
{
    if(state) { 
        lcd.locate(0,0);
        lcd. printf("        alarm       ");

    }   
}

main()
{

    set_time(1256729737);

    Thread threadclock);
    lcd.setBacklight(TextLCD::LightOn);
    sensor.attach_deasserted(&alarm);
    sensor.setSampleFrequency();


    while(1) {  



        alarm_state();
        Thread::wait(500);



    } 
} 

keycode.h

#include "../keypad/keypad.h"



char code[] = "8888";
char code2[] = "9999";


int getCode()
{
        lcd.locate(0,1);

    char codeTry[] = "";
    int previous = -1;
    int a = -1;

    while(strlen(codeTry) < strlen(code)) {

        int cycles = 0;

        while ((a == previous && cycles < 20) || a == -1) { //keep looping until different to last, or held down
            a = keyNum();
            if(a == 10) {
                sprintf(codeTry, "");
                lcd.locate(0,1);
                lcd.printf("                    ");
                lcd.locate(0,1);
                }

            cycles++;
            wait(0.01);
        }
        if(a != 11 && a != 10) {
        sprintf(codeTry, "%s%d", codeTry, a);
        lcd.printf("*");

        }
        previous = a;
        wait (0.1);
        if(a==11) a = -1;
    }

    if((strcmp(code2, codeTry) == 0)) {
        return 1;
        }

    return 0;
}

int secure()
{
    int b;
    int lock = getCode();
    
    do {b = keyNum();} while(b!=11) ; // "#" confirmation of the code
    if(lock == 1) { //   if code OK 

        return 0;
    };
    
    return -1;
}

1 Answer

9 years, 7 months ago.

Hi Wacław, I think your problem is that you use parallel threads and interrupt driven functions to access the LCD. One thread or function may be in the middle of writing something to the display when it gets interrupted by the sensor pin being pressed, which results in a re-locate of the cursor and writing some other text. The LCD lib will now get confused because the cursor does not return to the original location before the interrupt occurred. In fact it may be even worse since the I2C methods used to access the display are broken up right in the middle of some ongoing communications (garbled characters). You need a more careful deconflicted access to the display to make this work. Probably best to have a single method that is in control of all access to the display and use some shared variables and/or flags to provide this method with information from the other threads on what it needs to display (eg timestring, alarm state).

Accepted Answer

Thanks for a quick answer.

Unfortunately even without using interrupt from sensor (or any other interrupt) problem still exist. It`s a good idea that LCD is confused but I have no idea what is wrong.

I`m still trying to resolve this problem but still nothing.

posted by Wacław Kuczalski 27 Mar 2015

Its not just the interrupt on sensor. You also have threads for clock and alarm_state that could access the display in parallel. Best approach is to restructure code and allow only one single function access to the display. It should be activated at regular intervals and then update lines 0..3 as needed.

posted by Wim Huiskamp 27 Mar 2015

I think its more than I can do. Unfortunately I have no idea how to do this.

Than You for your answer

posted by Wacław Kuczalski 27 Mar 2015

Something like should work (not yet tested)

// Host PC Communication channels
Serial pc(USBTX, USBRX); // tx, rx

//shared variables 
bool armedstate = false;
bool alarmtriggered = false;
char codestring[10] = "";
int pass = 0;
 
// all display access in this single function
// could add some flags to prevent unnecessary updates when nothing changed
void displayUpdate()
{
//Show Alarm state
  lcd.locate(0,0);
  if (alarmtriggered) {   
    //         "01234567890123456789"      
    lcd.printf("      alarm         "); 
  }  else if (pass == -1) {
    lcd.printf("    wrong code      ");
  }  else if (armedstate) {
    lcd.printf("      armed         ");
  } else  {
    lcd.printf("     disarmed       ");
  }
 
//Show keycodes 
  lcd.locate(0,1);
  if (strlen(codestring) == 0) {
    lcd.printf("                    ");  // clear the display when there is nothing to show
  }
  else {  
    lcd.printf("%s", codestring);  // show the string with ***
  }  
 
//Show empty line
  lcd.locate(0,2);
  lcd.printf("                    ");  // clear the display
 
//Show Time
  lcd.locate(0,3);
  time_t seconds = time(NULL);
  //lcd.printf("Time = %s", ctime(&seconds));
  char buffer[9];
  strftime(buffer, 9, "%H:%M:%S", localtime(&seconds));
  lcd.printf("%s", buffer);
}
 
void alarm() {
  if(armedstate) { 
    alarmtriggered = true;
  }   
}
 
Ticker displaytick; 
char command;
main()
{
    set_time(1256729737);
 
    lcd.setBacklight(TextLCD::LightOn);
    sensor.attach_deasserted(&alarm);
    sensor.setSampleFrequency();
    
    displaytick.attach(&displayUpdate, 0.5);  // update display every 0.5 second
    
    while(1) {  

#if(1)
//final code
      pass = secure();  // wait for keycodes and check:
                        //   pass==1 for code ok, pass == -1 for wrong code, pass == 0 otherwise
#else
//try this to test 
      command = pc.getc();       
      pc.printf("\n\r");         
      switch (command) {
        case '*' :  // key read
          pass = 0;
          if (strlen(codestring) < 4) {       // make sure to stay within array boundaries 
            sprintf(codestring, "%s%c", codestring, '*');
          }
          else {
            sprintf(codestring, "");
          }               
          break;          
        case '1' :  // code ok
          pass = 1;
          break;
        case 'x' :  // code wrong
          pass = -1;
          break;
        case 'a' :  // alarmtrigger
          pass = 0;
          alarm();
          break;
        default :          
          pass = 0; // no valid key read
          break;          
      }    
#endif
 
      if ((pass == 1) && !armedstate) {
         armedstate = true;
      }
      else if ((pass == 1) && armedstate) {
         armedstate = false;
      }
      else if (pass == -1) {
        wait(1);        // wrong code, delay before next attempt
        pass = 0;     // erase error report on display
      }  
    } 
} 

posted by Wim Huiskamp 27 Mar 2015

and the rest


#include "../keypad/keypad.h"
 

char code[] = "8888";
char code2[] = "9999";
 
 
int getCode()
{
    char codeTry[] = "";
    int previous = -1;
    int a = -1;
 
    while(strlen(codeTry) < strlen(code)) {
        int cycles = 0;
 
        while ((a == previous && cycles < 20) || a == -1) { //keep looping until different to last, or held down
            a = keyNum();
            if(a == 10) {
                sprintf(codeTry, "");
                sprintf(codestring, "");    // clear string                
            }
 
            cycles++;
            wait(0.01);
        }
        if(a != 11 && a != 10) {
          sprintf(codeTry, "%s%d", codeTry, a);
          if (strlen(codestring) < 4) {       
            sprintf(codestring, "%s%c", codestring, '*');  // add * to string
          } 
        }
        previous = a;
        wait (0.1);
        if(a==11) a = -1;
    }
 
    if((strcmp(code2, codeTry) == 0)) {
        return 1;
    }
 
    return 0;
}
 
int secure()
{
    int b;
    int lock = getCode();
    
    do {b = keyNum();} while(b!=11) ; // "#" confirmation of the code
    if(lock == 1) { //   if code OK  
        return 1;
    };
    
    return -1;
}

posted by Wim Huiskamp 27 Mar 2015

This code almost work. Problem is that every 0.5 second (ticker refresh) armedstate is changing and on the display I can see "armed" or "disarmed". I think that this happens because every 0.5 second armedstate is changing but I dont know why because I cannot enter any code.

posted by Wacław Kuczalski 28 Mar 2015

OK, I see how that happened. Try modified code above.

posted by Wim Huiskamp 28 Mar 2015

Unfortunately this doesn`t work too. Now when I enter code after second digit, state is changing from disarmed to armed, and when I put next digit it is changing from armed to alarm. Also it has no effect when I enter all 4 digit and press # for accept the code.

Thank You for your involvement in solving the problem.

posted by Wacław Kuczalski 28 Mar 2015

The codestring array size was not correct and this leads to problems. That should be fixed now. Note that the secure() and getCode() part does not look very clean and easy. You may want to fix that. For testing I have used keyboard keys instead. This code is still included, but disabled (part starts with command = pc.getc()). You can try this out yourself.

posted by Wim Huiskamp 28 Mar 2015

Now this code works until I press the button and change alarmtriggered state. Then program is stop working (clock is running). Also doesn`t work cleaning screen in second line (" * " after code entry). Manualy cleaning second line (by pressing * on keypad) works fine. Tommorow I will try to modify the code to run as it should.

Thanks very much for help and if You have any suggestions how to improve secure() and getCode() I would be glad if You show me the way how to do this.

edit Let`s say that I deal with cleaning second line (I`ve add " sprintf(codestring, ""); " in secure()).

Now I have to deal with suspending program when alarmtriggered is true.

posted by Wacław Kuczalski 29 Mar 2015