Taking serial input and adding to an array

Hello All,

I’m working on a project that requires a user to provide a series of text inputs to be stored in array for processing once all the inputs have been collected. Basically I am trying to get the user to input a single letter followed by a number up to 99. Once all the inputs are collected the code would need to take each letter and its associated numeric value and execute a command based on the letter chosen for the time dictated by the numeric value. I have been able to do this with python 3 as per the following code:

#!/usr/bin/env python3
#import packages
import explorerhat as es
from time import sleep

#create list to hold instructions and their execuion time in seconds
test_list = []

#collects user input and checks for valid entries
def user_input():
    d = 0
    while d < 1:
        d = 0
        x = input("Please enter a command: ")
        if x == "w":
            return x
            d = d + 1
        elif x == "s":
            return x
            d = d + 1
        elif x == "a":
            return x
            d = d + 1
        elif x =="d":
            return x
            d = d + 1
        else:
            print("Only use a, b, c or d please!!")
            d = 0
    
#collects the length of time the user wants the instruction to run for in seconds
def user_time():
    e = 0
    while e < 1:
        e = 0
        z = int(input("Please enter a value between 1 and 60: "))
        if z < 1:
            print("Value too low!! Please enter a new value.")
        elif z > 60:
            print("Value too high!! Please enter a new value.")
        else:
            e = e + 1
    return z

#executes the list in FIFO order
def actions_tbc():
    while len(test_list)>0:
        a, b = test_list.pop(0)
        if a == "a":
            es.light.green.on()
            sleep(b)
            es.light.green.off()
        elif a == "b":
            es.light.red.on()
            sleep(b)
            es.light.red.off()
        elif a == "c":
            es.light.blue.on()
            sleep(b)
            es.light.blue.off()
        elif a =="d":
            es.light.yellow.on()
            sleep(b)
            es.light.yellow.off()

#flashy ending
def flashy_ending():
    print("Program complete. Goodbye!")
    for i in range(5):
        es.light.on()
        sleep(0.2)
        es.light.off()
        sleep(0.2)
    exit()
    
#executes functions to get user input and update the list
for i in range(4):
    x = user_input()
    z = user_time()        
    test_list.append([x, z])

#displays the instructions on screen               
print(test_list)

#action time
actions_tbc()

#flashy ending
if len(test_list) == 0:
    flashy_ending()

#program is complete

The trouble I am having is replicating this functionality in Arduino. So far I have tried modifying the examples in the serial input tutorial but I cannot get any to work the way I want. My current code so far is:

const byte numIn = 8;
char inputData[numIn];
int dat = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Arduino input test, please enter 8 characters:");
}

void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available() > 0){
  for (int i = 0; i <= 7; i++) {
    if (i == 8) {
      Serial.println(inputData);
      break;
    }
    else {
      Serial.print("Value "); Serial.print(i); Serial.println(": ");
      inputData[dat] = Serial.read();
      dat++;
  } 
  }
  }

However when this code is run and a value is entered in to the serial console the loop runs 8 times and just prints " Value ‘i’: " without actually storing any data or allowing further user input:

Arduino input test, please enter 8 characters:
Value 0:
Value 1:
Value 2:
Value 3:
Value 4:
Value 5:
Value 6:
Value 7:

To recap I am looking to input an arbitrary number of values into an array (both characters and numbers) via serial input then parse the array first in first and print to serial the inputted data eg:

input characters: a, b, c, d, e, f, g, h
input numbers: 1, 2, 3, 4, 5, 6, 7, 8

serial output:
a, 1, b, 2 etc

Any help would be much appreciated

What Arduino are you using?

With the regular Arduinos such as the Uno and Mega it is not a good idea to allocate memory at run time - the available memory is small and it is very easy to crash the program. Much better to allocate at compile time sufficient space for the largest list of inputs that you expect.

The risks of memory corruption still exist on Arduinos and ESPs (8266 or 32) with a great deal more so you might get away with it - until the moment when you are demonstrating it to an important friend or client :slight_smile:

...R
Serial Input Basics - simple reliable non-blocking ways to receive data.
Simple Python - Arduino demo

Thank you for responding so quickly. Im using a nano for testing this code but I want to eventually expand to a mega and control things like motors etc. Im just trying to nail down the basics of user input before taking it further.

Look closely at the following loop() code:

void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available() > 0) {
    for (int i = 0; i <= 7; i++) {
      if (i == 8) {
        Serial.println(inputData);
        break;
      }
      else {
        Serial.print("Value "); Serial.print(i); Serial.println(": ");
        inputData[dat] = Serial.read();
        dat++;
      }
    }
  }

There are several issues with this code

  1. You are checking to see if there is any data available from serial then trying to read 8 bytes! In other words as soon as 1 byte is available you are trying to read 8 bytes.
  2. In the for loop i will never equal 8.
  3. You are not printing the individual bytes your are receiving which is why you never see the data associated with each read.
  4. You will write past the end of inputData because you never reset dat.

Also, inputData is not a string, and you cannot expect Serial.print to produce correct results.

This would be much better:

const byte numIn = 8;
char inputData[numIn+1];
int dat = 0;

void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Arduino input test, please enter 8 characters:");
}

void loop() 
{
  // put your main code here, to run repeatedly:
  if (Serial.available() > 0) 
  {
    inputData[dat] = Serial.read();
    Serial.print("Value "); 
    Serial.print(dat); 
    Serial.print(": ");
    Serial.println(inputData[dat]);
    dat++;
    if (dat == numIn) 
    {
      inputData[numIn] = '\0';
      Serial.println(inputData);
      dat = 0;
    }
  }
}

Better still, would be to terminate as you go along, so you could print the string at any time

TheMemberFormerlyKnownAsAWOL:
Better still, would be to terminate as you go along, so you could print the string at any time

I like the way you think.

ToddL1962:
This would be much better:

const byte numIn = 8;

char inputData[numIn+1];
int dat = 0;

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Arduino input test, please enter 8 characters:");
}

void loop()
{
  // put your main code here, to run repeatedly:
  if (Serial.available() > 0)
  {
    inputData[dat] = Serial.read();
    Serial.print("Value ");
    Serial.print(dat);
    Serial.print(": ");
    Serial.println(inputData[dat]);
    dat++;
    if (dat == numIn)
    {
      inputData[numIn] = '\0';
      Serial.println(inputData);
      dat = 0;
    }
  }
}

That has been a big help and is a big step in the right direction for me so a massive thank you Todd and everyone that has contributed so far. I'm going to work on this some more now and I'll share my code once done. Thank you all again.