Pages: [1]   Go Down
Author Topic: A fatal error with Arduino and Python  (Read 1003 times)
0 Members and 1 Guest are viewing this topic.
Reading, Berkshire
Offline Offline
Full Member
***
Karma: 2
Posts: 132
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello.
There is a possibility that this error is not caused by an Arduino issue, therefore this post will serve as a learning curve for myself and others.

 I am currently making pin12 go HIGH and LOW after receiving a new tweet on Python.
The program will run for a couple of days and then Python will crash. I have yet to see the Error code as the unit is very far away from myself. Until I am sent the error code, I'm trying to understand possible problems.

I have two queries.
  • How can I ensure the Python script will not crash if Serial connection is temporarily lost?
At the moment, using a Try / Except doesn't seem to cut it as once unplugged, the computer doesn't recognise the COM port again.


  • Does disconnecting and reconnecting to the Arduino from a PC use more memory each time or does it reset the Arduino?

Here is the python code (with the COM port Try/Except removed):
Code:
import json
import urllib
from pprint import pprint
import time
import serial

#to collect the first tweet without vending
countTweet = 0
tweet= 0
noTweet= 0

#the infinate loop
while True:
    #the connection to the arduino
    ser = serial.Serial('COM3',9600)
    #not connected to arduino before connection is made
    connected = False

    #loop until the arduino is connected
    while not connected:
        serin = ser.read()
        connected = True
    #debug arduino connection
    if connected == True:
        pprint('connected to arduino')
   
    try:
        response = urllib.urlopen('http://search.twitter.com/search.json?q=%23happy&result_type=recent&rpp=1&filter:retweets')
    except IOError:
        pprint ('internet or twitter is down. This program will continue working when the connection opens again.')
        time.sleep(15)
        continue
    j =json.loads(response.read())
    #Check that j contains the correct json, else twitter has sent another broken one.
    if j['results']:
        text = j['results'][0]['text']
        id = j['results'][0]['id']
        pprint(text)
        pprint(id)   
        tweet+= 1
    else:
        #How many times the Json is false
        noTweet += 1

    #to isolate the first loop, if the first ID has been stored already (countTweet == 1)
    if countTweet != 0:
        pprint ("new tweet")
        #if lastID is not equal to ID
        if lastID != id:
        #Tell Arduino to Vend
            ser.write('1')
            ser.write('0')
            #loop until the arduino tells us it is done vending by sending us a '0'
            while ser.read() == '1':
                ser.read()
               
            #Make lastID equal to ID
            lastID = id
            pprint ('lastID updated')
        #if no new tweets, print     
        else:
            pprint ('no new tweets')
    #If it's the first loop, confirm by printing to the screen
    else:
        pprint("First loop complete")
        lastID = id
        pprint(lastID)
        countTweet += 1
   

    #make count not equal to 0 after first loop
 

    pprint ('closing arduino connection')
    ser.close()

    #wait
    pprint('waiting 15 seconds')
    pprint ('Number of Tweets')
    pprint (countTweet)
    pprint('Working JSON')
    pprint(tweet)
    pprint('Broken JSON')
    pprint(noTweet)
    time.sleep(15)
   
       


And here is the simple Arduino sketch:
Code:
void setup(){
  Serial.begin(9600);
  pinMode(12, OUTPUT);
  //Tell python we are done with setup
  Serial.write('1');
}

void loop(){

  if(Serial.available() >0){
    //Vend pin high
    digitalWrite(12,HIGH);
    delay(1000);
    digitalWrite(12,LOW);
    //Tell Python we have flashed
    Serial.write('0');
  }
}
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 551
Posts: 46240
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I don't know python, but this looks wrong:
Code:
            while ser.read() == '1':
                ser.read()
How does the while loop ever end? You read a value, and test it. If it isn't a '1', you read and discard a value (that might have been the only '0' that the Arduino sends). Then, you read again, to see if the '0' has arrived yet.

Or, am I missing something?
Logged

0
Offline Offline
God Member
*****
Karma: 39
Posts: 986
Get Bitlash: http://bitlash.net
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Concur with Paul, and found another similar issue:

Code:
    #loop until the arduino is connected
    while not connected:
        serin = ser.read()
        connected = True
 

The "connected = True" inside the loop guarantees it will run only once.  But anyway, how does this code in any way wait until the arduino is connected?

When you open the serial port, the Arduino resets and hangs out in the bootloader for a couple seconds to see if you want to upload new code.  During that time, your program is not running.  Try adding time.sleep(2) after opening the serial port to allow the bootloader to time out and start your arduino code before proceeding with your python stuff.

-br
Logged

Reading, Berkshire
Offline Offline
Full Member
***
Karma: 2
Posts: 132
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
It reads ser
if ser is a 1 it reads ser again
if ser is a 0 it exits the while loop
I did consider there might be a chance that it would miss the 0 coming from the Arduino. However, I set it running for a two day test and it worked. If the Serials are both set to 9600bps they must catch every '1' or '0' that comes their way.

it would be the same as writing

Code:
while ser.read() != '0':
ser.read()


Basically saying keep checking ser until you find a '0', do not progress with the script until Arduino has finished it's function.


The 'While True' infinite loop contains a 'connected = False' statement before the 'while not connected' but after 'ser.close()'

I'll add that time.sleep(2) but I must state that it works for an extended period as is. I imagine if it wasn't going to connect this way, it wouldn't work 99% of the time?


Slightly unrelated additional information:

The Arduino is connected to a MOSFET on pin 12 which controls a Solenoid. I built this circuit for it.


I don't know if there could be an issue here but as above, it works 99% of the time.


The Json feed has a 0.1% error rate when calling it every 15 seconds over two days. This has been handled in the code. Sometimes it would lack the 'results:' . So I loop around again, leaving a total of 30 second gap as to not call on twitter too often.




Logged

0
Offline Offline
God Member
*****
Karma: 39
Posts: 986
Get Bitlash: http://bitlash.net
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Basically saying keep checking ser until you find a '0', do not progress with the script until Arduino has finished it's function.
Quote
I understood your intent, and was pointing out that this code does not do quite that because it has a bug.  It only checks every other character, and throws away the other half without looking at them:
Code:
while ser.read() == '0':   // if the next serial input char is not '0'
    ser.read()                  // read and discard the following char
[code]


-br
[/code]
Logged

Reading, Berkshire
Offline Offline
Full Member
***
Karma: 2
Posts: 132
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Do you think

Code:
while ser.read() !='0'
    pprint('waiting for arduino')


or


Code:
while ser.read() == '0'
    break


would work?
Logged

Reading, Berkshire
Offline Offline
Full Member
***
Karma: 2
Posts: 132
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I have been forwarded the error message from the python script. It looks like you might be right.

Code:
Traceback (most recent call last):
  File "C:\Users\3d Exposure\Desktop\M001.py", line 19, in <module>
    ser = serial.Serial('COM3',9600)
  File "C:\Python27\lib\site-packages\serial\serialwin32.py", line 31, in __init__
    SerialBase.__init__(self, *args, **kwargs)
  File "C:\Python27\lib\site-packages\serial\serialutil.py", line 261, in __init__
    self.open()
  File "C:\Python27\lib\site-packages\serial\serialwin32.py", line 71, in open
    self._reconfigurePort()
  File "C:\Python27\lib\site-packages\serial\serialwin32.py", line 186, in _reconfigurePort
    raise ValueError("Cannot configure port, some setting was wrong. Original message: %s" % ctypes.WinError())
ValueError: Cannot configure port, some setting was wrong. Original message: [Error 31] A device attached to the system is not functioning.
Logged

0
Offline Offline
God Member
*****
Karma: 39
Posts: 986
Get Bitlash: http://bitlash.net
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If what you actually want to do is wait for the first '0' from the Arduino, why not:
Code:
    while ser.read() != '0':
        pass

-br
Logged

Reading, Berkshire
Offline Offline
Full Member
***
Karma: 2
Posts: 132
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I hope this will provide a better idea of what I need. I'm not even sure it is possible though.

http://stackoverflow.com/questions/15998425/python-serial-arduino-surviving-loss-of-connection

Quote
I'm running this script to test the Serial connection between an Arduino and Python before I try to send and receive information between the two.

If the Arduino is unplugged/re-plugged during while ser.read(), it will never restart from where it left off due to never receiving anything but a '1' from the Arduino>Serial.

What is a better way to write this that ensures the Serial connection can restart the script from the beginning after being unplugged?

Code:
from pprint import pprint
import time
import serial

while True:
    #the connection to the arduino
    try:
        ser = serial.Serial('COM13',9600)
    except IOError:
        connected = False
        pprint('Arduino not connected to COM13')
        time.sleep(3)
        continue
    serin = ser.read()
    connected = True
    pprint('connected to arduino')
    ser.write('1')
    while ser.read() == '1':
        pass
    pprint ('Arduino has finished flash')

    ser.close()
    pprint('connection closed')

    time.sleep(5)
Logged

0
Offline Offline
God Member
*****
Karma: 39
Posts: 986
Get Bitlash: http://bitlash.net
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Now I get it.  You want the Python program to reconnect to the Arduino when it's unplugged and then plugged back in.

I've never got unplug/replug detection working in Python.  It would go something like this: You'd need to detect the closing of the port (and the disappearing of its com port or device file), wait for its reappearance, and re-open it as you currently do.

You might get some coding ideas from this python network proxy I published on GitHub: https://github.com/billroy/serial-network-proxy/blob/master/bitty.py


-br

Logged

Pages: [1]   Go Up
Jump to: