Port busy when sending packets from python?

I’m trying to send packets of json data using python code to the arduino over ethernet udp protocol. It worked fine before, but then we had to switch devices–first from an older Mega that had bootloader problems to an uno, then to a newer Mega 2560 for more memory–and now the terminal is telling me the proper arduino port is busy when I run the listening program on it.

First I run this arduino code:

#include <ArduinoJson.h>


#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008


// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {  
  0x90, 0xAD, 0xDA, 0x00, 0x42, 0x5B };
IPAddress ip(172,16,174,146);

unsigned int localPort = 8080;      // local port to listen on

// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

char packetBuffer[UDP_TX_PACKET_MAX_SIZE];

void setup() {
  // start the Ethernet and UDP:
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);

  Serial.begin(9600);
}

void loop() {
  int packetSize = Udp.parsePacket();
  if(packetSize)
  {
    Serial.print("Received packet of size ");
    Serial.println(packetSize);
    Serial.print("From ");
    IPAddress remote = Udp.remoteIP();
    for (int i =0; i < 4; i++)
    {
      Serial.print(remote[i], DEC);
      if (i < 3)
      {
        Serial.print(".");
      }
    }
    Serial.print(", port ");
    Serial.println(Udp.remotePort());

    // read the packet into packetBufffer
    Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);
    Serial.println("Contents:");
    Serial.println(packetBuffer);

   
  }
}

Then this python sending code:

#!/usr/bin/env python
from __future__ import print_function
##################################################################################
# command line:
# python read_event_to_arduino.py  --in_files /data/i3home/elims/display3D/event_files/data_dict.p
#                                  --port /dev/ttyACM0
#                                  --baud_rate 115200
#                                  --sleep_time 5
##################################################################################

###################### PRINTOUTS ########################
import os, sys, optparse
import socket
print ('########################################################################################')
print ('#### This job is running on {0} ... '.format(socket.gethostname()) )
print ('########################################################################################')
print (' ')
#########################################################

from optparse import OptionParser
import serial, time, pickle
import numpy as np

#########################################################
#### defining pulse list...
#########################################################
class pulseList():

    def __init__(self, dom, string, time, charge, width):
        self.dom = dom
        self.string = string
        self.time = time
        self.charge = charge
        self.width = width

    def __repr__(self):
        return repr( (self.dom, self.string, self.time, self.charge, self.width) )

#########################################################
#### parsing options
#########################################################
usage = "%prog [options] <inputfiles>"
parser = OptionParser(usage=usage)

parser.add_option("-i", "--in_files", type = "string", default = [],
                  help = "Path/Name of the input pickle file")
parser.add_option("-n", "--num_events", type = "int", default = 0,
                   help = "Number of events to be displayed (default: all)")
parser.add_option("-d", "--port", type = "string", default = "/dev/ttyACM0",
                  help = "arduino device port")
parser.add_option("-b", "--baud_rate", type="int", default = 115200,
                  help = "baud rate of arduino board")
parser.add_option("-s", "--sleep_time", type="int", default = 50,
                  help = "break time between sending each events")
(options, args) = parser.parse_args()

#### note1: if software not recognizing port:
####            sudo usermod -a -G dialout <user name>
####            sudo chmod a+rw <port location>

#########################################################
#### setting up input file and arduino...
#########################################################
in_files = options.in_files
port = options.port
baud_rate = options.baud_rate
sleep_time = options.sleep_time
in_file = '/data/i3home/elims/display3D/event_files/data_dict.p'

data = pickle.load(open(in_file, 'rb'))
arduino = serial.Serial(port, baud_rate, timeout=.1)
time.sleep(1)

print ('in_file: {0}'.format(in_file))
print ('arduino: {0} with a baud rate of {1} Hz'.format(port, baud_rate))
print (' ')

#########################################################
#### reading in data from pickle file...
#### NOTE1: write '-1' to denotes end of each pulse
#########################################################
#event_map = []
for index, event in enumerate(data):
    print ( '################## Event {0} with {1} hits ##################'.format(index, len(data[event])) )
    pulse_map = []
    for hit in data[event].items():
        #current_hit = pulseList(hit[1]['dom'], hit[1]['string'], hit[1]['time'], hit[1]['charge'], hit[1]['width'])
        current_hit = [hit[1]['time'], hit[1]['dom'], hit[1]['string'], hit[1]['charge'], hit[1]['width']]  ## python list
        pulse_map.append(current_hit)
    pulse_map.sort(key=lambda h:h[0])
    ##### double check pulse map is timely sorted
    for j, pulse in enumerate(pulse_map):
        print ('pulse {0}: {1:.4f}, {2}, {3}, {4:.4f}, {5}'.format(j, pulse[0], pulse[1], pulse[2], pulse[3], pulse[4]))
    print (' ')
    #########################################################
    #### sending number of hits in the current event to arduino...
    #########################################################
    info = [len(pulse_map), pulse_map]
    print ( '... sending {0} to arduino ... '.format(str(info[0]).encode()) )
    arduino.write(str(info[0]).encode())
    time.sleep(2)
    for j, pulse in enumerate(pulse_map):
        for information in pulse:            
            arduino.write(str(information).encode())
            time.sleep(2)
        arduino.write('-1')
        time.sleep(5)
    #arduino.write(str(pulse_map))
    time.sleep(sleep_time) #give the connection a second to settle

I use the following linux command in terminal: python read_event_to_arduino.py --baud_rate 9600 --port /dev/ttyACM2

and get this error:

File “read_event_to_arduino.py”, line 71, in
arduino = serial.Serial(port, baud_rate, timeout=.1)
File “/data/i3home/jbelenky/pySerial/pyserial-2.7/serial/serialutil.py”, line 282, in init
self.open()
File “/data/i3home/jbelenky/pySerial/pyserial-2.7/serial/serialposix.py”, line 289, in open
self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK)
OSError: [Errno 16] Device or resource busy: ‘/dev/ttyACM2’

Ultimately I’m going to need an ethernetudp code that parses json data and sends it to the arduino, but for now I just need to get the connection working again.

Have you something else connected to ttyACM2? Have you tried to see if you can connect to the Arduino using that port with a terminal program or the Arduino IDE Serial Monitor?

Sometimes ports don't close properly. Is there another instance of Python running - that needs to be killed?

...R

This is the first time I've really worked with ports...what else might be connected to ttyACM2? The Arduino IDE Serial Monitor looks like its connecting ok. We also got this exact same error when the Mega was on a different port.

What seems to be happening is that, the python program will send the first packet of data if the serial monitor isn't open. However while it's supposed to send the next packet of data right after, it stops right after the first one and gets thoughtful. It sends the next packet after maybe a minute. And I can't actually see the arduino receiving any of this because opening the serial monitor makes the terminal unhappy.

I can't quickly see the flow of the Python program. You may be seeing your problem because the Python program opens and closes the serial connection - and maybe does not close it properly.

Your Python program should open the serial port once and keep it open until it is completely finished with the Arduino. Also, the Python program should leave time for the Arduino to reset after it opens the serial port.

Have a look at this Python - Arduino demo

...R

I didn't write the Python program, so I'll look into it--but to my knowledge it does do those things, and successfully did do them until we switched devices. This makes me suspect it's not exactly a Python problem but mmm

What, exactly, was the original device - the one that you were using when the code was working?

Did you make ANY change (even the teeniest little one) to either the Arduino or the Python code compared with what it was when it was working?

...R

One thing I found a while back while playing with VB and ports was that when you "close" a port in a system, you are not actually closing the port - you are telling the OS you are done with the port and it can close it. Turns out that may be a significant delay (seconds or more) between you "closing" the port and when the OS actually "closes" the port (and until the OS does that, the port is "busy"). Happens in Windows and Linux from what I found. That minor detail can really confuse you if you are not aware of it.

Robin, we were using an older Arduino Mega and then an Arduino Uno, until bootloader issues caused us to switch from the Uno to the new Mega 2560. I definitely don't recall making any changes whatsoever to either code.

Mikey, but that shouldn't affect it when absolutely nothing is running, right? Like, what's happening is that I run the Arduino listening code, and it works fine. And then I run the Python sending code, and it runs fine. But if I open the serial monitor, nothing appears, and if the serial monitor is open when I run the Python code, it tells me the port is busy. The Python code also runs (albeit veerrryyy slowly) if the wrong port or wrong baud rate is specified.

You are probably correct - it just sort of had that "flavor" to it when I first read the description (and it took me many hours Googling around to finally find how that worked!)

sneergh:
and if the serial monitor is open when I run the Python code, it tells me the port is busy.

What else do you expect?

Only one PC program can use the port at any one time.

How have we got as far as Reply #8 before you told us this important piece of information? I asked about this in Reply #1

…R

Well, I would expect to open the serial monitor, run the program, and then see the information in the monitor. Because this is exactly what happened before.

sneergh: The Arduino IDE Serial Monitor looks like its connecting ok.

Yea, this is what I meant when I said this. I didn't think it would be such an issue because like I said--it's exactly how it worked before, and it did work. I'm fairly sure it's an issue in the Python program, but I didn't write it and it hasn't been changed At All since the last success.

sneergh: Well, I would expect to open the serial monitor, run the program, and then see the information in the monitor. Because this is exactly what happened before.

That statement does not say anything about your earlier comment that I quoted in Reply #9

We seem to be going round in circles.

If Python is using the Serial Port the Arduino IDE cannot use it. If the Arduino IDE is using the Serial Port then Python cannot use it.

In my experience the Arduino IDE properly tidies up after itself when you close the Serial Monitor. Some of my Python programs did not - until I spoke crossly to them.

...R