Errore comunicazione arduino/python

Qualcuno mi può aiutare con questo semplice codice? Non riesco a capire come risolvere questi errori che mi restituisce la shell di python.

Arduino:

char x='0';
void setup(){
  pinMode(13,OUTPUT);
  Serial.begin(9600);
}
void loop() {
  x=Serial.read();
  Serial.println(x);
  if(x=='a'){
    digitalWrite(13,HIGH);}
  else if(x=='0'){
    digitalWrite(13,LOW);}
  delay(500);
  
}

Python:

from tkinter import *
import serial
arduino=serial.Serial("com4",9600)
def accendi():
    arduino.write("a")
def spegni():
    arduino.write("0")

finestra=Tk()    
a=Label(text="Programma per accendere e spengere led arduino").pack()
d=Button(text="ACCENDI",command=accendi).pack()
c=Button(text="SPEGNI",command=spegni).pack()

Errore sulla shell di python:

>>> Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python33\lib\idlelib\run.py", line 109, in main
    seq, request = rpc.request_queue.get(block=True, timeout=0.05)
  File "C:\Python33\lib\queue.py", line 175, in get
    raise Empty
queue.Empty

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
    return self.func(*args)
  File "C:/Users/Luca santini/Desktop/Prova_accendi e spegni led/prova.py", line 5, in accendi
    arduino.write("a")
  File "C:\Python33\lib\site-packages\serial\serialwin32.py", line 258, in write
    data = bytes(data)
TypeError: string argument without an encoding

Questo compare quanto clicco sul tasto ACCENDI, ma anche per SPEGNI dà errore...

Grazie, aspetto qualche vostro suggerimento!

Prima di passare all'interfaccia grafica, conviene fare dei test dall'IDLE. Dall'IDLE funziona? Se scrivi ad uno ad uno i comandi, ti rendi conto meglio.

Se scrivi la sequenza di comandi

import serial
arduino=serial.Serial("com4",9600)
arduino.write("a")

cosa succede?

Usi la pyserial? Va bene con Python 3?

Ci vuole forse un:

arduino.open()

da qualche parte?

Trovato l'errore. Il tutto stava nel comando: arduino.write('a')

Leggendo qui: https://mail.python.org/pipermail/python-list/2009-October/555361.html

Sono arrivato a questo: arduino.write('a'.encode('utf-8'))

Che funziona perfettamente!!! Grazie ancora!

Scusate ma vorrei togliermi questo dubbio. Supponiamo di realizzare un qualcosa che richieda più di un sensore collegato ad arduino. Io voglio che tutti questi sensori restituiscano dei dati via seriale al pc, attraverso poi un software in python questi vengono acquisiti ed elaborati. La domanda è: come faccio a mandare diversi dati via seriale tutti insieme e far si che python riesca a riconoscerli? Mi spiego meglio: ho un sensore di umidità e di temperatura. Leggo i dati dai sensori e li invio via seriale. Come faccio a fare in modo che python riconosca il valore della temperatura e quello dell'umidità senza correre il rischio d invertirli? Quando poi ci sono MOLTI sensori la faccenda diventa ancora più difficile...Grazie!!!

Strano, ho usato tante volte la comunicazione senza dover specificare la codifica.

Per l'altra questione... puoi fare in 100 modi diversi.

  • Inviii da pytohon una richiesta e ricevi in sequenza i dati

  • Ad ogni dato associ una lettera e trasmetti "T20.0", "U67.0" e quando leggi sai cosa sono

  • Li trasmetti insieme "20.0/67.0"

...

Ok ho capito, vorrei optare però per un flusso continuo di 2 due dati, sostanzialmente sono due numeri. Il problema mi sorge quando vado a scrivere:

arduino=serial.Serial("com4",9600)
val=arduino.read()

Come faccio a sapere cosa va a leggere? O meglio: con questa istruzione lui legge entrambi i dati, cosi che io possa selezionarli? Magari se possibile un seplicissimo esempietto, grazie :)

Altro errore!
L’errore sta in questo: quando passo una variabile da arduino via seriale, sul monitor seriale va tutto bene, ma quando utilizzo poi questo numero in un programma in python mi da un errore di confronto fra bytes() (quello che proviene da arduino) e int().

Arduino:

char comando;
int sensore=0,stato=0;
void setup(){
  pinMode(13,OUTPUT);
  Serial.begin(9600);
 }

void loop(){
  sensore=analogRead(A0);
  Serial.println(sensore,DEC);
  delay(1000);
  comando=Serial.read();
  if(comando=='a'){
    digitalWrite(13,HIGH);
  }
  else if(comando=='s'){
    digitalWrite(13,LOW);
  } 
  stato=digitalRead(13);
  if(stato==1){stato=1025;}
  else if(stato==0){stato=1024;}
  //Serial.println(stato);
}

Python

from tkinter import *
import serial
arduino=serial.Serial("com4",9600)
def accendi():
    arduino.write('a'.encode('utf-8'))
def spegni():
    arduino.write('s'.encode('utf-8'))

finestra=Tk()
img=PhotoImage(file='lamp.gif') 
a=Button(text="ACCENDI",image=img,command=accendi).pack()
b=Button(text="SPEGNI",image=img,command=spegni).pack()
while(0==0):
    val=arduino.readline()
    if(val<250):
        accendi()
    else:
        spegni()

Alcune cose nel programma sono in più perchè è in via di sviluppo, quindi fate finta di non vederle.

Ecco l’errore che mi dà la shell di python:

Traceback (most recent call last):
  File "C:\Users\Luca santini\Desktop\Esercizi_python\Sensore luminosità\fotores.py", line 15, in <module>
    if(val<250):
TypeError: unorderable types: bytes() < int()

Vi prego aiutatemi!

Ciao,
Fai un esperimento prova a trasmettere :

 Serial.println("ciao Luca");

e vedi cosa ricevi via Python

se funziona trasformi le variabili in stringa su Arduino

paulus1969: conviene fare dei test dall'ID~~L~~E

idle in inglese significa "in ozio", ossia che non fa nulla. IDE significa Integrated Development Environment, ossia ambiente di sviluppo integrato ;)

Habe:

Traceback (most recent call last):

File “C:\Users\Luca santini\Desktop\Esercizi_python\Sensore luminosità\fotores.py”, line 15, in
    if(val<250):
TypeError: unorderable types: bytes() < int()

Beh, io non conosco il PYthon ma qui l’interprete ti dice che stai confrontando 2 variabili di tipo differente.

Vi prego aiutatemi!

:roll_eyes:

Come detto da @Leo Tu usi val=arduino.readline() per leggere e perciò leggi una frase, una riga, perciò una frase di testo, non un numero Devi convertirlo da testo "250" a numero 250.

http://stackoverflow.com/questions/379906/parse-string-to-float-or-int

Infatti.

Ribadisco che ti conviene fare i primi test con l'IDLE per capire cosa fare comando per comando e dopo implementare l'interfaccia grafica. Solo così ti potrai concentrare sui singoli comandi. Mi pare anche strana la mancanza di open e close sulla seriale...

Per quanto riguarda l'IDLE... ehm... ammetto di essere pigro, ma l'IDE di Python si chiama proprio IDLE, l'autore ha spiegato la sigla con una scusa per quella L, ma in realtà si pensa che sia una dedica ad Eric Idle (Python --- Monty Python... programmatori burloni!)

@Paulus: ah, ok. Non sapevo di questa cosa. Scusami.

leo72: ... Beh, io non conosco il PYthon ma qui l'interprete ti dice che stai confrontando 2 variabili di tipo differente.

Esatto ... Python è piuttosto permalosetto su certe cose, ma, come in tutti i linguaggi, esistono potenti sistemi di casting.

@Habe : fatti una ricerca su Google per : python casting ... troverai svariate informazioni ;)

Guglielmo

Ok grazie mille, davvero gentili. Semplicemente basta fare cosi:

val=int(val)

E "magicamente" ottengo il mio intero. Avevo capito dove stava il problema, ma non riuscivo a capire come risolverlo!

Riguardo a questo avete dei suggerimenti? Scusate la pesantezza ma questo mondo è mooolto affascinante! :)

Habe: Ok ho capito, vorrei optare però per un flusso continuo di 2 due dati, sostanzialmente sono due numeri. Il problema mi sorge quando vado a scrivere:

arduino=serial.Serial("com4",9600)
val=arduino.read()

Come faccio a sapere cosa va a leggere? O meglio: con questa istruzione lui legge entrambi i dati, cosi che io possa selezionarli? Magari se possibile un seplicissimo esempietto, grazie :)

In parte ti ha risposto @Paulus qui: http://forum.arduino.cc//index.php?topic=196800.msg1453153#msg1453153

Il "protocollo" ovvero come i due programmi decidono come scambiarsi i dati lo devi decidere da te. I metodi sono tanti. Il protocollo praticamente deve imporre i vincoli su come e cosa deve essere spedito e queste regole devono essere applicate nei due programmi. Il primo suggerimento di @paulus mi sembra ottimo, spedisci un carattere da Python per avvisare che stai spedendo qualcosa, esempio '@' Poi i dati li spedisci o con un formato specifico (esempio "A0020.00" e poi "B0130.12" con spazio occupato fisso) oppure se sempre 2 sono, li spedisci uno dietro l'altro con un divisore (esempio '/' la barra) Magari alla fine della trasmissione mandi un carattere di fine trasmissione esempio '#' Quindi un ipotetico protocollo potrebbe essere:

@A0020.00B0130.12#

Dove ogni parte del messaggio ha i significati suggeriti sopra. Logico che chi riceve deve interpretare i pezzi della frase ricevuta e controllare che sia conforme alle regole stabilite. Se il msg vìola le regole potrebbe (il ricevente) rispondere con un msg di errore (esempio "ER") e se okay con un msg di okay (esempio "OK"). Chi ha spedito se riceve ER allora ritrasmette.