My (Python) publisher:
import zmq
import time
context = zmq.Context()
socket = context.socket(zmq.PUB)
connectStr = "tcp://*:%d" % 5563
socket.bind(connectStr)
messageNum = 0
while True:
++messageNum
message = "Testing %d"%messageNum
print("Sending.. '%s'"%message)
socket.send_string(message)
time.sleep(1)
messageNum += 1
My (C++) subscriber (running in GTest):
TEST(ZeroMqPubSubTest, SubscribeGetsData)
{
// Set up the subscriber we'll use to receive the message.
zmq::context_t context;
zmq::socket_t subscriber(context, ZMQ_SUB);
// Connect to the publisher
subscriber.connect("tcp://127.0.0.1:5563");
subscriber.setsockopt(ZMQ_SUBSCRIBE, ""); // Set the filter blank so we receive everything
zmq::message_t response(0);
EXPECT_TRUE(subscriber.recv(&response));
}
I start up the publisher then start up the subscriber. The latter never returns though.
If I run a Python subscriber doing (I thought) exactly the same thing..
import zmq
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect ("tcp://127.0.0.1:5563")
socket.setsockopt_string(zmq.SUBSCRIBE, "")
print ("Waiting for data...")
while True:
message = socket.recv()
print ("Got some data:",message)
..it works fine:
Waiting for data...
Got some data: b'Testing 8'
Got some data: b'Testing 9'
There are two overloads of setsockopt defined in zmq.hpp:
template<typename T> void setsockopt(int option_, T const& optval)
{
setsockopt(option_, &optval, sizeof(T) );
}
inline void setsockopt (int option_, const void *optval_, size_t optvallen_)
{
int rc = zmq_setsockopt (ptr, option_, optval_, optvallen_);
if (rc != 0)
throw error_t ();
}
By providing only two arguments you implicity used the first overload, which assumes a value length of sizeof(T). This resolves to one, because "" is a zero-terminated character array. To pass in an empty string you need to use the second overload and specify a length of 0:
subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0);
Alternatively, use a zero size data type:
char none[0];
subscriber.setsockopt(ZMQ_SUBSCRIBE, none);
Related
I am creating a C server and Python client for UNIX domain datagram sockets (UDS) IPC on Ubuntu 18.04. My scenario is: Python runs as a child process created with fork-execv where C is the parent process. The Python client blocks on socket.recvfrom until data is sent by the C server. When the C server sends data to the Python client then C will block on recvfrom until Python sends data to C with sendto.
I have used UDS for a C client and a C server with no problems, but the C-Python setup is causing some problems. For this Python version I worked from an example at https://lloydrochester.com/post/c/unix-domain-socket-datagram.
I create a server socket in C and bind to it; it returns file descriptor 5:
int64_t * create_socket_server(struct sockaddr_un svaddr, int64_t retvals[])
{
int sfd, j;
ssize_t numBytes;
socklen_t len;
char buf[BUF_SIZE];
retvals[0] = 0;
retvals[1] = 0;
sfd = socket(AF_UNIX, SOCK_DGRAM, 0); /* Create server socket
if (sfd == -1)
return retvals;
if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
return retvals;
memset(&svaddr, 0, sizeof(struct sockaddr_un));
svaddr.sun_family = AF_UNIX;
strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);
if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) == -1)
return retvals;
retvals[0] = sfd;
retvals[1] = (int64_t)&svaddr;
return retvals;
}
I do not create or explicitly connect to the client socket on the C side.
On the Python side I bind to the client socket. Here is my Python code, following the example cited, but altered somewhat to fit my use case:
#!/usr/bin/python3
import socket
import os, os.path
csock_file = "/tmp/py_sock"
ssock_file = "/tmp/ud_ucase"
if os.path.exists(csock_file):
os.remove(csock_file)
csock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
csock.bind(csock_file) # Bind to the server socket
return_msg = "Got it"
while True:
(bytes, address) = csock.recvfrom(720)
msg = bytes.decode('utf-8')
print("Python received")
if msg != "Code_99":
print('address:',address,'received:',msg)
csock.sendto(str.encode(return_msg), ssock_file)
if msg == "Code_99":
print("closing")
#Close the socket
I want recvfrom to be blocking in both Python and C because Python should block until C sends, but when I leave it at blocking (the default) then Python blocks both processes when it calls (bytes, address) = csock.recvfrom(720), and C cannot continue.
If I set it to nonblocking with csock.setblocking(False) I get this error message:
(bytes, address) = csock.recvfrom(720)
BlockingIOError: [Errno 11] Resource temporarily unavailable
So my question is why does Python block both processes, and why do I get that error message in nonblocking mode?
Thanks for any help.
Explanation
why does Python block both processes ?
When your client is waiting for your server's response with recvfrom, you server just did nothing, thus server blocks at its recvfrom as well.
why do I get that error message in nonblocking mode ?
Your server/client might not be as robust as the one you quoted (i.e. from lloydrochester.com). Serveral parts broke and result in breaking the whole thing. Some of them are just about C Lang, such as Variable Declarations, Function Returning, etc. Others are about network programming, such as Buffer Sizing, Socket Internals, etc. It's not realistic to list them all and analyse one by one. Better read through K&R and BSD socket to fix them thoroughly.
However, here is an relatively simple implementation for you case, based on your codes, shown below. In addition, you might want to change the reply message to Code_99 in the 48th line of server_alice.c.
Environment
Ubuntu 18.04
gcc 7.5.0
Python 3.6.9
server_alice.c
#include <sys/un.h>
#include <sys/socket.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define BUF_SIZE 720
#define SV_SOCK_PATH "ssock"
int create_socket_server(int *sfd_ptr, struct sockaddr_un *svaddr_ptr);
int main(int argc, char *argv[]) {
struct sockaddr_un svaddr, claddr;
int sfd, j;
ssize_t numBytes;
socklen_t len;
char buf[BUF_SIZE];
int64_t retvals[2];
if (create_socket_server(&sfd, &svaddr) == 0)
printf("create_socket_server...DONE\n");
else exit(0);
for (;;) {
len = sizeof(struct sockaddr);
printf("waiting clients...\n");
numBytes = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);
if (numBytes == -1) {
fprintf(stderr, "error recvfrom");
return 4;
}
claddr.sun_path[len - sizeof(sa_family_t) - 1] = 0;
buf[numBytes] = '\0';
fprintf(stdout, "server received %ld bytes from %s, they are: \x1b[32m%s\x1b[0m\n", (long) numBytes,
claddr.sun_path, buf);
for (j = 0; j < numBytes; j++) {
buf[j] = toupper((unsigned char) buf[j]);
}
// char *reply_msg="Code_99"; # different reply message
char *reply_msg = "Hello Bob~ This is a message: blablablabla";
j = sendto(sfd, reply_msg, strlen(reply_msg), 0, (struct sockaddr *) &claddr, len);
if (j != strlen(reply_msg)) {
fprintf(stderr, "error sendto %s", strerror(errno));
}
}
exit(EXIT_SUCCESS);
}
/* Your create_socket_server, with a few changes */
int create_socket_server(int *sfd_ptr, struct sockaddr_un *svaddr_ptr) {
struct sockaddr_un svaddr;
int sfd = socket(AF_UNIX, SOCK_DGRAM, 0); // Create server socket
if (sfd == -1)
return -1;
if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
return -1;
memset(&svaddr, 0, sizeof(struct sockaddr_un));
svaddr.sun_family = AF_UNIX;
strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);
if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) == -1)
return -1;
memcpy(sfd_ptr, &sfd, sizeof(int));
memcpy(svaddr_ptr, &svaddr, sizeof(struct sockaddr_un));
return 0;
}
client_bob.py
#!/usr/bin/python3
import socket
import os, os.path
csock_file = "./csock"
ssock_file = "./ssock"
if os.path.exists(csock_file):
os.remove(csock_file)
csock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
csock.bind(csock_file) # Bind to the server socket
return_msg = "Got it"
csock.sendto(str.encode("Hello Alice! I'm coming!"), ssock_file)
# while True: # ! CATION ! If 'while(true)', there will be infinite message sending back and forth!
(bytes, address) = csock.recvfrom(720)
msg = bytes.decode('utf-8')
if msg != "Code_99":
print('address: ', address, 'received: ', msg)
csock.sendto(str.encode(return_msg), ssock_file)
if msg == "Code_99":
print("closing")
csock.close()
Server Output:
$ gcc server_alice.c && ./a.out
create_socket_server...DONE
waiting clients...
server received 24 bytes from ./csock, they are: Hello Alice! I'm coming!
waiting clients...
server received 6 bytes from ./csock, they are: Got it
waiting clients...
Client Output:
$ python3 client_bob.py
address: ssock received: Hello Bob~ This is a message: blablablabla
I am sending a double value from C through TCP using lwIP. To accomplish that, I am using an union between a double and one string of 8 bytes (because the lwIP's function to send data is of type 'char *'). Here te code:
void process_echo_request(void *p) {
int sd = (int)p;
int RECV_BUF_SIZE = 2048;
char recv_buf[RECV_BUF_SIZE];
int n, nwrote;
union {
double dval;
char sval[sizeof(double)];
} data_to_send;
data_to_send.dval = 1e+23;
while (1) {
/* read a max of RECV_BUF_SIZE bytes from socket */
if ((n = read(sd, recv_buf, RECV_BUF_SIZE)) < 0) {
xil_printf("%s: error reading from socket %d, closing socket\r\n", __FUNCTION__, sd);
break;
}
/* break if the recved message = "quit" */
if (!strncmp(recv_buf, "quit", 4))
break;
/* break if client closed connection */
if (n <= 0)
break;
data_to_send.dval += 1e+23;
/* handle request */
if ((nwrote = write(sd, data_to_send.sval, 8)) < 0) {
xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n",
__FUNCTION__, n, nwrote);
xil_printf("Closing socket %d\r\n", sd);
break;
}
}
/* close connection */
close(sd);
vTaskDelete(NULL);
I am trying to read from a Python script, but I fail on get the double value. The code:
#!/usr/bin/env python
import socket
from ctypes import *
TCP_IP = '192.168.0.10'
TCP_PORT = 1000
BUFFER_SIZE = 8
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(" ")
data = s.recv(BUFFER_SIZE)
s.close()
print float(data)
It fails with the message ValueError: could not convert string to float: �J��-�D
I know, is because the string is not a literal representation of a real value. Any idea? I need the real (double) value.
For more information, the lwIP echo server is running in a Zynq 7000 FPGA+SoC. I am running the Python code from a Debian 8. The double value is a 64 bit temperature for a sensor.
-- EDIT --
The final working code is:
#!/usr/bin/env python
import socket
from struct import *
TCP_IP = '192.168.0.10'
TCP_PORT = 1000
BUFFER_SIZE = 8
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(" ")
data = s.recv(BUFFER_SIZE)
s.close()
print unpack("d",data)[0]
you're recieving the binary representation of the double, not the string one.
You have to unpack it using struct module and the double specifier which is d. struct.unpack returns a tuple, so take first & only value to get your float
my_value = struct.unpack("d",data)[0]
note that you can even fix the endianness with < or >, not necessary if you're on the same machine or CPU type for both programs.
I'm trying to read lines from an Arduino board with a very simple code (for the sake of showcasing the problem) on Linux.
Python code:
# arduino.py
import serial
arduino = serial.Serial('/dev/ttyACM0')
with arduino:
while True:
print(arduino.readline())
Arduino code:
// simpleWrite.ino
long ii = 0;
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600);
}
void loop() {
Serial.println(ii);
ii++;
}
As the board auto-resets when the serial connection is opened, the first bytes are likely garbage. After a second or two everything works fine.
This is a typical output:
$ python arduino.py
b'09\r\n'
b'540\r\n'
b'541\r\n'
b'542\r\n'
b'543\r\n'
b'544\r\n'
b'545\r\n'
b'546\r\n'
b'547\r\n'
b'548\r\n'
b'549\r\n'
b'550\r\n'
b'551\r\n'
b'552\r\n'
b'553\r\n'
b'554\r\n'
b'555\r\n'
b'556\r\n'
b'557\r\n'
b'55\xfe0\r\n' # <---- Here the board restarted
b'1\r\n'
b'2\r\n'
b'3\r\n'
b'4\r\n'
b'5\r\n'
b'6\r\n'
b'7\r\n'
b'8\r\n'
b'9\r\n'
b'10\r\n'
However, I see the Arduino IDE Serial Monitor doesn't have this problem, and properly shows a delay (while restarting) and then prints all the lines starting from the first one.
Is there a way to emulate this behaviour in Python using pySerial? That is, discarding all the output before restarting and nothing more? Perhaps through some low-level functions?
I tried looking at the relevant Arduino source code, but I don't know Java and it didn't help.
Note: Of course I could sleep for, say, three seconds, discard everything and start from there, but I would probably discard some of the first lines too.
Edit: Apparently, this problem doesn't exist on Windows and the accepted solution was not necessary.
The Arduino IDE's monitor toggle's the assigned DTR pin of the port when connected. Where this toggling causes a reset on the Arduino. Noting that the DTR is toggled after the Monitor has opened the Serial port and is ready to receive data. In your case the below example should do the same.
Import serial
arduino = serial.Serial('/dev/ttyS0',
baudrate=9600,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1,
xonxoff=0,
rtscts=0
)
# Toggle DTR to reset Arduino
arduino.setDTR(False)
sleep(1)
# toss any data already received, see
# http://pyserial.sourceforge.net/pyserial_api.html#serial.Serial.flushInput
arduino.flushInput()
arduino.setDTR(True)
with arduino:
while True:
print(arduino.readline())
I would also add the compliment to the DTR for the Arduino's with AVR's using built-in USB, such as the Leonoardo, Esplora and alike. The setup() should have the following while, to wait for the USB to be opened by the Host.
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
}
It will have no effect for FTDI's based UNO's and such.
I realize this is an old question, but hopefully this can be useful to somebody else out there with the same problem.
I had an issue where if I used any baudrates other than 9600, the serial connection in python would just receive gibberish all the time, even if Serial.begin(...) is properly set on the arduino and matches the value used in the python code.
I read online that the bootloader or watchdog may take a second to load (when the board is power-cycled) and it may send stuff over serial at some specific baudrate (for chip programming possibly). I'm guessing that this is what messes up the serial communication in python.
Here's the piece of code that gives me reliable results:
import serial
from time import sleep
arduino = serial.Serial('/dev/ttyACM0') # dummy connection to receive all the watchdog gibberish (unplug + replug) and properly reset the arduino
with arduino: # the reset part is actually optional but the sleep is nice to have either way.
arduino.setDTR(False)
sleep(1)
arduino.flushInput()
arduino.setDTR(True)
# reopen the serial, but this time with proper baudrate. This is the correct and working connection.
arduino = serial.Serial('/dev/ttyACM0',baudrate=57600)
with arduino:
while True:
print(arduino.readline())
The code used on the arduino side for testing is as simple as this:
void setup() {
Serial.begin(57600);
Serial.println("setup");
}
void loop() {
Serial.println("hello");
delay(200);
}
Please follow this link for a reliable PC-Arduino USB serial communication using python.
Python code simply sends a short message to the Arduino and prints the reply it receives.
// This is very similar to Example 3 - Receive with start- and end-markers
// in Serial Input Basics http://forum.arduino.cc/index.php?topic=396450.0
const byte numChars = 64;
char receivedChars[numChars];
boolean newData = false;
byte ledPin = 13; // the onboard LED
//===============
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
delay(200);
digitalWrite(ledPin, LOW);
delay(200);
digitalWrite(ledPin, HIGH);
Serial.println("<Arduino is ready>");
}
//===============
void loop() {
recvWithStartEndMarkers();
replyToPython();
}
//===============
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
//===============
void replyToPython() {
if (newData == true) {
Serial.print("<This just in ... ");
Serial.print(receivedChars);
Serial.print(" ");
Serial.print(millis());
Serial.print('>');
// change the state of the LED everytime a reply is sent
digitalWrite(ledPin, ! digitalRead(ledPin));
newData = false;
}
}
//===============
Python Code
import serial
import time
startMarker = '<'
endMarker = '>'
dataStarted = False
dataBuf = ""
messageComplete = False
#========================
#========================
# the functions
def setupSerial(baudRate, serialPortName):
global serialPort
serialPort = serial.Serial(port= serialPortName, baudrate = baudRate, timeout=0, rtscts=True)
print("Serial port " + serialPortName + " opened Baudrate " + str(baudRate))
waitForArduino()
#========================
def sendToArduino(stringToSend):
# this adds the start- and end-markers before sending
global startMarker, endMarker, serialPort
stringWithMarkers = (startMarker)
stringWithMarkers += stringToSend
stringWithMarkers += (endMarker)
serialPort.write(stringWithMarkers.encode('utf-8')) # encode needed for Python3
#==================
def recvLikeArduino():
global startMarker, endMarker, serialPort, dataStarted, dataBuf, messageComplete
if serialPort.inWaiting() > 0 and messageComplete == False:
x = serialPort.read().decode("utf-8") # decode needed for Python3
if dataStarted == True:
if x != endMarker:
dataBuf = dataBuf + x
else:
dataStarted = False
messageComplete = True
elif x == startMarker:
dataBuf = ''
dataStarted = True
if (messageComplete == True):
messageComplete = False
return dataBuf
else:
return "XXX"
#==================
def waitForArduino():
# wait until the Arduino sends 'Arduino is ready' - allows time for Arduino reset
# it also ensures that any bytes left over from a previous message are discarded
print("Waiting for Arduino to reset")
msg = ""
while msg.find("Arduino is ready") == -1:
msg = recvLikeArduino()
if not (msg == 'XXX'):
print(msg)
#====================
#====================
# the program
setupSerial(115200, "/dev/ttyACM0")
count = 0
prevTime = time.time()
while True:
# check for a reply
arduinoReply = recvLikeArduino()
if not (arduinoReply == 'XXX'):
print ("Time %s Reply %s" %(time.time(), arduinoReply))
# send a message at intervals
if time.time() - prevTime > 1.0:
sendToArduino("this is a test " + str(count))
prevTime = time.time()
count += 1
you need to set your var , try:
unsigned long ii = 0;
but pay attention that this is a 32 bit var and when it is full ,cause overflow and reboot.
for me work.
As suggested by #Kobi K add a minimal delay time, for load real data at 9600 boud each char has a duration of 2 ms,
void loop() {
Serial.println(ii);
delay(20);
ii++;
}
And in python you need to declare a Pyserial like this:
arduino=serial.Serial('/dev/ttyACM0',9600,timeout=0.0001)
hope this help you
I am beginner in python written first program two days ago. I am having connection problem in python client and C server for AF_UNIX. I have C socket Server with AF_LOCAL.
#define NAME "#/tmp/kvsd"
int
main()
{
int sock, msgsock, rval;
struct sockaddr_un server;
char buf[1024];
unlink(NAME);
printf("before socket \n");
sock = socket(AF_LOCAL, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
memset(&server, 0, (sizeof (server)));
server.sun_family = AF_LOCAL;
memcpy(server.sun_path, NAME, strlen(NAME));
server.sun_path[0] = 0;
printf("before bind \n");
int len = strlen(server.sun_path) + sizeof(server.sun_family);
if (bind(sock, (struct sockaddr *) &server, len)) {
perror("binding stream socket");
exit(1);
}
printf("before listen \n");
if (listen(sock, 5) == -1) {
perror("listen");
exit(1);
}
printf("before accept \n");
msgsock = accept(sock, 0, 0);
printf("accepted \n");
if (msgsock == -1)
perror("accept");
else do {
bzero(buf, sizeof(buf));
printf("before read \n");
if ((rval = read(msgsock, buf, 1024)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", buf);
} while (rval > 0);
close(msgsock);
close(sock);
unlink(NAME);
}
And Python AF_UNIX client.py:-
####### CLIENT CODE #######
from socket import *
# Create an unbond and not-connected socket.
sock = socket(AF_UNIX, SOCK_STREAM)
# Connect to the peer registered as "MyBindName" in the abstract namespace. Note the '\0'.
str = "\0/tmp/kvsd\0"
print "len ", len (str)
sock.connect("\0/tmp/kvsd")
# Wait for message
msg = sock.recv(100)
print msg
# Send reply
sock.send("Hi there!\n")
# Block until new message arrives
msg = sock.recv(100)
# When the socket is closed cleanly, recv unblocks and returns ""
if not msg:
print "It seems the other side has closed its connection"
# Close it
sock.close()
But When I run the client I'm getting following error:
[root#mat afunix]# python ./client.py len 11 Traceback (most recent call last): File "./client.py", line 13, in sock.connect("\0/tmp/kvsd") File "", line 1, in connect socket.error: [Errno 111] Connection refused [root#mat afunix]#
I am trying to use the abstract namespaces for UNIX socket but my python client is not able to connect to c server.
I tried without abstract namespaces it works. (changed NAME macro in server.c to "/tmp/kvsd" and argument to sock.connect to "/tmp/kvsd").
Can someone help me to figure out what may be the exact issue ?
Thanks in advance.
Following line has a problem.
int len = strlen(server.sun_path) + sizeof(server.sun_family);
server.sun_path has now leading null character. So strlen(server.sun_path) is 0. You need change above line as follow:
#include <stddef.h>
....
int len = offsetof(struct sockaddr_un, sun_path) + strlen(NAME);
Then, it will work.
EDIT: updated the code to use offsetof to avoid padding issue. (Thank you, alk)
PS: I assume that both server, client use name without trailing null byte. If you use name with trailing null byte, add 1 to len.
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);
}
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.
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.