Trouble sending file line by line through serial using state machine and IR remote control

Hello, I am trying to send a file line by line to the serial monitor ,the code should send the first line and wait for an input string "ok" from the user over serial. the Arduino Uno receive commands from a Sony remote control too and react to them using a state machine. So when I press power button on the remote a txt file is opened and the "sending line-wait for ok" process starts. Sony remote control has the bad habit of sending the same command 3 times each time a button is pressed.

My trouble is that I do not know how to give structure to my program, right now,if I press the power button , all the content in the array is sent , it seems my Detect() function is not working at all, only if I press the button again, new lines arrive to the monitor. I went in obfuscated mode and changed the position of my function from the bottom until the actual place with no results, then I tried using Serial.readBytesUntil (commented right now) and commenting my original function and same result: I need to push the power button to make some new lines appear.
So basically I cannot see my problem. May you guys have a look and share your thoughts? Any help is welcome!

#include "PetitFS.h"

/////////////SD CARD FILE SYSTEM//////////////
FILE *fptr;
FATFS fs;      /* File system object */
DIR dir;        /* Directory object */
FILINFO fno;      /* File information object */

/////// remote control/////////

const int start_bit PROGMEM = 2200;    //Start bit threshold (Microseconds)Sony protocol
const int bin_1  PROGMEM = 1000;    //Binary 1 threshold (Microseconds)
const int bin_0  PROGMEM = 400;     //Binary 0 threshold (Microseconds)
// Connect a 38KHz remote control sensor to the pin below
const byte irPin PROGMEM = 6;
int key;
int KeyCounter = 0; //to count number of received commands.
const long  interval PROGMEM = 4000;

char DetectOK[16];//an array to store ok responses while sending lines.

//---------------manage SD Card and sending lines ------------------
bool okToSendLine = true;

char bufSD[127];//for Sd file content.
//-----------------------------------------------------------------------------
unsigned long previousMillisJog = 0;
unsigned long previousMillisJog2 = 0;
unsigned long previousMillistest = 0;
unsigned long previousMillistest2 = 0;
int FirstSample;
int LastSample;
bool lock = false; // for getting rid of repeated commands


void setup() {
  pinMode(irPin, INPUT);
  Serial.begin(115200);



}

void loop() {

  key = getIRKey();

  // Serial.println(key);//to find Sony remote control button codes.
  //------------------to get rid of repeated IR commands----------
  KeyCounter++;
  previousMillisJog = millis();

  if (previousMillisJog - previousMillisJog2 >= (interval / 80)) { //50
    previousMillisJog2 = previousMillisJog;
    FirstSample = KeyCounter; //save number of arrived commands

    // Serial.print("after interval FirstSample is:");
    //    Serial.println(FirstSample);

  }
  previousMillistest = millis();
  if (previousMillistest - previousMillistest2 >= interval / 10) { //400
    previousMillistest2 = previousMillistest;
    LastSample = FirstSample; // transfer number of commands after longer time

    // Serial.print("after interval LastSample is:");
    //   Serial.println(LastSample);

  }

  if (FirstSample = LastSample) {
    // Serial.print("yay");
    lock = false;
    KeyCounter = 0;
    FirstSample = 0;
    LastSample = 0;

  }//So basically we are checking above if the commands are still arriving or not by comparing number
  //of received commands after a long time. Necessary for jogging the steppers motors.


  switch (key) {

    case 229:
      //Some stuff
      break;

    case 186://PICK A FILE
      //mount SD card here and select one txt file to send.
      break;


    case 149: //-----------//POWER BUTTON SEND LINE BY LINE--------

      //only one press should send all lines one by one,
      //but it has to wait for an"ok" over serial between lines to send the next one.


      if (lock == false) { //because Sony remotes send at least 3 repeated commands per button.
        pf_open(fno.fname);  // Open test file.
        lock = true;
      }


      UINT nr;
      pf_read(bufSD, sizeof(bufSD), &nr); //we recolect chunks of data here everytime.the cursor/pointer to
      //read moves everytime we get an new line feed.

      for (int i = 0; okToSendLine == true && i < sizeof(bufSD); i++) {
        Serial.write(bufSD[i]);
        if (bufSD[i] == '\n') {
          //Serial.println("got a \n");
          okToSendLine = false;
          //pf_lseek(fs.fptr - (sizeof(bufSD) - (i + 1)));
        }
      }
      Detect();


      //int j = Serial.readBytesUntil('k', DetectOK, 16);
      //if (!j) {
      //okToSendLine = true;
      // }



      break;
    case 180:
      //Add pause, stop, exit ,etc to control sending lines during ejecution of case 149.
      break;

  }// end of switch loop
}// end of loop




void Detect() {
  static byte ndx = 0;
  char rc;
  while (Serial.available() > 0) {
    //while (Serial.available() > 0 && okToSendLine == false) {
    //delay(3);
    rc = Serial.read();

    if (rc != 'k') {
      DetectOK[ndx] = rc;
      ndx++;
      if (ndx >= 32) {
        ndx = 31;
      }
    }
    else { //it is a K!
      DetectOK[ndx] = rc; //we ADD THE K TO THE ARRAY
      DetectOK[ndx + 1] = '\0'; // terminate the string
      ndx = 0;
      //    okToSendLine = true;
    }
  }
  if (strstr(DetectOK, "ok")) { //in this case we keep array for further operations, out of while loop
    okToSendLine = true;
    DetectOK[0] = '\0'; //clean array.
  }
}



//////////////////NEW CODE FOR SONY REMOTE ONLY//////////////////////////////
int getIRKey() {

  int data[12];
  byte i;

  while (pulseIn(irPin, LOW) < start_bit) {}; //Wait for a start bit

  for (i = 0 ; i < 11 ; i++)
    data[i] = pulseIn(irPin, LOW);      //Start measuring bits, I only want   low pulses

  for (i = 0 ; i < 11 ; i++)            //Parse them
  {
    if (data[i] > bin_1)                //is it a 1?
      data[i] = 1;
    else if (data[i] > bin_0)           //is it a 0?
      data[i] = 0;
    else
      return -1;                        //Flag the data as invalid; I don't know what it is! Return -1 on invalid data
  }

  int result = 0;
  for (i = 0 ; i < 11 ; i++)            //Convert data bits to integer
    if (data[i] == 1) result |= (1 << i);

  return result;

}

For the printing and reading you can implement a small state machine.

In the first state, you send the line, in the second state you check if there is a reply and check if it is "OK". If yes, back to the previous state

That's the basics, can't help further at this moment.

1 Like

This is how it should be with this code.
Your sending code is situated inside "case 149" option of the switch(key) - so it works only after POWER is pressing. It send one line of the file and exits from the "case 149" option, regardless to the results of Detect() function. So for the sending next portion of the file you should press POWER again.

You Detect() function looks almost normal :slight_smile: with the exception of one bad error - you define array of 16bytes for receiving chars, but write to it up to 32 position...
And by the way, think about why you need a 16bytes buffer if you search a string with a length of only 2 bytes?

1 Like

This is probably an error (= Vs ==)

1 Like

Thanks PaulRB, if I do "== "instead of" =" , I start receiving repeated codes ,my intention here is to check if the button is released already, let say, I got 12 commands,then check again after some time and if I still have 12 commands then the button was released.

Array length corrected to 32 :grin: I want to use the response for more processing, the whole reply from the user can vary in length, but will always contain an "ok" in it. Not sure if I must correct my original post and break the continuity of comments so I'm including the corrected one here:

#include "PetitFS.h"

/////////////SD CARD FILE SYSTEM//////////////
FILE *fptr;
FATFS fs;      /* File system object */
DIR dir;        /* Directory object */
FILINFO fno;      /* File information object */

/////// remote control/////////

const int start_bit PROGMEM = 2200;    //Start bit threshold (Microseconds)Sony protocol
const int bin_1  PROGMEM = 1000;    //Binary 1 threshold (Microseconds)
const int bin_0  PROGMEM = 400;     //Binary 0 threshold (Microseconds)
// Connect a 38KHz remote control sensor to the pin below
const byte irPin PROGMEM = 6;
int key;
int KeyCounter = 0; //to count number of received commands.
const long  interval PROGMEM = 4000;

char DetectOK[32];//an array to store ok responses while sending lines.

//---------------manage SD Card and sending lines ------------------
bool okToSendLine = true;

char bufSD[127];//for Sd file content.
//-----------------------------------------------------------------------------
unsigned long previousMillisJog = 0;
unsigned long previousMillisJog2 = 0;
unsigned long previousMillistest = 0;
unsigned long previousMillistest2 = 0;
int FirstSample;
int LastSample;
bool lock = false; // for getting rid of repeated commands


void setup() {
  pinMode(irPin, INPUT);
  Serial.begin(115200);



}

void loop() {

  key = getIRKey();

  // Serial.println(key);//to find Sony remote control button codes.
  //------------------to get rid of repeated IR commands----------
  KeyCounter++;
  previousMillisJog = millis();

  if (previousMillisJog - previousMillisJog2 >= (interval / 80)) { //50
    previousMillisJog2 = previousMillisJog;
    FirstSample = KeyCounter; //save number of arrived commands

    // Serial.print("after interval FirstSample is:");
    //    Serial.println(FirstSample);

  }
  previousMillistest = millis();
  if (previousMillistest - previousMillistest2 >= interval / 10) { //400
    previousMillistest2 = previousMillistest;
    LastSample = FirstSample; // transfer number of commands after longer time

    // Serial.print("after interval LastSample is:");
    //   Serial.println(LastSample);

  }

  if (FirstSample = LastSample) {
    // Serial.print("yay");
    lock = false;
    KeyCounter = 0;
    FirstSample = 0;
    LastSample = 0;

  }//So basically we are checking above if the commands are still arriving or not by comparing number
  //of received commands after a long time. Necessary for jogging the steppers motors.


  switch (key) {

    case 229:
      //Some stuff
      break;

    case 186://PICK A FILE
      //mount SD card here and select one txt file to send.
      break;


    case 149: //-----------//POWER BUTTON SEND LINE BY LINE--------

      //only one press should send all lines one by one,
      //but it has to wait for an"ok" over serial between lines to send the next one.


      if (lock == false) { //because Sony remotes send at least 3 repeated commands per button.
        pf_open(fno.fname);  // Open test file.
        lock = true;
      }


      UINT nr;
      pf_read(bufSD, sizeof(bufSD), &nr); //we recolect chunks of data here everytime.the cursor/pointer to
      //read moves everytime we get an new line feed.

      for (int i = 0; okToSendLine == true && i < sizeof(bufSD); i++) {
        Serial.write(bufSD[i]);
        if (bufSD[i] == '\n') {
          //Serial.println("got a \n");
          okToSendLine = false;
          //pf_lseek(fs.fptr - (sizeof(bufSD) - (i + 1)));
        }
      }
      Detect();


      //int j = Serial.readBytesUntil('k', DetectOK, 32);
      //if (!j) {
      //okToSendLine = true;
      // }



      break;
    case 180:
      //Add pause, stop, exit ,etc to control sending lines during execution of case 149.
      break;

  }// end of switch loop
}// end of loop




void Detect() {
  static byte ndx = 0;
  char rc;
  while (Serial.available() > 0) {
    //while (Serial.available() > 0 && okToSendLine == false) {
    //delay(3);
    rc = Serial.read();

    if (rc != 'k') {
      DetectOK[ndx] = rc;
      ndx++;
      if (ndx >= 32) {
        ndx = 31;
      }
    }
    else { //it is a K!
      DetectOK[ndx] = rc; //we ADD THE K TO THE ARRAY
      DetectOK[ndx + 1] = '\0'; // terminate the string
      ndx = 0;
      //    okToSendLine = true;
    }
  }
  if (strstr(DetectOK, "ok")) { //in this case we keep array for further operations, out of while loop
    okToSendLine = true;
    DetectOK[0] = '\0'; //clean array.
  }
}



//////////////////NEW CODE FOR SONY REMOTE ONLY//////////////////////////////
int getIRKey() {

  int data[12];
  byte i;

  while (pulseIn(irPin, LOW) < start_bit) {}; //Wait for a start bit

  for (i = 0 ; i < 11 ; i++)
    data[i] = pulseIn(irPin, LOW);      //Start measuring bits, I only want   low pulses

  for (i = 0 ; i < 11 ; i++)            //Parse them
  {
    if (data[i] > bin_1)                //is it a 1?
      data[i] = 1;
    else if (data[i] > bin_0)           //is it a 0?
      data[i] = 0;
    else
      return -1;                        //Flag the data as invalid; I don't know what it is! Return -1 on invalid data
  }

  int result = 0;
  for (i = 0 ; i < 11 ; i++)            //Convert data bits to integer
    if (data[i] == 1) result |= (1 << i);

  return result;

}

So I want press the power button once, send the line,wait for ok,send the second.... etc.
but I am lost, should I implement another state machine inside the case 149 with a new control variable as sterretje said?

it is just like having all the working parts but being unable to put all together!

Right now only the first line is sent, then i need to press power again as it should be, but not what I wanted. I have another issue moving the read pointer of the file but i think I can correct it with the documentation.that is why i have the commented debugging line //Serial.println("got a \n"); but I'm still struggling putting all together.

I am not saying this is the only error in your code, clearly there are others. But that line I mentioned is an error and should be corrected. == is the operator used to compare 2 values in C/C++. = is the assignment operator and will set the variable on the left to the value on the right, even if written inside an if() statement. That's not what you wanted.

This won't fix your current problem, but I could not help noticing this code could be easily simplified and shortened:

//////////////////NEW CODE FOR SONY REMOTE ONLY//////////////////////////////
int getIRKey() {

  int result = 0;

  while (pulseIn(irPin, LOW) < start_bit) {}; //Wait for a start bit

  for (byte i = 0 ; i < 11 ; i++) {
    //Start measuring bits, I only want   low pulses
    int pulse = pulseIn(irPin, LOW);
    if (pulse > bin_1)                //is it a 1?
      result |= (1 << i);
    else if (pulse < bin_0)           //is it not a 0?
      return -1;                        //Flag the data as invalid; I don't know what it is! Return -1 on invalid data
  }

  return result;

}

wow! I will revise the "==" thing and will replace the code you shortened ,I also found last night the SerialEvent() function, (I didn't know such a thing existed!) putting code out of void loop() sounds like witchcraft to me :grin: will be back with the results asap!

my getIRKey() is blocking :frowning: both while loop and pulseIn are blocking,once I comment
key = getIRKey() both my Detect() function and SerialEvent() (not showed) can work in a flawless manner.
But I still want to be able to use some buttons for pause , exit , etc while sending the lines.
what do you guys think is the next step? use an IR library? I can only use pin 6 for the ir sensor on the UNO, so not sure if I can use interrupts based libraries. I have been looking for alternatives to while loops but the results are far beyond my understanding, something to do with events. I changed while for IF (inside the loop) and kindda worked but accepting the incoming commands very slowly.
or maybe the solution is there but I cannot see it :frowning:

I finally found a solution, I was working on two version of code simultaneously so I ended up ditching the one above and here the working one, using Shirriff's library, so now I can get the the incoming both serial response and ir commands (ignoring repeated ones) while sending the lines from the file. So I'm happy! :laughing: :grinning: any suggestion about improving or shrinking the sketch is always welcome!

#include "PetitFS.h"
#define DECODE_SONY
#include <Arduino.h>
#include "PinDefinitionsAndMore.h" //Define macros for input and output pin etc.
#include <IRremote.hpp>

//----------------File system stuff--------------------------
FILE *fptr;
FATFS fs;      /* File system object */
DIR dir;        /* Directory object */
FILINFO fno;      /* File information object */


//----------------for SD file manage and read serial response.-------
char bufSD[127];//for Sd file content,regardless of file size.Cool!
char DetectOK[32];//an array to store ok responses while sending lines.
bool okToSendLine = false;
bool flag = false;

void setup() {

  Serial.begin(115200);
  IrReceiver.begin(IR_RECEIVE_PIN);

}

void loop() {
  /*
     Check if received data is available and if yes, try to decode it.
     Decoded result is in the IrReceiver.decodedIRData structure.

     E.g. command is in IrReceiver.decodedIRData.command
     address is in command is in IrReceiver.decodedIRData.address
     and up to 32 bit raw data in IrReceiver.decodedIRData.decodedRawData
  */
  if (IrReceiver.decode()) {

    /* // Print a short summary of received data
      //IrReceiver.printIRResultShort(&Serial);
      if (IrReceiver.decodedIRData.protocol == UNKNOWN) {
         Serial.println(F("Received noise or an unknown (or not yet enabled) protocol"));
         // We have an unknown protocol here, print more info
         IrReceiver.printIRResultRawFormatted(&Serial, true);
      }*/
    //Serial.println(IrReceiver.decodedIRData.flags);
    /*
       !!!Important!!! Enable receiving of the next value,
       since receiving has stopped after the end of the current received data packet.
    */
    IrReceiver.resume(); // Enable receiving of the next value

    /*
       Finally, check the received data and perform actions according to the received command
    */

    if (IrReceiver.decodedIRData.command == 0x15) {//power button
      //Serial.print("HI"); // do something
      if (IrReceiver.decodedIRData.flags == false) { //not sure if the right thing to do
        //but it worked!

        // Initialize SD and file system.
        pf_mount(&fs); //to be able to extract sd card at almost any moment.
        pf_open("50.txt");// Open test file.
        flag = true;
        okToSendLine = true;
        beep();
      }
    }
  }//end of IrReceiver.decode()

  test();
  Detect();

}//end of loop


void test() {


  UINT nr;
  if (okToSendLine == true) {
    pf_read(bufSD, sizeof(bufSD), &nr); //we colect chunks of data here everytime.the cursor/pointer to
    //read moves everytime we get an new line feed.

    /*if (!nr) { //if(nr == 0)  also  if(nr) is if(nr != 0)
      bufSD[0]= '\0';
       Serial.println("eof");
       }*/


    if (nr < sizeof(bufSD)) {     //if we got the end of file print the remain characters and stop there
      for (int i = 0; i < nr; i++) {
        //----------------------------------------------
        Serial.write(bufSD[i]);
        if (bufSD[i] == '\n') { //we got the last chunk but we still need to find the end of lines there.
          okToSendLine = false;
          pf_lseek(fs.fptr - (nr - (i + 1)));
          //Serial.print(nr);
          break;
        }// the loop stops when i<nr it could be any number <127

      }
      flag = false; //so you stop the loop just below and start doing the one above loop instead
      //Serial.println(flag);
    }
    if (flag == true) {
      for (int i = 0; i < sizeof(bufSD); i++) {
        Serial.write(bufSD[i]);
        if (bufSD[i] == '\n') {
          okToSendLine = false;
          pf_lseek(fs.fptr - (sizeof(bufSD) - (i + 1)));
          break;
        }
      }
    }
  }//end of if(okToSendLine == true)
}//end of test()



void Detect() {
  static byte ndx = 0;
  char rc;
  while (Serial.available() > 0) {
    rc = Serial.read();
    if (rc != 'k') {
      DetectOK[ndx] = rc;
      ndx++;
      if (ndx >= 32) {
        ndx = 31;
      }
    }
    else { //it is a K!
      DetectOK[ndx] = rc; //we ADD THE K TO THE ARRAY
      DetectOK[ndx + 1] = '\0'; // terminate the string
      ndx = 0;
      okToSendLine = true;
      DetectOK[0] = '\0'; //clean array.
    }
  }
  /*if (strstr(DetectOK, "ok")) { //in this case we keep array for further operations, out of while loop
    okToSendLine = true;
    DetectOK[0] = '\0'; //clean array.
    }*/
}

void beep() {
  IrReceiver.stop();
  tone(TONE_PIN, 4000, 200);
  delay(200);
  tone(TONE_PIN, 600, 200);
  delay(200);
  IrReceiver.start(400000); // to compensate for 400 ms stop of receiver. This enables a correct gap measurement.
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.