OK I'm going to rephrase my previous thread/question and simplify it..
Starting from scratch, how can I read what's being sent to the Arduino via serial, an ASCII string, and then a) echo it to my LCD or b) parse it so that the Arduino can do something upon receipt of certain strings?
I'm sure it only needs to be a few lines of code (to create a buffer, write to it, then read from it..?) but I really cannot find a definitive starting point online anywhere.
#include <LiquidCrystal.h>
LiquidCrystal lcd(4, 5, 6, 7, 8, 9);
void setup() {
Serial.begin(9600); //INTIALISING THE SERIAL PORT
lcd.begin(4,20);
}
void loop() {
}
How is a packet defined? How do you know that you are starting to read a packet? How do you know when you are done reading that packet?
If the packet has start and end markers, it is easy to read all the data:
#define SOP '<'
#define EOP '>'
bool started = false;
bool ended = false;
char inData[80];
byte index;
void setup()
{
Serial.begin(57600);
// Other stuff...
}
void loop()
{
// Read all serial data available, as fast as possible
while(Serial.available() > 0)
{
char inChar = Serial.read();
if(inChar == SOP)
{
index = 0;
inData[index] = '\0';
started = true;
ended = false;
}
else if(inChar == EOP)
{
ended = true;
break;
}
else
{
if(index < 79)
{
inData[index] = inChar;
index++;
inData[index] = '\0';
}
}
}
// We are here either because all pending serial
// data has been read OR because an end of
// packet marker arrived. Which is it?
if(started && ended)
{
// The end of packet marker arrived. Process the packet
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
}
Where the comment says Process the packet, that can include printing the packet on the LCD.
if(started && ended)
{
lcd.print(inData);
// The end of packet marker arrived. Process the packet
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
Is that correct?
'cos nothing happens if I sent or something through the IDE's serial monitor
Are you able to show anything on the LCD? Add a Serial.println() statement to echo what is received from the serial monitor, to make sure communication is working.
OK it seems to work fine now!! I've got it showing AT responses from my Sony Ericsson T630 on the LCD using + and , as the start and end characters. It successfully shows up phone numbers when I ring the phone anyway
How can I get it to 'clear' the display after, say, xx minutes of it displaying the same string without any 'new' serial data, and revert back to an "idle" screen? Cos at the moment it's just showing the last received serial data
Will work out how to trigger other hardware in due course...
Thanks
My code so far:
#include <LiquidCrystal.h>
#define SOP '+'
#define EOP ','
bool started = false;
bool ended = false;
char inData[80];
byte index;
LiquidCrystal lcd(4, 5, 6, 7, 8, 9);
void setup()
{
Serial.begin(9600);
lcd.begin(4, 20);
Serial.println("AT+CLIP=1");
delay(1000);
Serial.println("AT+CMGF=1");
delay(1000);
Serial.println("AT+CNMI=2,1,0,0,0");
delay(1000);
}
void loop()
{
// Read all serial data available, as fast as possible
while(Serial.available() > 0)
{
char inChar = Serial.read();
if(inChar == SOP)
{
index = 0;
inData[index] = '\0';
started = true;
ended = false;
}
else if(inChar == EOP)
{
ended = true;
break;
}
else
{
if(index < 79)
{
inData[index] = inChar;
index++;
inData[index] = '\0';
}
}
}
// We are here either because all pending serial
// data has been read OR because an end of
// packet marker arrived. Which is it?
if(started && ended)
{
lcd.clear();
lcd.print(inData);
// The end of packet marker arrived. Process the packet
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
}
#include <LiquidCrystal.h>
#define SOP '+'
#define EOP ','
bool started = false;
bool ended = false;
char inData[80];
byte index;
unsigned long showedNumber = 0;
unsigned long showTime = 120000UL; // 15 minutes (use whatever value you like here)
LiquidCrystal lcd(4, 5, 6, 7, 8, 9);
void setup()
{
Serial.begin(9600);
lcd.begin(4, 20);
Serial.println("AT+CLIP=1");
delay(1000);
Serial.println("AT+CMGF=1");
delay(1000);
Serial.println("AT+CNMI=2,1,0,0,0");
delay(1000);
}
void loop()
{
// Read all serial data available, as fast as possible
while(Serial.available() > 0)
{
char inChar = Serial.read();
if(inChar == SOP)
{
index = 0;
inData[index] = '\0';
started = true;
ended = false;
}
else if(inChar == EOP)
{
ended = true;
break;
}
else
{
if(index < 79)
{
inData[index] = inChar;
index++;
inData[index] = '\0';
}
}
}
// We are here either because all pending serial
// data has been read OR because an end of
// packet marker arrived. Which is it?
if(started && ended) // The end of packet marker arrived. Process the packet
{
lcd.clear();
lcd.print(inData);
if(millis() - showedNumber > showTime)
{
lcd.clear();
lcd.print("Idle.");
}
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
}
I wrote that without access to the Arduino IDE, and will try it when I get home this evening if so.
Why not? Can't you tell where the while(Serial.available()) block ends? Put that code after the while block's ending }.
Can you suggest how/where I might modify my code to make it work?
Not any more than I already have. The ability to understand where a block starts and ends is fundamental to programming. I can't do more than tell you that a block starts with a statement followed by a { and ends when the matching } is found.
I put all the code you needed to add to your sketch in an earlier reply. You copied 3 of 4 blocks of code to your sketch. You got 2 in the right place, and one in the wrong place. Copy the missing one, to the right place, and move the misplaced block.
I was actually referring to your "When you do display a value to the LCD, you need to set showedNumber. The code to do that is missing." but then I missed the shownumber block in your previous post. My mistake.
Anyway..
#include <LiquidCrystal.h>
#define SOP '+'
#define EOP ','
bool started = false;
bool ended = false;
char inData[80];
byte index;
unsigned long showedNumber = 0;
unsigned long showTime = 120000UL; // 15 minutes (use whatever value you like here)
LiquidCrystal lcd(4, 5, 6, 7, 8, 9);
void setup()
{
Serial.begin(9600);
lcd.begin(4, 20);
Serial.println("AT+CLIP=1");
delay(1000);
Serial.println("AT+CMGF=1");
delay(1000);
Serial.println("AT+CNMI=2,1,0,0,0");
delay(1000);
}
void loop()
{
// Read all serial data available, as fast as possible
while(Serial.available() > 0)
{
char inChar = Serial.read();
if(inChar == SOP)
{
index = 0;
inData[index] = '\0';
started = true;
ended = false;
}
else if(inChar == EOP)
{
ended = true;
break;
}
else
{
if(index < 79)
{
inData[index] = inChar;
index++;
inData[index] = '\0';
}
}
}
// We are here either because all pending serial
// data has been read OR because an end of
// packet marker arrived. Which is it?
if(started && ended) // The end of packet marker arrived. Process the packet
{
lcd.clear();
lcd.print(inData);
showedNumber = millis();
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
if(millis() - showedNumber > showTime)
{
lcd.clear();
lcd.print("Idle.");
}
}
Sure. Create a boolean variable. Set it false when serial data arrives. Set it true when the Idle message is printed. Only print the idle message when the variable is true.
bool isIdle = true;
void loop()
{
while(Serial.available() > 0)
{
isIdle = false;
// Leave the rest of the code here
}
if(millis() - showedNumber > showTime && !isIdle)
{
lcd.clear();
lcd.print("Idle.");
isIdle = true;
}
}
Thanks! I've since discovered that, because I now have it printing two sets of temperatures on the idle screen from digital temperature sensors, which flicker every half a second or so as they update (due to the sensors.requestTemperatures(); command) that I can't 'slow down' the updating of this without introducing a delay thus making the boolean idea a bit redundant... Unless you've got a way around that? Which I'm sure you have..
I'm learning quite a bit as I go along, with this, by the way!
See the idleScreen() function for the flickering issue
Code:
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
#define ONE_WIRE_BUS 2
#define SOP '+'
#define EOP ','
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
bool started = false;
bool ended = false;
char inData[80];
byte index;
unsigned long showedNumber = 0;
unsigned long showTime = 10000UL; // 120000 milliseconds (2 minutes)
LiquidCrystal lcd(4, 5, 6, 7, 8, 9);
bool isIdle = true;
void bootScreen()
{
lcd.clear();
lcd.setCursor(3,1);
lcd.print("Initialising...");
delay(1000);
}
void tempSMS()
{
int thisByte = '"';
Serial.print("AT+CMGS=");
delay(500);
Serial.write(thisByte);
delay(500);
Serial.print("07759737941");
delay(500);
Serial.write(thisByte);
Serial.println("");
delay(500);
Serial.print("Temperature warning. \x1A");
delay(500);
}
void idleScreen()
{
sensors.requestTemperatures(); // Send the command to get temperatures
lcd.clear();
lcd.setCursor(6,0);
lcd.print("Ready.");
lcd.setCursor(0,2);
lcd.print ("Out:");
lcd.setCursor(0,3);
lcd.print("In:");
lcd.setCursor(10,2);
lcd.print("C");
lcd.setCursor(10,3);
lcd.print("C");
lcd.setCursor(5,2);
lcd.print(sensors.getTempCByIndex(0), 1); // Outdoor Temp Sensor
lcd.setCursor(5,3);
lcd.print(sensors.getTempCByIndex(1), 1); // Indoor Temp Sensor
// if (sensors.getTempCByIndex(1) > 26)
// {
// tempSMS();
// }
isIdle = true;
}
void setup()
{
Serial.begin(9600);
lcd.begin(4, 20);
sensors.begin();
bootScreen();
Serial.println("AT+CLIP=1");
delay(1000);
Serial.println("AT+CMGF=1");
delay(1000);
Serial.println("AT+CNMI=2,1,0,0,0");
delay(1000);
idleScreen();
}
void loop()
{
// Read all serial data available, as fast as possible
while(Serial.available() > 0)
{
isIdle = false;
char inChar = Serial.read();
if(inChar == SOP)
{
index = 0;
inData[index] = '\0';
started = true;
ended = false;
}
else if(inChar == EOP)
{
ended = true;
break;
}
else
{
if(index < 79)
{
inData[index] = inChar;
index++;
inData[index] = '\0';
}
}
}
// We are here either because all pending serial
// data has been read OR because an end of
// packet marker arrived. Which is it?
if(started && ended) // The end of packet marker arrived. Process the packet
{
lcd.clear();
lcd.print(inData);
showedNumber = millis();
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
if(millis() - showedNumber > showTime)
{
idleScreen();
}
}
See the idleScreen() function for the flickering issue
The flickering is caused by you clearing and reprinting the whole screen. You should clear the screen only once, before the idleScreen() function is called. The idleScreen function should really only overwrite the temperature data already shown.
So, you need to break idleScreen() into two parts or more parts - one to clear the screen, one to show the static data, and one to show the dynamic data (the temperatures). It is probably not really necessary to update the dynamic data as often as you are. Once a minute is probably often enough.
Thanks. I managed this on a previous sketch I wrote which just displayed temps (i.e. the static data was in void setup() and the dynamic (temperatures) was in a loop with the delay I chose).
How can I go about setting the delay (i.e. for a minute) and yet it still 'listens' for new serial data during this delay? The way I tried, the delay 'overwrote' the boolean bits. If that makes sense.
In the function that displays the dynamic data, see if it is time to display data, just like you are doing with the serial data. If it's time, do it. Otherwise, just return.