2
votes

I would like to set up a serial communication between a Python daemon and an Arduino. At first, the Python daemon sets up a serial connection that will last for the whole lifetime of the daemon. Through this connection, I would like to send data to the Arduino and receive back data in the acks variable every time the Python daemon receives commands.

The problem is that while the first time the communication goes well, nothing is sent through serial afterwards. If I make the a new connection for every request it works, but it makes the program very slow, which I'd like to avoid. edit: the real issue is when send a correct string to the arduio evrything goes well but when i send a wrong one the serial port block and it will never reconize corrct strings again( the problem is in the arduino code)

Python code:

import serial
import time
import sys
from socket import *
import threading
import thread

def handler(clientsock,addr):
    while 1:
        #arduino.flush()
        data = clientsock.recv(BUFSIZ)
        if not data:
            break
        print data
        print data
        #time.sleep(3)
        arduino.write(data)
        #time.sleep(3)
        ack = arduino.readline(1)
        arduino.flush()
        clientsock.send(ack+"\n")
    clientsock.close()

if __name__=='__main__':
    HOST = '0.0.0.0'
    PORT = 21567
    BUFSIZ = 1024
    ADDR = (HOST, PORT)
    arduino = serial.Serial('/dev/ttyACM0',9600,timeout=6)
    serversock = socket(AF_INET, SOCK_STREAM)
    serversock.bind(ADDR)
    serversock.listen(2)

    while 1:
        print 'waiting for connection...'
        clientsock, addr = serversock.accept()
        print '...connected from:', addr
        thread.start_new_thread(handler, (clientsock, addr))

Arduino code:

      int relayPinCH1 = 7; // pin de commande du relais 1
  char inData[20]; // Allocate some space for the string
  char inChar=-1; // Where to store the character read
  byte index = 0; // Index into array; where to store the character

void setup()
{
  pinMode(relayPinCH1, OUTPUT);
  Serial.begin(9600);
}



char Comp(char* This) {
    while (Serial.available() > 0) // Don't read unless
                                   // there you know there is data
    {
        if(index < 19) // One less than the size of the array
        {
            inChar = Serial.read(); // Read a character
            inData[index] = inChar; // Store it
            index++; // Increment where to write next
            inData[index] = '\0'; // Null terminate the string
        }
    }
    Serial.flush();
    if (strcmp(inData,This)  == 0) {
        for (int i=0;i<19;i++) {
            inData[i]=0;
        }
        index=0;
        return(0);
    }
    else {
        return(1);
    }
}



void loop()
{
  //Serial.println("Hello Pi");
    if (Comp("l11\n")==0)
    {
      Serial.flush();
      digitalWrite(relayPinCH1, HIGH);
      Serial.println("y");
    }

    if (Comp("l10\n")==0)
    {
      Serial.flush();
      digitalWrite(relayPinCH1, LOW);
      Serial.println("n");
    } 
  delay(1000);
}
2

2 Answers

2
votes

In your Arduino code, your logic is kind of funky - so, I'm not sure, but are you clearing index to 0 before you start the loop again? It looks like once index == 19, it may or may not get reset to 0 depending upon later logic. If you enter Comp() a second time and index >= 19 then you'll never read the serial port again.

0
votes

I think @Zeus is entirely right (and hence I upvoted that answer), but there are also other problems. To reiterate what @Zeus is saying:

  • index is only reset to 0 if the comparison succeeds. So your buffer is full, the string you are looking for isn't there, and index never goes back to 0 again.
  • Once index reaches 19, no more reading is done. As a result, whatever is in inData stays in inData and all the future comparisons will fail, meaning index will never get reset to 0.

There are a number of other problems in the code, but the main issue is that the design is very fragile, and prone to exactly the sort of error you are experiencing. For instance if the newlinews your Python script is sending are CR+LF for newlines, but you are expecting CR only, you'll have the same sort of failure you have now: first time communications work, but never again.

I would suggest reorganizing your code like this:

  • Your function for reading serial port reads a line from a serial port and returns that to the caller (without the newlines), regardless of the content of the communications.
  • The caller compares the line received from the serial port with the list of known commands and executes them accordingly.

This might look rougly as follows

char strCommand[0xFF];
int idxCommandChar;

// Read a command from serial, returning the command size
// This function BLOCKS, i.e., doesn't return until a command is available
int readSerialCommand() {
  // We reset the index to zero on every read: the command is overwritten every time
  idxCommandChar = 0;

  // Read serial characters and store them in strCommand
  // until we get a newline
  int in = Serial.read();
  while (in!='\n')  {
    strCommand[idxCommandChar++] = in;
    in = Serial.read();
  }
  // Add the string terminator
  strCommand[idxCommandChar++] = '\0';
  // Return command size
  return idxCommandChar;  
}

// Get command from serial, and process it.
void processCommand() {
  readSerialCommand();
  if (strcmp(strCommand, "CMD1")==0) {
    // do something
  } else if (strcmp(strCommand, "CMD2")==0) {
    // do something else
  } else {
    // Unknown command
    Serial.println("Unknown command");
  }

}

void loop() {
  processCommand();
  delay(1000);
}

This code blocks on serial, i.e. doesn't return until a newline is detected. You could easily modify the code to be non-blocking, perhaps like this:

/* Read serial characters, if available and store them in strCommand
   until we get a newline
   Returns 0 if no command is available */
int readSerialCommand() {
  idxCommandChar = 0;
  while (Serial.available()) {
    int in = Serial.read();
    while (in!='\n')  {
      strCommand[idxCommandChar++] = in;
      in = Serial.read();
    }
    strCommand[idxCommandChar++] = '\0';
    return idxCommandChar;  
  }
  return 0;
}

// Get command from serial (if available), and process it.
void processCommand() {
  if (readSerialCommand()) {
     ....

In either case you might loose serial characters while you are waiting, so you may want to rethink that strategy.