[SOLVED] Read serial data in a case statement... I think??

Hi Arduino enthusiasts

I have a problem I'm struggling to figure out. In a nutshell; a python script requests sensor values which it then stores in a database and the second python script pulls data from the data and stores it into arrays on the arduino. I recently created a switch case to either "write" sensor values or "read" relay values. Now the problem comes when dealing with the case "ReadRelays". The python script writes 2 strings; the case statement correctly uses the first string and "forwards" in on the correct case. Straight after, the python script writes another string with the data I want enclosed in delimeters. At the moment it drops the second of the strings and outputs their ascii values in the default case statement. Instead I want this string to "feed into void RedRelays ()"

Is this at all possible and if not how can I get it to work?

Below is a copy of the my code.

/*-----( Import needed libraries )-----*/
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <RTClib.h>

/*-----( Declare Constants and Pin Numbers )-----*/
#define SOP '('
#define EOP ')'
#define photopin A0
const int temp_pin = A1;
bool started = false;
bool ended = false;

#define RELAY_ON 1
#define RELAY_OFF 0
#define Relay_1  30  // Arduino Digital I/O pin number
#define Relay_2  31
#define Relay_3  32
#define Relay_4  33 
#define Relay_5  34  // Arduino Digital I/O pin number
#define Relay_6  35
#define Relay_7  36
#define Relay_8  37  // Arduino Digital I/O pin number

/*-----( Declare objects )-----*/
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(temp_pin);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
//Real Time Clock
RTC_DS1307 RTC;

/*-----( Declare Variables )-----*/
// Assign the addresses of your 1-Wire temp sensors.
DeviceAddress probe1 = { 0x28, 0x64, 0x23, 0xBD, 0x03, 0x00, 0x00, 0x5F }; 
DeviceAddress probe2 = { 0x28, 0xAB, 0x10, 0xBD, 0x03, 0x00, 0x00, 0x2E };

float photolevel;
float dallas1;
float dallas2;
char photo1[10];
char temp1[10];
char temp2[10];
char inData[80];
char temp;
int count = 0;
int array[11];
int incomingbyte;
byte index;

void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);
  sensors.begin();     //Get DS18B20 temperatures
  sensors.setResolution(probe1, 10); //set resolution to 10bit
  sensors.setResolution(probe2, 10); //set resolution to 10bit
  Wire.begin();        // Start the Wire (I2C communications)
  RTC.begin();         // Start the RTC Chip
}

void loop() {
  
  if (Serial.available()>0)
  {
    incomingbyte=Serial.read();
    
    switch (incomingbyte) {
      case 'a':
        //Serial.println(incomingbyte);
        WriteSensors();
        SensorAction();
        break;
      case 'b':
        //Serial.println(incomingbyte);
        ReadRelays();
        RelayAction();
        break;
      default:
      //Serial.println(incomingbyte);
      break;
    }
  }
}

void WriteSensors()
{
  DateTime now = RTC.now();  //Get time from RTC
  photolevel = analogRead(photopin);  //Read light level
  
  sensors.requestTemperatures();
  dallas1 = sensors.getTempC(probe1);
  dallas2 = sensors.getTempC(probe2);
  
  dtostrf(photolevel, 1, 0, photo1);
  dtostrf(dallas1, 1, 2, temp1);
  dtostrf(dallas2, 1, 2, temp2);
  
  String tempAsString1 = String(photo1);
  String tempAsString2 = String(temp1);
  String tempAsString3 = String(temp2);
  
  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(" ");  
  Serial.println(tempAsString1 + " " + tempAsString2 + " " + tempAsString3); 

}

void ReadRelays()
{
  // 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
        
    if (strlen(inData) > 0)
   {
     
      char *token = strtok(inData, ",");
      if(token)
      {
         index = 0;
         
         array[index] = atoi(token);

         while (token = strtok(NULL, ","))
         {
            array[index++] = atoi(token);
         }
      }
    }
      
    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
    
  }
} 

void RelayAction() {
  
  Serial.print(array[0]);
  Serial.print(array[1]);
  Serial.print(array[2]);
  Serial.print(array[3]);
  Serial.print(array[4]);
  Serial.print(array[5]);
  Serial.print(array[6]);
  Serial.print(array[7]);
  Serial.print(array[8]);
  Serial.print(array[9]);
  Serial.print(array[10]);
  Serial.println(array[11]); 
  
}

void SensorAction() {
  
  
}

I have got ReadRelays() to work separately in void loop() but not in a case statement.

Thanks,
Damo

If I get it correctly, you are expecting data to be in the buffer when readrelays is called. This might not yet be the case. In the main loop it will be processed because if you missed it the first time you will get it the next time.
Could I suggest to put a simple waitForData() loop in readrelays at the beginning:

void ReadRelays()
{
  //
  // wait for data
  //
  while (Serial.available() == 0);

  // Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {

nicoverduin:
If I get it correctly, you are expecting data to be in the buffer when readrelays is called. This might not yet be the case. In the main loop it will be processed because if you missed it the first time you will get it the next time.
Could I suggest to put a simple waitForData() loop in readrelays at the beginning:

void ReadRelays()

{
  //
  // wait for data
  //
  while (Serial.available() == 0);

// Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {

Thanks for the feedback. I gave that a go but arrays come back as 0000000000, i.e. empty.

If it helps my python script includes, and I know this is not a python forum, but I ser.write("b") then a few lines later after selecting from the database I ser.write(stringfromdatabase).

I do not know if it is the pyton or Arduino, but you could make a test script saying "This is the serial or so" if that comes through then the problem is in the Arduino, otherwise it is the python.

nicoverduin:
I do not know if it is the pyton or Arduino, but you could make a test script saying "This is the serial or so" if that comes through then the problem is in the Arduino, otherwise it is the python.

Thanks for the reply, sorry for the really late reply!

Yes I tried something like that. Python does send the information but I'm guessing too fast or too slow. I have a feeling it has something to do with the case statement. I looked at Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking from Nick Gammon, I think a state machine is required. His code is way past my understanding but think its the way.

Here my cleaned up code

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>

#define SOP '('
#define EOP ')'
#define photopin A0
#define temp1 A1
#define temp2 A2
bool started = false;
bool ended = false;

OneWire oneWire1(temp1);
OneWire oneWire2(temp2);

DallasTemperature sensor1(&oneWire1);
DallasTemperature sensor2(&oneWire2);

char inData[80];
char temp;
int count = 0;
int array[11];
byte index;

void setup()
{
Serial.begin(9600);
sensor1.begin(); //Get DS18B20 temperatures
sensor2.begin(); //Get DS18B20 temperatures
Wire.begin(); // Start the Wire (I2C communications)
}

void loop ()
{
SerialCommunication();
}

void SerialCommunication()
{
if (Serial.available()>0)
{
char inChar = Serial.read();
switch (inChar)
{
case 'r':
Sensors();
break;
case 'w':
Relays();
ProcessRelays();
break;
default:
break;
}
}
}

void Sensors()
{
sensor1.requestTemperatures();
sensor2.requestTemperatures();

Serial.print(analogRead(photopin));
Serial.print(",");
Serial.print(sensor1.getTempCByIndex(0));
Serial.print(",");
Serial.println(sensor2.getTempCByIndex(0));
}

void Relays()
{
while (Serial.available() ==0);
// 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

if (strlen(inData) > 0)
{

char *token = strtok(inData, ",");
if(token)
{
index = 0;

array[index] = atoi(token);

while (token = strtok(NULL, ","))
{
array[index++] = atoi(token);
}
}
}

// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
}

void ProcessRelays()
{
Serial.println(array[0]);
Serial.println(array[1]);
Serial.println(array[2]);
}

How can I get this to work?

Thanks,
Damian

Could I suggest to put a simple waitForData() loop in readrelays at the beginning:

That will wait for ONE byte to become available.

Python does send the information but I'm guessing too fast or too slow.

It is impossible to send serial data too fast. Serial data, by nature, is slow.

Since the Python script is sending a delimiter at the end of the packet, it is quite reasonable to use a while loop in the ReadRelays() function (which isn't reading relays, by the way) that loops until the end-of-packet marker arrives. You can make that while loop terminate if the end-of-packet marker hasn't arrived within a reasonable amount of time, so the Arduino doesn't block waiting for data that is never going to arrive (because it got lost/mangled).

PaulS:
Since the Python script is sending a delimiter at the end of the packet, it is quite reasonable to use a while loop in the ReadRelays() function (which isn't reading relays, by the way) that loops until the end-of-packet marker arrives. You can make that while loop terminate if the end-of-packet marker hasn't arrived within a reasonable amount of time, so the Arduino doesn't block waiting for data that is never going to arrive (because it got lost/mangled).

As always thanks for your feedback PaulS!!

The output of the python is in this format w(1,2,3) I think, as i'll explain. If I remove the "w" from being sent and loop the Relays() function then relay values are successfully stored in arrays. The delimiters sent from python are () but only around the relay values and not the w. If I change the "w" to say an "a" then nothing happens as the case statement breaks. So Arduino is reading the w, without being surrounded by delimiters.
Saying that, I want the case statement to read the first char (in this example "w") in SerialCommunicationl() and then start processing the packet in Relays() which is surrounded by delimiters. Is what I want to happen possible, and how can I do it?

Thanks,
Damian

So Arduino is reading the w, without being surrounded by delimiters.

When the whole packet is a single letter, delimiters are hardly needed.

Saying that, I want the case statement to read the first char (in this example "w") in SerialCommunicationl()

Which it does.

and then start processing the packet in Relays() which is surrounded by delimiters.

The Relays() function is surrounded by delimiters?

Is what I want to happen possible,

Yes. That's why I responded.

and how can I do it?

void Relays()
{
   char inChar = "";
   unsigned long startedReading = millis();
   unsigned long timeToWait = 10000; // Don't wait more than 10 seconds for the packet
   while(inChar != ')' && millis() - startedReading < timeToWait)
   {
       if(Serial.available() > 0)
       {
          inChar = Serial.read();
          // Use inChar the same way you were using it
       }
   }

   // The if(started && ended) block goes here...
}

Thanks PaulS,

My code now:

void Relays()
{
char inChar = "";
unsigned long startedReading = millis();
unsigned long timeToWait = 5000;
while (inChar != ')' && millis() - startedReading < timeToWait)
{
if(Serial.available() > 0)
{
// Read all serial data available, as fast as possible
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

if (strlen(inData) > 0)
{

char *token = strtok(inData, ",");
if(token)
{
index = 0;

array[index] = atoi(token);

while (token = strtok(NULL, ","))
{
array[index++] = atoi(token);
}
}
}

// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
}

Using that code I get an error "invalid conversion from 'const char* to 'char'? Is this part {char inChar="";} and this part {inChar = Serial.read();} correct?

Is this part {char inChar="";}

Oops, sorry, no. Those should be single quotes, not double quotes.

this part {inChar = Serial.read();} correct?

Yes.

Thanks heaps PaulS

Oops, sorry, no. Those should be single quotes, not double quotes.

I tried that already, which gave the error: empty character constant

EDIT: actually that did work, had to put a space between single quotes