From 3020957594918360a15a283165702d00f60b3440 Mon Sep 17 00:00:00 2001 From: Philippe Roy Date: Tue, 20 Dec 2022 04:52:25 +0100 Subject: [PATCH] =?UTF-8?q?Cr=C3=A9ation=20du=20jumeau=20num=C3=A9rique=20?= =?UTF-8?q?du=20bras=20robotis=C3=A9=20Roppy=20Ergo=20Jr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poppy_ergo_jr/AUTHORS.md | 12 + poppy_ergo_jr/CC-BY-SA-4.0.txt | 423 +++++++++++++++++++++++++++++++++ poppy_ergo_jr/ergo_jr.bat | 1 + poppy_ergo_jr/ergojr.py | 305 ++++++++++++++++++++++++ poppy_ergo_jr/ergojr_cmd.py | 59 +++++ poppy_ergo_jr/ergojr_doc.py | 94 ++++++++ poppy_ergo_jr/ergojr_lib.py | 406 +++++++++++++++++++++++++++++++ 7 files changed, 1300 insertions(+) create mode 100644 poppy_ergo_jr/AUTHORS.md create mode 100644 poppy_ergo_jr/CC-BY-SA-4.0.txt create mode 100644 poppy_ergo_jr/ergo_jr.bat create mode 100644 poppy_ergo_jr/ergojr.py create mode 100644 poppy_ergo_jr/ergojr_cmd.py create mode 100644 poppy_ergo_jr/ergojr_doc.py create mode 100644 poppy_ergo_jr/ergojr_lib.py diff --git a/poppy_ergo_jr/AUTHORS.md b/poppy_ergo_jr/AUTHORS.md new file mode 100644 index 0000000..6b53487 --- /dev/null +++ b/poppy_ergo_jr/AUTHORS.md @@ -0,0 +1,12 @@ +# Bras robotisé Poppy Ergo Jr + +Maquette numérique : Fichiers STEP +- Licence : Creative Commons BY-SA V4 +- Dépôt : https://github.com/poppy-project/poppy-ergo-jr +- Auteur : https://www.poppy-project.org + +Maquette numérique : Fichiers FreeCAD +- Licence : Creative Commons BY-SA V4 +- Dépôt : https://gitlab.com/blender-edutech/blender-edutech +- Auteur : Philippe Roy + diff --git a/poppy_ergo_jr/CC-BY-SA-4.0.txt b/poppy_ergo_jr/CC-BY-SA-4.0.txt new file mode 100644 index 0000000..e27a713 --- /dev/null +++ b/poppy_ergo_jr/CC-BY-SA-4.0.txt @@ -0,0 +1,423 @@ +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/poppy_ergo_jr/ergo_jr.bat b/poppy_ergo_jr/ergo_jr.bat new file mode 100644 index 0000000..66b6ce8 --- /dev/null +++ b/poppy_ergo_jr/ergo_jr.bat @@ -0,0 +1 @@ +ergo_jr.exe -con diff --git a/poppy_ergo_jr/ergojr.py b/poppy_ergo_jr/ergojr.py new file mode 100644 index 0000000..331517a --- /dev/null +++ b/poppy_ergo_jr/ergojr.py @@ -0,0 +1,305 @@ +import bge # Bibliothèque Blender Game Engine (UPBGE) +import twin # Bibliothèque de l'environnement 3D des jumeaux numériques +import math +import time + +############################################################################### +# ergojr.py +# @title: Commandes pour le bras robotisé Poppy Ergo Jr +# @project: Blender-EduTech +# @lang: fr +# @authors: Philippe Roy +# @copyright: Copyright (C) 2022 Philippe Roy +# @license: GNU GPL +# +# Commandes déclenchées par/sur le simulateur (sml_*) +############################################################################### + +# Récupérer la scène UPBGE +scene = bge.logic.getCurrentScene() + +# Couleurs + +couleur_magenta = (0.800, 0.005, 0.315,1) # bouton non activable : magenta +couleur_orange = (0.799, 0.130, 0.063,1) # bouton activable : orange +couleur_blanc = (0.8, 0.8, 0.8, 1) # bouton focus : blanc +couleur_jaune = (0.8, 0.619, 0.021, 1) # bouton activé : jaune + +# Constantes UPBGE + +JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED +JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED +ACTIVATE = bge.logic.KX_INPUT_ACTIVE +# JUST_DEACTIVATED = bge.logic.KX_SENSOR_JUST_DEACTIVATED + +############################################################################### +# Initialisation de la scène +############################################################################### + +def init(cont): + if cont.sensors['Init'].positive == False: # 1 seule fois + return False + + twin.manip_init() # Manipulation du modèle 3D + twin.cmd_init() # Commandes + + # Mémorisation de la position et orientation des composants du système + scene.objects['Portail']['init_lx']=scene.objects['Portail'].worldPosition.x + scene.objects['Portail']['init_ly']=scene.objects['Portail'].worldPosition.y + scene.objects['Portail']['init_lz']=scene.objects['Portail'].worldPosition.z + scene.objects['Engrenage']['init_rx']=scene.objects['Engrenage'].worldOrientation.to_euler().x + scene.objects['Engrenage']['init_ry']=scene.objects['Engrenage'].worldOrientation.to_euler().y + scene.objects['Engrenage']['init_rz']=scene.objects['Engrenage'].worldOrientation.to_euler().z + + system_init() # Initialisation du système + +############################################################################### +# Actionneurs +############################################################################### + +## +# Action du simulateur pour le clignotant +## + +def sml_clignotant (cont): + if scene.objects['System']['run']: + obj = cont.owner + if obj['actif'] and scene.objects['Led allumee'].visible == False: + scene.objects['Led allumee'].setVisible(True,False) + scene.objects['Led'].setVisible(False,False) + # print ("Clignotant allumée") + if obj['actif']==False and scene.objects['Led allumee'].visible == True: + scene.objects['Led'].setVisible(True,False) + scene.objects['Led allumee'].setVisible(False,False) + # print ("Clignotant éteint") + +## +# Action du simulateur pour le moteur +## + +def sml_moteur (cont): + if scene.objects['System']['run']: + obj = cont.owner + pas_rot = math.pi/7 # z = 14 + pas = 2.35619/0.3 # pas echelle 1:1 = 2.35619 -> pas à l'échelle de la maquette (0,3) : 2.35619/0.3 = 7,85396 + vitesse = 0.05 + engrenage_obj = scene.objects['Engrenage'] + portail_obj = scene.objects['Portail'] + if obj['actif_ouvrir']: + # print (scene.objects['Portail'].worldPosition.x) + engrenage_obj.applyRotation((0, 0, -pas_rot*vitesse), True) + portail_obj.applyMovement((-pas*vitesse, 0, 0), True) + # else: + if obj['actif_fermer']: + # print (scene.objects['Portail'].worldPosition.x) + engrenage_obj.applyRotation((0, 0, pas_rot*vitesse), True) + portail_obj.applyMovement((pas*vitesse, 0, 0), True) + +############################################################################### +# Capteurs fin de course +############################################################################### + +## +# Etat capteur fin de course portail ouvert +## + +def sml_fdc_ouvert (cont): + if scene.objects['System']['run'] : + obj = cont.owner + obj_etat=obj['actif'] + obj_microrupteur=scene.objects['Microrupteur fdc ouvert'] + # Etat capteur en fonction de la grille : worldPosition.x : 0 -> 65.5 et localPosition.x : 0 -> 218 + if scene.objects['Portail'].localPosition.x <= 0 and obj['actif'] == False : + obj['actif'] = True + if scene.objects['Portail'].localPosition.x > 0 and obj_microrupteur['actif'] == False and obj['actif'] == True : + obj['actif'] = False + #Forçage + if obj_microrupteur['actif'] == True: + obj['actif'] = True + #Couleurs + if obj['actif'] == True and obj_microrupteur.color !=couleur_jaune: + obj_microrupteur.color =couleur_jaune + if obj['actif'] == False : + if obj_microrupteur['MO'] == True and obj_microrupteur.color !=couleur_blanc: + obj_microrupteur.color =couleur_blanc + if obj_microrupteur['MO'] == False and obj_microrupteur.color !=couleur_orange: + obj_microrupteur.color =couleur_orange + +## +# Etat capteur fin de course portail fermé +## + +def sml_fdc_ferme (cont): + if scene.objects['System']['run'] : + obj = cont.owner + obj_etat=obj['actif'] + obj_microrupteur=scene.objects['Microrupteur fdc ferme'] + # Etat capteur en fonction de la grille : worldPosition.x : 0 -> 65.5 et localPosition.x : 0 -> 218 + if scene.objects['Portail'].localPosition.x >= 218 and obj['actif'] == False : + obj['actif'] = True + if scene.objects['Portail'].localPosition.x < 218 and obj_microrupteur['actif'] == False and obj['actif'] == True : + obj['actif'] = False + #Forçage + if obj_microrupteur['actif'] == True: + obj['actif'] = True + #Couleurs + if obj['actif'] == True and obj_microrupteur.color !=couleur_jaune: + obj_microrupteur.color =couleur_jaune + if obj['actif'] == False : + if obj_microrupteur['MO'] == True and obj_microrupteur.color !=couleur_blanc: + obj_microrupteur.color =couleur_blanc + if obj_microrupteur['MO'] == False and obj_microrupteur.color !=couleur_orange: + obj_microrupteur.color =couleur_orange + +## +# Forçage capteur fin de course avec la souris +## + +def sml_microrupteur (cont): + if scene.objects['System']['run'] : + system_click(cont, cont.owner) + +############################################################################### +# Capteur barrage +############################################################################### + +## +# Emetteur IR +## + +def sml_emet_ir (cont): + if scene.objects['System']['run'] : + obj = cont.owner + obj_emetteur_ir=scene.objects['Emetteur IR'] + obj_recepteur_ir=scene.objects['Recepteur IR'] + if obj['actif'] and scene.objects['Emetteur IR Led allumee'].visible == False: + scene.objects['Emetteur IR Led allumee'].setVisible(True,False) + scene.objects['Emetteur IR Led'].setVisible(False,False) + obj_emetteur_ir.color = couleur_orange + obj_recepteur_ir.color = couleur_orange + if obj['actif']==False and scene.objects['Emetteur IR Led allumee'].visible == True: + scene.objects['Emetteur IR Led'].setVisible(True,False) + scene.objects['Emetteur IR Led allumee'].setVisible(False,False) + obj_emetteur_ir.color = couleur_magenta + obj_recepteur_ir.color = couleur_magenta + +## +# Récepteur IR +## + +def sml_recep_ir (cont): + if scene.objects['System']['run'] and scene.objects['Module emetteur IR']['actif'] : + obj = cont.owner + system_click(cont, scene.objects['Module recepteur IR']) + if scene.objects['Module recepteur IR']['actif']==True : + scene.objects['Emetteur IR'].color = couleur_jaune + scene.objects['Recepteur IR'].color = couleur_jaune + +############################################################################### +# Boutons poussoirs +############################################################################### + +## +# Bouton pousssoir coté rue +## + +def sml_bp_rue (cont): + if scene.objects['System']['run'] : + system_click(cont, scene.objects['Module bouton cote rue']) + +## +# Bouton pousssoir coté cour +## + +def sml_bp_cour (cont): + if scene.objects['System']['run'] : + system_click(cont, scene.objects['Module bouton cote cour']) + +############################################################################### +# Système +############################################################################### + +## +# Initialisation du système +# Le moteur est géré en continue. +## + +def system_init (): + + # Clignotant + scene.objects['Module led']['actif']=False + scene.objects['Led allumee'].setVisible(False,False) + scene.objects['Led'].setVisible(True,False) + + # Emetteur IR + scene.objects['Module emetteur IR']['actif']=False + scene.objects['Emetteur IR Led allumee'].setVisible(False,False) + scene.objects['Emetteur IR Led'].setVisible(True,False) + scene.objects['Emetteur IR'].color = couleur_magenta + scene.objects['Recepteur IR'].color = couleur_magenta + +## +# Réinitialisation du système +## + +def system_reset (): + + # Grille à l'état initial + # applyRotationTo(scene.objects['System'], 0, 0, 0) + scene.objects['Portail'].worldPosition.x = scene.objects['Portail']['init_lx'] + scene.objects['Portail'].worldPosition.y = scene.objects['Portail']['init_ly'] + scene.objects['Portail'].worldPosition.z = scene.objects['Portail']['init_lz'] + + # Moteur à l'état initial + rres=0.001 # resolution rotation + obj1=scene.objects['Engrenage'] + while (obj1.localOrientation.to_euler().y) > 1.1*rres : + obj1.applyRotation((0, 0, -rres), True) + while (obj1.localOrientation.to_euler().y) < -1.1*rres : + obj1.applyRotation((0, 0, rres), True) + + # Led à l'état initial + scene.objects['Led'].setVisible(True,False) + scene.objects['Led allumee'].setVisible(False,False) + + # Capteur barrage IR + scene.objects['Emetteur IR Led'].setVisible(True,False) + scene.objects['Emetteur IR Led allumee'].setVisible(False,False) + scene.objects['Emetteur IR'].color = couleur_magenta + scene.objects['Recepteur IR'].color = couleur_magenta + + # I/O à l'état initial + scene.objects['Module led']['actif']=False + scene.objects['Ensemble moteur']['actif_ouvrir']=False + scene.objects['Ensemble moteur']['actif_fermer']=False + scene.objects['Capteur fdc ouvert']['actif'] =True + scene.objects['Capteur fdc ferme']['actif'] =False + scene.objects['Module emetteur IR']['actif'] =False + scene.objects['Module recepteur IR']['actif'] =False + scene.objects['Module bouton cote rue']['actif'] =False + scene.objects['Module bouton cote cour']['actif'] =False + +## +# Click sur les éléments cliquables du systèmes +## + +def system_click(cont, obj_activation): + obj = cont.owner + if cont.sensors['MO'].status == JUST_ACTIVATED : + obj.color = couleur_blanc + obj['MO']=True + if cont.sensors['Click'].status == JUST_ACTIVATED and scene.objects['System']['manip_mode']==0: + if obj['MO']: + obj_activation['actif'] = True + obj.color = couleur_jaune + if cont.sensors['MO'].status == JUST_RELEASED : + obj['MO']=False + if cont.sensors['Click'].status == ACTIVATE : + obj.color = couleur_jaune + else: + obj.color = couleur_orange + if cont.sensors['Click'].status == JUST_RELEASED: + obj_activation['actif'] = False + obj.color = couleur_blanc + if cont.sensors['MO'].status != ACTIVATE : + obj.color = couleur_orange diff --git a/poppy_ergo_jr/ergojr_cmd.py b/poppy_ergo_jr/ergojr_cmd.py new file mode 100644 index 0000000..88f3134 --- /dev/null +++ b/poppy_ergo_jr/ergojr_cmd.py @@ -0,0 +1,59 @@ +from ergojr_lib import * # Bibliothèque Poppy Ergo Jr + +############################################################################### +# ergojr_cmd.py +# @title: Commandes du bras robotisé Poppy Ergo Jr +############################################################################### + +############################################################################### +# Instructions élémentaires du bras robotisé Poppy Ergo Jr +# +# Actions (ordre = True ou False) : +# - Allumer voyant niveau 0 : voy_0 (True|False) +# - Allumer voyant niveau 1 : voy_1 (True|False) +# - Monter le monte-charge (moteur sens trigo) : mot_m (True|False) +# - Descendre le monte-charge (moteur sens horaire) : mot_d (True|False) +# +# Capteurs (valeur retournée = True ou False) : +# - Capteur présence cabine niveau 0 : pc_0() +# - Capteur présence cabine niveau 1 : pc_1() +# +# Pupitre (valeur retournée = True ou False) : +# - Bouton poussoir appel niveau 0 : ba_0() +# - Bouton poussoir appel niveau 1 : ba_1() +# +# Gestion du temps : +# - Temporisation en seconde : tempo(duree) +# +############################################################################### + +# Brochage du monte-charge +brochage={} + +############################################################################### +# Fonctions +############################################################################### + +############################################################################### +# Commandes +############################################################################### + +def commandes(): + + # Ecrire votre code ici ... + voy_0(True) # Activer le voyant du niveau 0 + while True: + pass + + fin() # A garder + + +############################################################################### +# En: External call << DONT CHANGE THIS SECTION >> +# Fr: Appel externe << NE PAS MODIFIER CETTE SECTION >> +############################################################################### + +if __name__=='start': + thread_cmd_start(commandes) +if __name__=='stop': + thread_cmd_stop() diff --git a/poppy_ergo_jr/ergojr_doc.py b/poppy_ergo_jr/ergojr_doc.py new file mode 100644 index 0000000..f0bd948 --- /dev/null +++ b/poppy_ergo_jr/ergojr_doc.py @@ -0,0 +1,94 @@ +############################################################################### +# ergojr_doc.py +# @title: Documentation du bras robotisé Poppy Ergo Jr +# @project: Blender-EduTech +# @lang: fr +# @authors: Philippe Roy +# @copyright: Copyright (C) 2022 Philippe Roy +# @license: GNU GPL +############################################################################### + +################################################################################ +# Documentation du système +################################################################################ + +system_card=["twins-card", "serial-card", "movement-card", "sensor-card", "gyro-card", "board-card", "model-card", "arduino-card"] +card_description ={} + +# Jumeau numérique +# Message envoyé (asynchrone) : \n avancer : a, reculer : r, droite : d, \n gauche g, marquer : m et forer : f \n\n\n """ +card_twins_title="Jumeau numérique" +card_twins_text=""" jumeau() \n -> Active le jumeau réel.\n + jumeau_config(port, vitesse) \n -> Définit la configuration de la liaison \n série.\n + Si le port n\"est pas spécifié, il sera \n recherché automatiquement (carte \n Arduino Uno ou Mega). \n + La vitesse par défaut est 115200 baud.""" +card_twins_url=[] +card_description.update({"twins-card" : [card_twins_title, card_twins_text, card_twins_url]}) + +# Liaison série +card_serial_title="Liaison série" +card_serial_text=""" serie_msg(texte) \n -> Envoi un message \n + texte=serie_rcpt() \n -> Reçoit un message""" +card_serial_url=[["Liaison série : pySerial","https://pythonhosted.org/pyserial/"], + ["Protocole Firmata : pyFirmata","https://github.com/tino/pyFirmata"]] +card_description.update({"serial-card" : [card_serial_title, card_serial_text, card_serial_url]}) + +# Ouvrir et fermer +card_movement_title="Ouvrir et fermer" +card_movement_text=""" mot_o (True | False) \n -> Ouvre le portail (moteur sens trigo) \n + mot_f (True | False) \n -> Ferme le portail (moteur sens horaire) \n + fdc_o() \n -> Capteur fin de course portail ouvert\n Retourne True si le portail est ouvert. \n + fdc_f() \n -> Capteur fin de course portail fermé\n Retourne True si le portail est fermé.""" +card_movement_url=[] +card_description.update({"movement-card" : [card_movement_title, card_movement_text, card_movement_url]}) + +# Capteurs +card_sensor_title="Capteur Infrarouge" +card_sensor_text=""" ir_emet(True | False) \n -> Active l\"émetteur infrarouge (IR) \n + ir_recep() \n -> Récepteur barrage infrarouge (IR)\n Retourne True s\"il n\"y a pas d\"obstacle.""" +card_sensor_url=[] +card_description.update({"sensor-card" : [card_sensor_title, card_sensor_text, card_sensor_url]}) + +# Gyrophare +card_gyro_title="Gyrophare" +card_gyro_text=""" gyr (True | False) \n -> Active le gyrophare""" +card_gyro_url=[] +card_description.update({"gyro-card" : [card_gyro_title, card_gyro_text, card_gyro_url]}) + +# Pupitre +card_board_title="Pupitre" +card_board_text= """ bp_ext() \n -> Bouton poussoir coté rue\n Retourne True si le bouton est pressé.\n + bp_int() \n -> Bouton poussoir coté cour\n Retourne True si le bouton est pressé.""" +card_board_url=[] +card_description.update({"board-card" : [card_board_title, card_board_text, card_board_url]}) + +# Maquette +card_model_title="Maquette" +card_model_text=""" Le modèle 3D est basé sur la maquette \n développée par l\"entreprise A4 Technologie. \n + Les documents techniques et \n pédagogiques signés A4 Technologie \n sont diffusés librement sous licence \n Creative Commons BY-NC-SA. \n + Le pilotage de la maquette se fait par une \n carte Arduino (Uno ou Mega) reliée à \n l"ordinateur via la liaison série (USB) et le \n protocole Firmata.""" +card_model_url=[["A4 Technologie","https://www.a4.fr"], + ["Maquette A4 Technologie","https://www.a4.fr/wiki/index.php?title=Portail_coulissant_(BE-APORT-COUL)"]] +card_description.update({"model-card" : [card_model_title, card_model_text, card_model_url]}) + +# Arduino +card_arduino_title="Arduino" +card_arduino_text=""" Arduino une plateforme open-source de \n développement électronique basée sur \n le microcontrôleur de la famille ATmega. + + Elle est utilisée pour la création d"objets \n électroniques interactifs et connectés : \n IoT, domotique, robotique, ... + + Le langage de programmation est le C. Par \n la bibliothèque Arduino il est particulièrement \n aisé d"accéder aux entrées/sorties de la + carte. Les platines permettent l"ajout \n d"extensions : relais, Grove, RFID, GPS, ... """ + +card_arduino_url=[["Plateforme Arduino","https://www.arduino.cc/"]] +card_description.update({"arduino-card" : [card_arduino_title, card_arduino_text, card_arduino_url]}) + +## +# Envoi des données +## + +def get_system_card(): + return system_card + +def get_card_description(): + return card_description diff --git a/poppy_ergo_jr/ergojr_lib.py b/poppy_ergo_jr/ergojr_lib.py new file mode 100644 index 0000000..09cba5f --- /dev/null +++ b/poppy_ergo_jr/ergojr_lib.py @@ -0,0 +1,406 @@ +import bge # Bibliothèque Blender Game Engine (UPBGE) +import threading # Multithreading +import trace +import sys +import time + +import serial # Liaison série +import pyfirmata # Protocole Firmata +from serial.tools.list_ports import comports # Détection du port automatique + +############################################################################### +# ergojr_lib.py +# @title: Bibliothèque utilisateur du bras robotisé Poppy Ergo Jr +# @project: Blender-EduTech +# @lang: fr +# @authors: Philippe Roy +# @copyright: Copyright (C) 2022 Philippe Roy +# @license: GNU GPL +############################################################################### + +scene = bge.logic.getCurrentScene() + +# Threads +threads_cmd=[] +debug_thread = scene.objects['System']['debug_thread'] + +# Jumeau numérique +board = None +board_it = None # Iterator (input) +bp_int_pin = None +bp_ext_pin = None +fdc_o_pin = None +fdc_f_pin = None +ir_emett_pin = None +ir_recept_pin = None +mot_o_pin = None +mot_f_pin = None +gyr_pin = None + +# UPBGE constants +JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED +JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED +ACTIVATE = bge.logic.KX_INPUT_ACTIVE +# JUST_DEACTIVATED = bge.logic.KX_SENSOR_JUST_DEACTIVATED + +############################################################################### +# Méthode kill pour les tâches (threads) +############################################################################### + +class thread_with_trace(threading.Thread): + def __init__(self, *args, **keywords): + threading.Thread.__init__(self, *args, **keywords) + self.killed = False + + def start(self): + self.__run_backup = self.run + self.run = self.__run + threading.Thread.start(self) + + def __run(self): + sys.settrace(self.globaltrace) + self.__run_backup() + self.run = self.__run_backup + + def globaltrace(self, frame, event, arg): + if event == 'call': + return self.localtrace + else: + return None + + def localtrace(self, frame, event, arg): + if self.killed: + if event == 'line': + raise SystemExit() + return self.localtrace + + def kill(self): + self.killed = True + +############################################################################### +# Start et stop des tâches (threads) +############################################################################### + +def thread_start(threads, type_txt, fct): + threads.append(thread_with_trace(target = fct)) + threads[len(threads)-1].start() + if (debug_thread): + print ("Thread",type_txt, "#", len(threads)-1, "open.") + +def thread_stop(threads, type_txt): + i=0 + zombie_flag=False + for t in threads: + if not t.is_alive(): + if (debug_thread): + print ("Thread",type_txt, "#",i,"closed.") + else: + if (debug_thread): + print ("Thread",type_txt, "#",i,"still open ...") + t.kill() + t.join() + if not t.is_alive(): + if (debug_thread): + print ("Thread",type_txt, "#",i,"killed.") + else: + if (debug_thread): + print ("Thread",type_txt, "#",i,"zombie...") + zombie_flag=True + i +=1 + if zombie_flag==False: + if (debug_thread): + print ("All threads",type_txt, "are closed.") + scene.objects['System']['thread_cmd']=False + return True + else: + if (debug_thread): + print ("There are zombies threads",type_txt, ".") + return False + +def thread_cmd_start(fct): + thread_start(threads_cmd, "commands", fct) + +def thread_cmd_stop(): + thread_stop(threads_cmd, "commands") + +def end(): + + # Jumeau numérique + if scene.objects['System']['twins']: + # serial_msg = "FI\n" + # twins_serial.write(serial_msg.encode()) # Communication série : modele 3d -> carte communication ( arduino | micro:bit ) + jumeau_close() + + # Thread + if (debug_thread): + print ("Thread commands is arrived.") + time.sleep(0.125) + scene.objects['System']['thread_cmd']=False + time.sleep(0.125) + +def fin(): + end() + +def quit(): + end() + +############################################################################### +# Actionneurs +############################################################################### + +# Ordres utilisateur du clignotant +def gyr (ordre): + global gyr_pin + scene.objects['Module led']['actif']=ordre + if scene.objects['System']['twins'] : + if ordre : + gyr_pin.write(1) + else: + gyr_pin.write(0) + +# Ordres utilisateur du moteur +def mot_o (ordre): + scene.objects['Ensemble moteur']['actif_ouvrir']=ordre + +def mot_f (ordre): + scene.objects['Ensemble moteur']['actif_fermer']=ordre + +# Ordre utilisateur du capteur barrage IR +def ir_emet(ordre): + scene.objects['Module emetteur IR']['actif']=ordre + +############################################################################### +# Capteurs +############################################################################### + +# Compte-rendu utilisateur du capteur fin de course portail ouvert +def fdc_o (): + return scene.objects['Capteur fdc ouvert']['actif'] + +# Compte-rendu utilisateur du capteur fin de course portail ouvert +def fdc_f (): + return scene.objects['Capteur fdc ferme']['actif'] + +# Compte-rendu utilisateur du capteur barrage IR +def ir_recep (): + if scene.objects['Module recepteur IR']['actif'] : + return False + else: + return True + +############################################################################### +# Boutons poussoirs +############################################################################### + +# Compte-rendu utilisateur du bouton pousssoir coté rue +def bp_ext (): + return scene.objects['Module bouton cote rue']['actif'] + +# Compte-rendu utilisateur du bouton pousssoir coté cour +def bp_int (): + return scene.objects['Module bouton cote cour']['actif'] + +############################################################################### +# Temporisation +############################################################################### + +def tempo (duree): + time.sleep(duree) + +############################################################################### +# Jumeau numérique +############################################################################### + +## +# Recherche automatique du port +## + +def serial_autoget_port(): + # USB Vendor ID, USB Product ID + board_dict={'microbit' :[3368, 516], + 'uno' :[9025, 67], + 'mega' :[9025, 66]} + for com in comports(): # Arduino Uno + if com.vid == board_dict["uno"][0] and com.pid == board_dict["uno"][1]: + return [com.device,"Arduino Uno"] + for com in comports(): # Arduino Mega + if com.vid == board_dict["mega"][0] and com.pid == board_dict["mega"][1]: + return [com.device,"Arduino Mega"] + return [None,""] + +## +# Création de l'objet carte (protocole Firmata) +## + +def board_init(port): + try: + return pyfirmata.Arduino(port) + except: + return None + +## +# Création de l'objet serial (communication série) +## + +def serial_init(port,speed): + try: + return serial.Serial(port,speed) + except: + return None + +## +# Affiche la liste des cartes (communication série) +## + +def serial_devices(): + for com in comports(): + print ("Name : "+str(com.name)+"\n" + +" Device : "+str(com.device)+"\n" + +" Hardware ID : "+str(com.hwid)+"\n" + +" USB Vendor ID : "+str(com.vid)+"\n" + +" USB Product ID : "+str(com.pid)+"\n" + +" USB device location : "+str(com.location)+"\n" + +" USB manufacturer : "+str(com.manufacturer)+"\n" + +" USB product : "+str(com.product)+"\n" + +" Interface-specific : "+str(com.interface)) + +## +# Activation de la communication avec la carte de communication (Arduino, Micro:bit) +# Vitesse : 115200 -> 7 fps, 38400 -> 6 fps, 9600 -> 2 fps +# pyserial : baudrate=115200 +# pyfirmata : baudrate=57600 +## + +def jumeau(pins): + global board + # global gyr_pin + + # UI : étape 1 + scene.objects['Twins-icon'].setVisible(True,True) + scene.objects['Twins-text']['Text'] = "Connection en cours ..." + scene.objects['Twins-text'].setVisible(True,False) + + # Mise en place de la carte + speed = 57600 + [device,board_name] =serial_autoget_port() # Recherche automatique du port + if device is None: + scene.objects['System']['twins'] = False + scene.objects['Twins-text']['Text'] = "Aucune connection disponible : jumeau réel débranché." + return False + board = board_init(device) + if board is None: + scene.objects['System']['twins'] = False + scene.objects['Twins-text']['Text'] = "Aucune connection disponible : port "+device+" pas prêt" + return False + scene.objects['System']['twins'] = True + # scene.objects['System']['twins_close'] = False + scene.objects['System']['twins_port'] = device + scene.objects['System']['twins_speed'] = speed + # scene.objects['System']['twins_readline'] = "" + board_it = pyfirmata.util.Iterator(board) # Itérateur pour les entrées + board_it.start() + + # UI : étape 2 + if board =="": + scene.objects['Twins-text']['Text'] = "Connection ouverte : "+device+" - "+str(speed)+" baud" + else: + scene.objects['Twins-text']['Text'] = "Connection ouverte : "+board_name+" sur "+device+" à "+str(speed)+" baud" + tempo (0.1) + + # Déclaration des entrées - sorties + for pin in pins: + print (pin) + # if + # bp_ext_pin = board_io('d:'+str(es_dict['bp_ext'])+':i') # Bouton poussoir coté rue + # bp_int_pin = board_io('d:'+str(es_dict['bp_int'])+':i') # Bouton poussoir coté cour + # fdc_o_pin = board_io('d:'+str(es_dict['fdc_o'])+':i') # Capteur fin de course portail ouvert + # fdc_f_pin = board_io('d:'+str(es_dict['fdc_f'])+':i') # Capteur fin de course portail fermé + # ir_recept_pin = board_io('d:'+str(es_dict['ir_recept'])+':i') # Recepteur pour le capteur barrage IR + + # gyr_pin = board_io('d:'+str(es_dict['gyr'])+':o') # Gyrophare + # mot_o_pin = board_io('d:'+str(es_dict['mot_o'])+':o') # Ouvrir le portail (moteur sens trigo) + # mot_f_pin = board_io('d:'+str(es_dict['mot_f'])+':o') # Fermer le portail (moteur sens horaire + # ir_emett_pin = board_io('d:'+str(es_dict['ir_emett'])+':o') # Emetteur pour le capteur barrage IR + return True + +# def board_io(da,pin,io): +# if pin_def is not None: +# return board.get_pin(da+':'+pin_def) +# else: +# print ("Définition entrée-sortie non trouvée : "+pin_def) + +## +# Fermeture de la communication série +## + +def jumeau_close(): + global board + # twins_serial.close() # Fermer proprement le port série + board.exit() # Fermer proprement la communication avec la carte + scene.objects['System']['twins'] = False + scene.objects['Twins-text']['Text'] = "Connection fermée" + +# Configuration du port +# FIXME +def jumeau_config(port, speed): + # global board + pass + # global twins_serial + # if scene.objects['System']['twins']: + # serial_msg1 = "CF\n" + # twins_serial.write(serial_msg1.encode()) + # tempo (1) + # serial_msg2 = str(speed)+"\n" + # twins_serial.write(serial_msg2.encode()) + # tempo (1) + # serial_msg3 = str(temps_avancer)+"\n" + # twins_serial.write(serial_msg3.encode()) + # tempo (1) + # serial_msg4 = str(temps_tourner)+"\n" + # twins_serial.write(serial_msg4.encode()) + # tempo (1) + # serial_msg5 = "FC\n" + # twins_serial.write(serial_msg5.encode()) + +## +# Envoi d'un message vers la communication série +## + +# def serie_msg(text): +# global twins_serial +# text2= text+"\n" +# scene.objects['Twins-text']['Text'] = "Communication : envoi message : "+text +# twins_serial.write(text2.encode()) + +## +# Mise en écoute de jumeau numérique (figeage de la scène) +## + +# def twins_listen(cont): +# global twins_serial +# if scene.objects['System']['twins']: +# if scene.objects['System']['twins_readline'] != "": +# scene.objects['Twins-text']['Text'] = "Écoute de la connection figeage de la scène.... Message reçu : "+scene.objects['System']['twins_readline'] +# else: +# scene.objects['Twins-text']['Text'] = "Écoute de la connection figeage de la scène..." +# if cont.sensors['Property'].positive: +# if scene.objects['System']['twins_listen'] : +# serial_msg = twins_serial.readline() +# if serial_msg is not None: +# scene.objects['System']['twins_readline'] = str(serial_msg) +# # scene.objects['Twins-text']['Text'] = "Message reçu : "+str(serial_msg) +# scene.objects['System']['twins_listen'] = False + +## +# Réception d'un message de la communication série +## + +# def serie_rcpt(): +# # scene.objects['Twins-text']['Text'] = "Écoute de la \nconnection\n figeage de \n la scène" +# scene.objects['System']['twins_readline'] = "" +# scene.objects['System']['twins_listen'] = True +# while scene.objects['System']['twins_readline'] == "": +# if scene.objects['System']['twins_readline'] != "": +# break +# # scene.objects['Twins-text']['Text'] = "Connection\nouverte :\n"+scene.objects['System']['twins_port']+"\n"+str(scene.objects['System']['twins_speed'])+" baud" +# return scene.objects['System']['twins_readline']