The approach noted in the question – having an identifying character at the front of each number – is a workable approach. Other approaches include:
• always sending numbers in pairs, eg 2000,300
would denote A2000 B300
.
• encoding the A or B as part of the number; eg, the range 0-4999 could denote A delays of 0 to 4999, while the range 5000-9999 could denote B delays of 0 to 4999.
• sending A delays via decimal digits, and B delays via letter-substitutes for digits; eg DAA could encode a B delay of 300.
One of the easiest-to-parse approaches is to have an identifying character at the end of each number, rather than at the front. This allows your code to pick up digits until a letter appears, and then dispatch based on the letter. Here's some example code:
// Sketch that accepts numbers and action code via serial input.
// Silently ignores any numbers not followed by a valid action letter.
// Does not test for number overflow. - jiw 3 Nov 2016
void setup() {
Serial.begin(115200); // init serial port
}
unsigned int val=0;
void loop() {
char inc;
while (Serial.available()) { // Get characters
inc = Serial.read();
if (inc >= '0' && inc <= '9') {
val = 10*val + (inc-'0');
} else {
switch (toupper(inc)) {
case 'A':
Serial.print("Case A got ");
Serial.println(val);
break;
case 'B':
Serial.print("Case B got ");
Serial.println(val);
break;
default :
; // handle blanks, returns, etc
}
val = 0; // Set val to zero after any non-digit
}
}
}
Edit 2: This edit incorporates some code suggestions from comments into the answer.
If you need to know whether a number for val1 has been specified -- that is, if you handle items like 500B
as well as 300,500B
-- you can set/test/clear a flag:
...
case ',':
val1=val; v1flag=1;
break;
...
case 'B':
if (!v1flag) val1=val1default;
Serial.print("B stuff with val and val1");
v1flag=0;
break;
...
To retain when the power is off, you can write a recordData()
function, that when called will write selected data to EEPROM if the value has changed. In setup()
, load data from where you stored it. For data validation, you can store in the cell after a value v its complement, ~v. If when you reload data it fails the validation test, use a default value instead, and write it to EEPROM for future use.
The code snippet shown next, from kaisarGreat's 8 May comment, has several problems to be corrected:
case a :
WATEREQFORN1 = val;
EEPROM.put(0, WATEREQFORN1);
case b :
WATEREQFORN2 = val;
EEPROM.put(1, WATEREQFORN2);
case d :
DELAY = val;
EEPROM.put(3, DELAY);
Logic or syntax problem: Missing break
statements at ends of cases. This error will allow execution to flow from one case into the next. While there are situations where that's useful, this is not one.
Logic or syntax problem: Unless a
, b
, ... are declared constants they won't work as case designators. Perhaps use 'A'
, 'B'
, ... instead.
Style problem: Variable names in all upper case. Conventionally, C and C++ programmers use all-upper-case names to designate defined constants or macros. Contravening that convention can lead to confusion if other people use or maintain the code. Instead of WATEREQFORN1
consider waterEq1
.
Allocation problem: Presumably the WATEREQ...
variables are multiple bytes in length. EEPROM.get()
and put()
read and write data types with multiple bytes using several consecutive bytes of EEPROM. For example, if WATEREQFORN1
is a real and takes 4 bytes, it will occupy cells 0, 1, 2, 3 in EEPROM.
The following code snippet fixes some of those problems.
enum { WaterAddr1=0, WaterAddr2=WaterAddr1+sizeof waterEq1, DelayAddr=WaterAddr2+sizeof waterEq2 };
...
case 'A':
waterEq1 = val;
EEPROM.put(WaterAddr1, waterEq1);
break;
case 'B':
waterEq2 = val;
EEPROM.put(WaterAddr2, waterEq2);
break;
case 'D':
Delay = val;
EEPROM.put(DelayAddr, Delay);
break;
...