Hi, I've been trying to build a program for a multi-material dispenser. Down the road, I am planning to have it take input on the number of chubs(packets), material needed, and weight of each material per packet to be collected from bins by having the arduino send Gcode to a smoothieboard-based cartesian robot.
My first attempts at coding this ambitious project did not seem to start well, so I started over and sampled part of the code from Robin2's informative article. (Planning and Implementing an Arduino Program)
It worked fine for me until I added the second question for materials needed. Then the serial monitor gave me this response :
23:39:28.610 -> Booting herb collector 23:39:30.602 -> How many chubs do you need?(0~250)
*23:39:32.287 -> You entered the following : 23 herbs * 23:39:33.283 -> How many chubs do you need?(0~250) 23:39:36.049 -> You entered the following : 12 herbs
What I need it to do is ask for the number of chubs, then ask which herbs I need.
But currently it only asks about the chubs, and sends responds about herbs which means the response is coming from the second part.
I tried putting a delay in between the two questions but it didn't seem to work.
This is the code I have been working with.
It's basically two copies of Robin2's code on user input and response back to back.
My setup is an Arduino Mega on serial monitor.
const byte herbs = 6; // num of herbs
const char slot[herbs+1] = {'황기','복령','당귀','계지','녹용','애엽'}; // name of herb slots(or bins) using arrays. Hand added for now
String herbcheck; // holds herb input while comparing with above array.. implementation later..
String chub; // how many chubs(packets) will be needed
byte gram[herbs]; // how many grams of a herb in each chub
//======for user question========
const unsigned long questionInterval = 1000;
unsigned long currentMillis;
unsigned long prevResponseMillis = 0;
boolean waitingForResponse = false;
//=====for user response=========
const byte buffSize = 31;
char userResponse[buffSize];
const char endMarker = '\r';
byte bytesRecvd = 0;
boolean ackResponse = false;
void setup() {
Serial.begin(9600); // start to listen to serial communication(baudrate)
Serial.println("Booting herb collector");
delay(2000);
}
void loop() {
currentMillis = millis();
askForChubs();
getChubs();
confirmChubs();
askForHerbs();
getHerbs();
confirmHerbs();
/* askForGrams();
getGrams();
confirmGrams();
printResults();
*/
}
void askForChubs() {
if (waitingForResponse == true) {
return;
}
if (currentMillis - prevResponseMillis >= questionInterval) {
Serial.println("How many chubs do you need?(0~250)");
waitingForResponse = true;
bytesRecvd = 0;
}
}
void getChubs() {
if (waitingForResponse == false) {
return;
}
if(Serial.available() == 0) {
return;
}
char inChar = Serial.read();
if (inChar != endMarker) {
userResponse[bytesRecvd] = inChar;
bytesRecvd ++;
if (bytesRecvd == buffSize) {
bytesRecvd = buffSize - 1;
}
}
else { // inChar is the endMarker
waitingForResponse = false;
userResponse[bytesRecvd] = 0;
prevResponseMillis = currentMillis;
ackResponse = true;
}
}
void confirmChubs() {
if (ackResponse == false) {
return;
}
chub = String(userResponse);
Serial.print("You entered the following : ");
Serial.print(chub);
Serial.println(" chubs ");
ackResponse = false;
delay(questionInterval+1);
}
void askForHerbs() {
if (waitingForResponse == true) {
return;
}
if (currentMillis - prevResponseMillis >= questionInterval) {
Serial.println("Which herbs do you need?");
waitingForResponse = true;
bytesRecvd = 0;
}
}
void getHerbs() {
if (waitingForResponse == false) {
return;
}
if(Serial.available() == 0) {
return;
}
char inChar = Serial.read();
if (inChar != endMarker) {
userResponse[bytesRecvd] = inChar;
bytesRecvd ++;
if (bytesRecvd == buffSize) {
bytesRecvd = buffSize - 1;
}
}
else { // inChar is the endMarker
waitingForResponse = false;
userResponse[bytesRecvd] = 0;
prevResponseMillis = currentMillis;
ackResponse = true;
}
}
void confirmHerbs() {
if (ackResponse == false) {
return;
}
herbcheck = String(userResponse);
Serial.print("You entered the following : ");
Serial.print(herbcheck);
Serial.println(" herbs ");
ackResponse = false;
}
Sorry about the long code. I thought most of the code would be necessary if anyone wanted to help out though. Googling or searching the forum was fruitless.
Any help would be greatly appreciated.
Thank you!
What have you got the line ending set to in the Serial monitor ? Anything other than no line ending will leave data in the Serial buffer so that it is available() the next time you check, even if the user has not entered a second answer
To protect against GIGO, the program should test that the answer is within the scope of the question. How many begs an answer that is a number. Herbs is not a number.
This is a problem. You can't fit those unicode characters into a single byte:
2:31: warning: character constant too long for its type
const char slot[herbs + 1] = {'황기', '복령', '당귀', '계지', '녹용', '애엽'}; // name of herb slots(or bins) using arrays. Hand added for now
^~~~~~~~
:2:41: warning: character constant too long for its type
const char slot[herbs + 1] = {'황기', '복령', '당귀', '계지', '녹용', '애엽'}; // name of herb slots(or bins) using arrays. Hand added for now
^~~~~~~~
2:51: warning: character constant too long for its type
const char slot[herbs + 1] = {'황기', '복령', '당귀', '계지', '녹용', '애엽'}; // name of herb slots(or bins) using arrays. Hand added for now
^~~~~~~~
2:61: warning: character constant too long for its type
const char slot[herbs + 1] = {'황기', '복령', '당귀', '계지', '녹용', '애엽'}; // name of herb slots(or bins) using arrays. Hand added for now
^~~~~~~~
2:71: warning: character constant too long for its type
const char slot[herbs + 1] = {'황기', '복령', '당귀', '계지', '녹용', '애엽'}; // name of herb slots(or bins) using arrays. Hand added for now
I suggest you use character pointers and string literals:
const char *slot[herbs + 1] = {"황기", "복령", "당귀", "계지", "녹용", "애엽"}; // name of herb slots(or bins) using arrays. Hand added for now
I think the problem is that some of your state flags (waitingForResponse, ackResponse) are used in multiple states. Changing the design to a "finite state machine" keeps all the state information in one variable named 'State'. It also allows the user input task to be in a single function instead of repeated in each state that gets user input.
enum States {askForChubs, getChubs, confirmChubs,
askForHerbs, getHerbs, confirmHerbs,
askForGrams, getGrams, confirmGrams,
printResults
} State = askForChubs;
const byte herbs = 6; // num of herbs
const char *slot[herbs + 1] = {"황기", "복령", "당귀", "계지", "녹용", "애엽"}; // name of herb slots(or bins) using arrays. Hand added for now
String herbcheck; // holds herb input while comparing with above array.. implementation later..
String chub; // how many chubs(packets) will be needed
byte gram[herbs]; // how many grams of a herb in each chub
//======for user question========
const unsigned long questionInterval = 1000;
unsigned long currentMillis;
unsigned long prevResponseMillis = 0;
boolean waitingForResponse = false;
//=====for user response=========
const byte buffSize = 31;
char userResponse[buffSize];
const char endMarker = '\r';
byte bytesRecvd = 0;
boolean ackResponse = false;
void setup()
{
Serial.begin(9600); // start to listen to serial communication(baudrate)
Serial.println("Booting herb collector");
delay(2000);
}
void GetUserInput(enum States nextState)
{
if (Serial.available())
{
char inChar = Serial.read();
if (inChar == endMarker)
{
userResponse[bytesRecvd] = 0;
prevResponseMillis = currentMillis;
State = nextState;
}
else
{
userResponse[bytesRecvd] = inChar;
bytesRecvd ++;
if (bytesRecvd == buffSize)
{
bytesRecvd = buffSize - 1;
}
}
}
}
void loop()
{
switch (State)
{
case askForChubs:
if (currentMillis - prevResponseMillis >= questionInterval)
{
Serial.println("How many chubs do you need?(0~250)");
State = getChubs;
bytesRecvd = 0;
}
break;
case getChubs:
GetUserInput(confirmChubs);
break;
case confirmChubs:
chub = String(userResponse);
Serial.print("You entered the following : ");
Serial.print(chub);
Serial.println(" chubs ");
State = askForHerbs;
break;
case askForHerbs:
if (currentMillis - prevResponseMillis >= questionInterval)
{
Serial.println("Which herbs do you need?");
bytesRecvd = 0;
State = getHerbs;
}
break;
case getHerbs:
GetUserInput(confirmHerbs);
break;
case confirmHerbs:
herbcheck = String(userResponse);
Serial.print("You entered the following : ");
Serial.print(herbcheck);
Serial.println(" herbs ");
State = askForChubs;
break;
// These case are not used yet:
case askForGrams:
case getGrams:
case confirmGrams:
case printResults:
State = askForChubs;
}
}
@johnwasser
Wow, thank you for all the suggestions. The characters in the array I will change to English for now and move on to pointers and string literals as the project progresses.
I am searching about enums and definitely will switch over to the enum structure. But the code that you have kindly written does not give any additional outputs at the moment. I am only getting 'booting herb collector' message at startup. Could you give a slight hint as to where I should try tweaking this further?
@UKHeliBob
Thank you. I was indeed on something else (carriage return), so I tried changing it to all the options with varied results :
no line ending & new line : on message appears after the initial 'Booting herb collector' message
carriage return & both NL & CR : both works out the same way as in the initial question
So something is definitely involved there in the serial buffer, but I can't figure out just what. Would these results suggest anything to you?
@herbschwarz
Thanks, I will keep that in mind, and try to implement it as my skills progress. For the moment the code is processing the answer for the first question in the format for the second question's answer (I think).