[résolu]Moniteur et communication série

Salut à tous,

Assez nouveau sur Arduino (j'ai commencé à bricoler dessus il y a un mois environ), je me frotte pour la première fois à un problème qu'une simple recherche sur le net ne suffit à résoudre. Je me tourne donc vers l'option "poser la question directement".

J'ai codé une petite interface en Python avec Kivy pour controler les 3 canaux d'une led RGB via le port série. Jusque là ça va. Le problème que je rencontre me parait vraiment étrange: si le moniteur série de l'ide arduino est ouvert, tout fonctionne bien, ma led réagit quand j'agis sur l'interface. Mais si je ferme le moniteur série, plus rien, la led s'éteint et ne répond plus, sauf parfois d'un clignotement très bref. Si je rouvre le moniteur, tout redevient normal.

Donc je me demande: le moniteur série est-il indispensable à la communication (il me semblait pas, mais j'ai peut-être raté un truc en route) ?

Un bout de code de l'arduino nous aiderait bien.

En principe, non on n'est pas obligé d'ouvrir la console série sur l'arduino pour que çà fonctionne.

Par quel port série passes-tu ? L'usb ou le Tx/Rx de la carte directement ?

Si ma mémoire est bonne : le problème vient du fait que le moniteur envoie un RESET (par le DTR), à la connexion et à la déco. Si tu passes par une autre interface (par exemple moi mes prog sont en VB.NET et je n'ai pas ce problème) tu peux gérer le DTR et donc le RESET (ou non).

Le code que j'utilise est le suivant:

int rpin = 9; //def des pins de couleur
int gpin = 10;
int bpin = 11;

int rval; //valeurs de led
int gval;
int bval;

int rprev = rval;
int gprev = rval;
int bprev = rval;

void setup()
{
  pinMode(rpin, OUTPUT); //def des E/S
  pinMode(gpin, OUTPUT);
  pinMode(bpin, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  
  while (Serial.available() )
  {
      int rval = Serial.parseInt();
      int gval = Serial.parseInt();
      int bval = Serial.parseInt();
      
      if(rval >= 0 && rval <= 255 && gval >= 0 && gval <= 255 && bval >= 0 && bval <= 255 && Serial.read() == '\n')
     { 
      rprev = rval;
      gprev = gval;
      bprev = bval;  
     }
 
      analogWrite(rpin, rprev);
      analogWrite(gpin, gprev);
      analogWrite(bpin, bprev);
  }
}

La communication se fait par USB.

Pour ce qui est du reset, j'ai vu pas mal de choses là dessus, mais je ne suis pas sûr que ça soit ça. Puisque si je n'ouvre pas le moniteur, il ne devrait pas faire de reset, et donc la communication devrait s'établir sans lui, non ?

Bah oui mais si tu n'envois pas de commande ==> pas d'allumage des LED nan ?

La commande est envoyé par mon interface en Python sous la forme "ValR,ValG,ValB" que je parse avec Serial.parseInt(), et j'envoie ça dans la led avec les analogWrite. Voilà le code si vous voulez mais je pense pas que le problème soit là. Mon problème c'est que ça marche bien quand le moniteur série est ouvert, et pas quand il est fermé, et je ne comprends absolument pas pourquoi.

le code (y'a que la partie ser.write qui importe, le reste c'est de l'interface graphique)

import kivy
from kivy.uix.floatlayout import FloatLayout 
from kivy.uix.gridlayout import GridLayout
from kivy.uix.slider import Slider 
from kivy.uix.label import Label 
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.checkbox import CheckBox
from kivy.clock import Clock
import serial
import sys

#import ihmtest.py



#class demo(GridLayout):
	



class app(App):

	def build(self):
		self.data = str('0,0,0')
		demo = GridLayout(cols = 3, rows = 3)

		label1 = Label(
				text = "R",
				)

		label2 = Label(
				text = "V",
				)

		label3 = Label(
				text = "B",
				)

		slider1 = Slider(
				orientation = 'vertical',
				pos_hint = {'x':0.5},
				min = 0,
				max = 255,
				value = 0,
				step = 1)
		slider2 = Slider(
				orientation = 'vertical',
				pos_hint = {'x':0.5},
				min = 0,
				max = 255,
				value = 0,
				step = 1)

		slider3 = Slider(
				orientation = 'vertical',
				pos_hint = {'x':0.5},
				min = 0,
				max = 255,
				value = 0,
				step = 1)

		

		label4 = Label(
				text = str(int(slider1.value))
				)

		label5 = Label(
				text = str(int(slider2.value))
				)

		label6 = Label(
				text = str(int(slider3.value))
				)

		demo.add_widget(label1)
		demo.add_widget(label2)
		demo.add_widget(label3)

		demo.add_widget(slider1)
		demo.add_widget(slider2)
		demo.add_widget(slider3)
		
		demo.add_widget(label4)
		demo.add_widget(label5)
		demo.add_widget(label6)


		def on_touch_event1(slider1, value):
			#print "valR = ", value
			label4.text = str(int(value))
			update_data()

		def on_touch_event2(slider2, value):
			#print "valV = ", value
			label5.text = str(int(value))
			update_data()

		def on_touch_event3(slider3, value):
			#print "valB = ", value
			label6.text = str(int(value))
			update_data()

		def update_data():
			# import serial
			
			self.data = str(int(slider1.value)) + ',' + str(int(slider2.value)) + ',' + str(int(slider3.value)) + '\n'
			SERIALPORT = "/dev/tty.usbserial-A6004nYS" ##def du port
			try:
				ser = serial.Serial(SERIALPORT, 9600)
			except serial.SerialException:
			 	print "no device connected - exiting"
			 	sys.exit()
			#xprint self.data
			
			ser.write(self.data)
			


		slider1.bind(value = on_touch_event1)
		slider2.bind(value = on_touch_event2)
		slider3.bind(value = on_touch_event3)

		return demo
		

if __name__ == '__main__':
	app().run()

Je comprends plus rien xD

Ton code python ne marche que si tu lances le moniteur en même temps ?

Oui c'est ça. Enfin sans le moniteur la partie python tourne (mes valeurs s'actualisent sur l'affichage), mais la led reste désespérément éteinte. Et si j'ouvre le moniteur, alors là la led réagit, et je peux faire mumuse avec les couleurs grace à mon interface.
Je sais vraiment pas où regarder pour me débarasser de ça...

Oki alors on dirait tout simplement que tu n'ouvres pas ton port dans ton programme, je connais pas python je pourrais pas t'aider mais ça n'a pas l'air d'être coder dans la partie que tu as posté. Le moniteur ne pourrait pas s'ouvrir si le port était déjà ouvert dans ton soft

J'ai tenté un truc dans l'idée de ce que tu dis: j'ai calé un ser.open() (ouverture du port donc) dans mon script juste avant le ser.write, et là j'ai un plantage qui me dit "port already open". Qu'à cela ne tienne, je vais le fermer et le rouvrir et tout ira bien. J'ai donc ajouté un ser.close() en tout début de script, et un second juste après le ser.write.
Là ça plante plus. Mais ça a toujours le même comportement vis-à-vis du moniteur (fallait s'y atendre, j'ai juste calé des bouts de code qui s'annulent les uns les autres). Le problème semble pas être là. Peut être qu'il faut que je me penche plus en détail sur mon port usb, mais j'dois avouer que je sais pas comment faire et je suis pas sur d'avoir les compétences nécessaires.

Ce serait possible que le buffer série ne soit accessible que quand le moniteur est ouvert ? Ce serait quand même moyennement logique non ?

Ce qui est sûr c'est que le moniteur ne se lance pas si le port est déjà ouvert ailleurs (ça marche avec n'importe programme d'ailleurs, une fois l'accès autorisé il n'y que le programme en question qui a le controle du port). J'avoue que la je sèche, trop de langage différents pour moi xD

Je vais aller jeter un oeil sur la gestion des ports en python alors, je trouverai peut être un truc utile. Merci pour tes réponses en tout cas.

Demande à Jean-François, je crois que Python ça le connait :wink:

Bonjour,

Avant de rendre python coupable il serait bon de ce pencher sur le code arduino :grin:
Il y a pas mal de chose pas nette dedans :wink:

const byte rpin = 9;
const byte gpin = 10;
const byte bpin = 11;
/* const = valeur constante -> valeur optimisé à la compilation */
/* int -> byte, pourquoi gâcher 2 octets (int) pour stocker un valeur <255 (1 octet) ! */

/* Les variables rval, gval et bval sont déjà déclaré EN LOCAL dans loop() */

byte rprev = 0;
byte gprev = 0;
byte bprev = 0;
/* Assigner une valeur global avec une autre variable globale NON INITIALISE -> pas bon */
/* Toujours fixer une valeur par défaut constante connu */
/* int -> byte, dans loop() tu test les valeurs pour quelles soit compris entre 0 et 255 */
/* Ps: en réalité ces variables ne servent à rien ;) (voir plus bas pourquoi) */

void setup() {
  pinMode(rpin, OUTPUT);
  pinMode(gpin, OUTPUT);
  pinMode(bpin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  
  if (Serial.available() > 10) { /* 10 -> valeur arbitraire, à changer dans le code final */
  /* while -> if, loop() en soit est déja une boucle ! */
  /* /!\ Attendre qu'il y ai des info à lire sur Serial (ici au moins 10 char) sinon parseInt() va planter ! */
  
      int rval = Serial.parseInt();
      int gval = Serial.parseInt();
      int bval = Serial.parseInt();
      
      if(rval >= 0 && rval <= 255 && gval >= 0 && gval <= 255 && bval >= 0 && bval <= 255 && Serial.read() == '\n')
     { 
		  /* analogWrite() n'as besoin d'être appelé que quand on veut changer la valeur d'une pin PWM */
		  /* Du coup il devient totalement inutile de stocker la valeur précédente de lapin ;) */
		  rprev = rval;
		  gprev = gval;
		  bprev = bval;  
	 
		/* Cette partie du code ne doit être exécuté uniquement lorsque la valeur change */
		/* Elle doit donc être DANS le corps du if */
		  analogWrite(rpin, rprev);
		  analogWrite(gpin, gprev);
		  analogWrite(bpin, bprev);
	  }
  }
}

Salut,

En effet mon code était un peu sale. Ça se voit mieux quand quelqu'un met le doigt sur les erreurs.
Cela dit, ça ne change rien à mon problème, le comprtement reste le même. Mais bon au moins j'ai appris deux trois trucs pour mieux coder. Merci Skywodd.

CaptainNapalm:
Cela dit, ça ne change rien à mon problème, le comprtement reste le même. Mais bon au moins j'ai appris deux trois trucs pour mieux coder.

Je viens de voir un truc pas beau du tout dans le code python :wink:

Tu ouvre le port série à chaque appel de updateData() !
Ouvre le port série une fois pour toute lors de l'instanciation de ta classe app, puis fait uniquement l'appel à ser.write(data) dans upateData().
Si l'auto reset à l'ouverture du port série se fait (ce qui semble le cas) ton arduino reboot à chaque fois et le temps du lancement tu "perd" les info :wink:

Yep ça roule maintenant =D
Merci pour vos réponses.

Essaye avec : ser.write(self.data.encode("utf8"))

Grillé, raté :smiley:

CaptainNapalm:
Yep ça roule maintenant =D

Publie ton code arduino et python qui marche (si ça ne te dérange pas bien sûr).
Cela permettra aux futurs lecteurs de ce topic de résoudre leurs problèmes :wink:

Yo,
Ça faisait un moment que j'étais pas repassé sur ce sujet. Mais mieux vaut tard que jamais, je mets les codes ici pour ceux que ça pourrait intéresser.
Bon du coup c'est ultra basique, mais y'a moyen d'étoffer tout ça en trouvant d'autres trucs à controler via une interface graphique.

Le sketch Arduino (pour le coup c'est vraiment basique haha)

const byte rpin = 9;
const byte gpin = 10;
const byte bpin = 11;


void setup() {
  pinMode(rpin, OUTPUT);
  pinMode(gpin, OUTPUT);
  pinMode(bpin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  
  if (Serial.available() > 5) { 
    int rval = Serial.parseInt();
    int gval = Serial.parseInt();
    int bval = Serial.parseInt();
      
      if(rval >= 0 && rval <= 255 && gval >= 0 && gval <= 255 && bval >= 0 && bval <= 255 && Serial.read() == '\n'){ 
	analogWrite(rpin, rval);
        analogWrite(gpin, gval);
	analogWrite(bpin, bval);
      }
 }
}

Et le python

# -*- coding: ISO-8859-1 -*-


import kivy
from kivy.uix.gridlayout import GridLayout
from kivy.uix.slider import Slider 
from kivy.uix.label import Label 
from kivy.app import App

import serial
import sys

SERIALPORT = "/dev/tty.usbmodem1d11" ##def du port série
try:
	ser = serial.Serial(SERIALPORT, 9600)
except serial.SerialException:
	print "no device connected - exiting"
	sys.exit()





class app(App):

	def build(self):
		self.data = str('0,0,0')
		demo = GridLayout(cols = 4, rows = 3)

		label1 = Label(
				text = "R",
				)

		label2 = Label(
				text = "V",
				)

		label3 = Label(
				text = "B",
				)

		label7 = Label(
				text = "General",
				)

		slider1 = Slider(
				orientation = 'vertical',
				pos_hint = {'x':0.5},
				min = 0,
				max = 255,
				value = 0,
				step = 1)
		slider2 = Slider(
				orientation = 'vertical',
				pos_hint = {'x':0.5},
				min = 0,
				max = 255,
				value = 0,
				step = 1)

		slider3 = Slider(
				orientation = 'vertical',
				pos_hint = {'x':0.5},
				min = 0,
				max = 255,
				value = 0,
				step = 1)

		slider4 = Slider(
				orientation = 'vertical',
				pos_hint = {'x':0.5},
				min = 0,
				max = 100,
				value = 100,
				step = 1)

		

		label4 = Label(
				text = str(int(slider1.value))
				)

		label5 = Label(
				text = str(int(slider2.value))
				)

		label6 = Label(
				text = str(int(slider3.value))
				)

		label8 = Label(
				text = str(int(slider4.value))
				)


		#Construction de l'interface graphique
		demo.add_widget(label1)
		demo.add_widget(label2)
		demo.add_widget(label3)
		demo.add_widget(label7)

		demo.add_widget(slider1)
		demo.add_widget(slider2)
		demo.add_widget(slider3)
		demo.add_widget(slider4)
		
		demo.add_widget(label4)
		demo.add_widget(label5)
		demo.add_widget(label6)
		demo.add_widget(label8)


		#def des actions
		def on_touch_event1(slider1, value):
			#print "valR = ", value
			label4.text = str(int(value))
			update_data()

		def on_touch_event2(slider2, value):
			#print "valV = ", value
			label5.text = str(int(value))
			update_data()

		def on_touch_event3(slider3, value):
			#print "valB = ", value
			label6.text = str(int(value))
			update_data()

		def on_touch_event4(slider4, value):
			#slider qui controle l'intensité générale en multipliant les autres par un facteur entre 0 et 1
			label8.text = str(int(value))
			update_data()


		def update_data():
			
			self.data = str(int(slider1.value)*int(slider4.value)/100) + ',' + str(int(slider2.value)*int(slider4.value)/100) + ',' + str(int(slider3.value)*int(slider4.value)/100) + '\n'
			ser.write(self.data)
			
			
		slider1.bind(value = on_touch_event1)
		slider2.bind(value = on_touch_event2)
		slider3.bind(value = on_touch_event3)
		slider4.bind(value = on_touch_event4)

		return demo
		

if __name__ == '__main__':
	app().run()

Hésitez pas si vous avez des questions, et encore merci pour votre aide.