Fun with Arduino 11 Data Input via Keyboard: Serial.read(), Serial.parseInt()

Since video 10 our software shows the cycle status and the current day / night time on the PC screen. The next step is to be able to enter a new value for the cycle time via the keyboard. Luckily this is made easy … the Arduino has several instructions to read data:

  • Serial.available() tells us that something has been typed
  • Serial.read() reads one character and returns it as a byte.
  • Serial.parseInt() reads multiple characters and returns an integer number

Read on below the video …

 

 

In setup() we add new print statements to tell the user how to enter a new cycle time. In loop() we add an if statement to check if data is available and if it is, we read it and we print it … that’s it. Let’s first try this with the Serial.read() function, which reads one character at a time.

#define SWITCH_PIN 2 // input for toggle switch
#define LIGHTS_PIN 8 // output to FET or relay module
#define LED_PIN A5   // output to LED on switch panel
#define T_MIN 1      // minimum random time to add to cycle_time
#define T_MAX 5      // maximum random time to add to cycle_time
byte switch_new, switch_old;
unsigned int cycle_time = 1; // [s] 180 = 3 minutes
unsigned int day_time, night_time;

void setup() {
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  pinMode(LIGHTS_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
  Serial.println();
  Serial.println("Day/Night cycle system is ready");
  Serial.println();
  Serial.print("Cycle time: ");
  Serial.print(cycle_time);
  Serial.println(" seconds");
  Serial.println();
  Serial.println("To enter a new cycle time, type 1-9");
  Serial.println("in the field above and press Enter");
  Serial.println();
}

void loop() {
  if (Serial.available() > 0) {
    cycle_time = Serial.read(); // multiply by 60 to get minutes
    Serial.println();
    Serial.print("Cycle time changed to: ");
    Serial.print(cycle_time);
    Serial.println(" minutes");
    Serial.println();
  }
  switch_new = digitalRead(SWITCH_PIN);
  if (switch_new != switch_old) {
    if (switch_new == LOW) Serial.println("Cycle started");
    else Serial.println("Cycle stopped");
    switch_old = switch_new;
  }
  if (switch_new == LOW) {
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(LIGHTS_PIN, HIGH);
    night_time = cycle_time + random(T_MIN, T_MAX);
    Serial.print("Night time = ");
    Serial.print(night_time);
    Serial.println(" seconds");
    delay((unsigned long)night_time * 1000);
    digitalWrite(LIGHTS_PIN, LOW);
    day_time = cycle_time + random(T_MIN, T_MAX);
    Serial.print("Day time   = ");
    Serial.print(day_time);
    Serial.println(" seconds");
    delay((unsigned long)day_time * 1000); 
  }
  else {
    digitalWrite(LED_PIN, LOW);
  }
}

cycle_time is declared as an unsigned int while Serial.read() gives us a byte … will the instruction cycle_time = Serial.read(); not give us any trouble like we saw in video 9? No it won’t, since a byte always fits in an int. If the data types would have been the other way around, then indeed something could go wrong.

arduino_11_pic1Let’s test this sketch. Hey … that is strange … when we enter 5, the cycle time is 53? Why is that? Well … that’s because numbers and characters are represented as bytes using the American Standard Code for Information Interchange, ASCII for short. You may not have been aware, but all the years that you have been typing on a PC or a smart phone, you have been ‘talking’ ASCII. The table below shows part of the code. The numbers 0-9 are represented by decimal ASCII values 48-57.

ascii.jpgIf we subtract 48 from the ASCII code that comes in via the keyboard we should get the correct numbers 1-9. What if the user types anything else than 1-9? Well … then we still get a cycle time, according to the ASCII table – 48. If an ‘N’ was typed, the cycle time would become 78-48=30. If we don’t want this, we can add an if statement to test if the input was between 1 and 9, but since nothing goes drastically wrong we keep it simple right now. Here is the code with 48 subtracted:

#define SWITCH_PIN 2 // input for toggle switch
#define LIGHTS_PIN 8 // output to FET or relay module
#define LED_PIN A5   // output to LED on switch panel
#define T_MIN 1      // minimum random time to add to cycle_time
#define T_MAX 5      // maximum random time to add to cycle_time
byte switch_new, switch_old;
unsigned int cycle_time = 1; // [s] 180 = 3 minutes
unsigned int day_time, night_time;

void setup() {
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  pinMode(LIGHTS_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
  Serial.println();
  Serial.println("Day/Night cycle system is ready");
  Serial.println();
  Serial.print("Cycle time: ");
  Serial.print(cycle_time);
  Serial.println(" seconds");
  Serial.println();
  Serial.println("To enter a new cycle time, type 1-9");
  Serial.println("in the field above and press Enter");
  Serial.println();
}

void loop() {
  if (Serial.available() > 0) {
    cycle_time = Serial.read() - 48;
    Serial.println();
    Serial.print("Cycle time changed to: ");
    Serial.print(cycle_time);
    Serial.println(" seconds");
    Serial.println();
  }
  switch_new = digitalRead(SWITCH_PIN);
  if (switch_new != switch_old) {
    if (switch_new == LOW) Serial.println("Cycle started");
    else Serial.println("Cycle stopped");
    switch_old = switch_new;
  }
  if (switch_new == LOW) {
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(LIGHTS_PIN, HIGH);
    night_time = cycle_time + random(T_MIN, T_MAX);
    Serial.print("Night time = ");
    Serial.print(night_time);
    Serial.println(" seconds");
    delay((unsigned long)night_time * 1000);
    digitalWrite(LIGHTS_PIN, LOW);
    day_time = cycle_time + random(T_MIN, T_MAX);
    Serial.print("Day time   = ");
    Serial.print(day_time);
    Serial.println(" seconds");
    delay((unsigned long)day_time * 1000); 
  }
  else {
    digitalWrite(LED_PIN, LOW);
  }
}

uicompleteLet’s test … yes that looks fine! Of course for our test we use short times, if we want the cycle time to be 1-9 minutes all we have to do is multiply by 60. If we type other characters than 1-9,  or if we enter more than one, nothing goes wrong, we just get an unexpected cycle time.

What if we would like to allow for the user to enter multi-digit numbers, like 180? We could write code to first read all the characters that were typed and then transfer them into an integer number … but … we don’t have to write that code ourselves, the Arduino has a function that does just that: Serial.parseInt().

Let’s change the Serial.read() statement into Serial.parseInt() and see what happens:

#define SWITCH_PIN 2 // input for toggle switch
#define LIGHTS_PIN 8 // output to FET or relay module
#define LED_PIN A5   // output to LED on switch panel
#define T_MIN 1      // minimum random time to add to cycle_time
#define T_MAX 5      // maximum random time to add to cycle_time
byte switch_new, switch_old;
unsigned int cycle_time = 1; // [s] 180 = 3 minutes
unsigned int day_time, night_time;

void setup() {
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  pinMode(LIGHTS_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
  Serial.println();
  Serial.println("Day/Night cycle system is ready");
  Serial.println();
  Serial.print("Cycle time: ");
  Serial.print(cycle_time);
  Serial.println(" seconds");
  Serial.println();
  Serial.println("To enter a new cycle time, type it");
  Serial.println("in the field above and press Enter");
  Serial.println();
}

void loop() {
  if (Serial.available() > 0) {
    cycle_time = Serial.parseInt();
    Serial.println();
    Serial.print("Cycle time changed to: ");
    Serial.print(cycle_time);
    Serial.println(" seconds");
    Serial.println();
  }
  switch_new = digitalRead(SWITCH_PIN);
  if (switch_new != switch_old) {
    if (switch_new == LOW) Serial.println("Cycle started");
    else Serial.println("Cycle stopped");
    switch_old = switch_new;
  }
  if (switch_new == LOW) {
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(LIGHTS_PIN, HIGH);
    night_time = cycle_time + random(T_MIN, T_MAX);
    Serial.print("Night time = ");
    Serial.print(night_time);
    Serial.println(" seconds");
    delay((unsigned long)night_time * 1000);
    digitalWrite(LIGHTS_PIN, LOW);
    day_time = cycle_time + random(T_MIN, T_MAX);
    Serial.print("Day time   = ");
    Serial.print(day_time);
    Serial.println(" seconds");
    delay((unsigned long)day_time * 1000); 
  }
  else {
    digitalWrite(LED_PIN, LOW);
  }
}

uifinalYes, that looks good. Now the user can type any multi digit value that he/she likes, and it will become the cycle time in seconds.

There’s this one issue though: if something is typed while the cycle is running, it is only shown after a complete night + day cycle has passed. In our test we use just a couple of seconds for the cycle time, then it is not too annoying, but when we use cycle times in the range of minutes, we don’t see anything on the screen when we type a number until only several minutes later. That is not nice!

This happens because we use the delay() statement as a timer. During a delay() the loop is halted. Luckily there is a simple solution: we can use the built in clock of the Arduino to read what time it is. We can do something if it is the time to do it, which we simply check with an if statement. We will implement this soon, but first we focus on the hardware User Interface we also promised to build … to change the cycle time with a rotating knob. We will run into the same issue there, we won’t see the change we make when we rotate the knob while the cycle is running, cause the Arduino is ‘busy’ counting its delay. So, after we made the hardware User Interface with a rotating knob we will solve this delay() issue.

— 0 —

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s