See site in english Voir le site en francais
Website skin:
home  download  forum  link  contact

Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

Author Topic: Le tableau de commande de Mars Bleu  (Read 1517 times)

0 Members and 1 Guest are viewing this topic.

Offline Mars Bleu

  • Hero Member
  • *****
  • Posts: 513
  • Karma: 18
Reply #25 - 14 January 2017, 10:17:55
      QUATRIÈME CHALLENGE: commander des leds à partir de Lua


Le premier challenge consistait à extraire des données d'Orbiter avec un script Lua. Ce challenge fait la deuxième partie du transfert de données vers la UNO elle-même programmée pour piloter les lumineux.

Le grand principe est qu'un script Lua doit fonctionner dans Orbiter, et échanger des données avec la UNO contenant le sketch qui les traite.

Il a fallu que je remette le nez dans le Web pour trouver quelque chose ("the nose buried in documentation" comme j'ai pu lire). C'est Widdernix dans le forum américain d'Orbiter qui m'a donné la solution dans le fil "talking with serial devices". Voir à: http://orbiter-forum.com/showthread.php?t=17847

Pour échanger des données, on a besoin d'une librairie, luars232.dll. J'ai cherché six semaines durant comment y accéder, avant de trouver qu'il fallait simplement mettre cette dll dans le répertoire d'Orbiter. Une fois cette dll bien positionnée, la commande rs232=require("luars232") ne faisait  plus planter le script Lua.
Nota: en ce qui me concerne, le plantage d'un script Lua sur Orbiter est tout à fait discret. En fait, on s'en aperçoit lorsque le défilé des note:set_text()ne fige plus à l'endroit du plantage présumé.

Après quelques mises au point, j'ai essayé le sketch Arduino suivant sur ce montage:



Code: [Select]
byte dataPin=2;
byte latchPin=3;
byte clockPin=4;
byte data=255;
byte byte1;


void setup()
  {
    pinMode (dataPin,OUTPUT);
    pinMode (latchPin,OUTPUT);
    pinMode (clockPin,OUTPUT);
    Serial.begin(9600);
  }
 
void loop()
{

    if (Serial.available()>0)
    {
    byte1=Serial.read();
    digitalWrite(latchPin,LOW);
    shiftOut(dataPin, clockPin, LSBFIRST, byte1);
    digitalWrite(latchPin, HIGH);

    }

   delay(150);
}

Le script Lua associé est le suivant:

Code: [Select]
note = oapi.create_annotation()
note:set_pos (0.35,0.1,0.8,0.95)
note:set_colour ({r=0.9,g=0.5,b=0.2})


rs232=require("luars232")
local port_name="COM4"
local e,p=rs232.open(port_name)
if e ~= rs232.RS232_ERR_NOERROR then
term.out('',string.format("can't open serial port '%s', error: '%s'\n", port_name, rs232.error_tostring(e)))
    return
end
-- set port settings
assert(p:set_baud_rate(rs232.RS232_BAUD_9600) == rs232.RS232_ERR_NOERROR)
assert(p:set_data_bits(rs232.RS232_DATA_8) == rs232.RS232_ERR_NOERROR)
assert(p:set_parity(rs232.RS232_PARITY_NONE) == rs232.RS232_ERR_NOERROR)
assert(p:set_stop_bits(rs232.RS232_STOP_1) == rs232.RS232_ERR_NOERROR)
assert(p:set_flow_control(rs232.RS232_FLOW_OFF)  == rs232.RS232_ERR_NOERROR)

byte1='U'
err, len_written = p:write(string.char(byte1))
  end

note:set_text('Longueur écrite='..len_written)
proc.wait_sysdt(1)

assert(p:close() == rs232.RS232_ERR_NOERROR)-- this closes the COM

note:set_text('End of program')
proc.wait_sysdt(1)
note:set_text('  ')
proc.wait_sysdt(1)

Et là, VICTOIRE, j'ai réussi à écrire dans le registre à décalage et à afficher un octet sur 8 leds. Le caractère 'U' correspondant à 85, c'est-à-dire 01010101 en binaire, soit une led sur deux. Changeons le contenu de la variable byte1, et les leds s'allumeront autrement.

Je vous laisse digérer tout ceci, avant de vous montrer un petit TP dont l'objet sera d'afficher le machmètre
d'un XR2.


Offline Mars Bleu

  • Hero Member
  • *****
  • Posts: 513
  • Karma: 18
Reply #26 - 27 January 2017, 14:48:02
Pour ceux qui suivent, et tous les autres qui dorment au fond de la classe  :rant: :badfinger:, voici le petit TP
que je vous annonçais il y a quelques jours:

Pour ce faire, j'ai étoffé mon montage provisoire en adjoignant une bredboard supplémentaire comprenant deux afficheurs 7 segments branchés sur un 4543 ( entrée 4 bits=>afficheur 7 segments). Avec ces deux afficheurs, je voulais donc avoir un machmètre, en allant chercher la valeur par:

                                                      m=v:get_machnumber()

Puis avec une mise en forme, l'envoyer  par le port USB à la UNO qui la renverrait aux registres de sortie.

Ci dessous, le script Lua:
Code: [Select]
function send_data(n1,n2)
  e,p=rs232.open(port_name)
  if e ~= rs232.RS232_ERR_NOERROR then
    term.out('',string.format("can't open serial port '%s', error: '%s'\n", port_name, rs232.error_tostring(e)))
      return
  end
  -- set port settings
  assert(p:set_baud_rate(rs232.RS232_BAUD_9600) == rs232.RS232_ERR_NOERROR)
  assert(p:set_data_bits(rs232.RS232_DATA_8) == rs232.RS232_ERR_NOERROR)
  assert(p:set_parity(rs232.RS232_PARITY_NONE) == rs232.RS232_ERR_NOERROR)
  assert(p:set_stop_bits(rs232.RS232_STOP_1) == rs232.RS232_ERR_NOERROR)
  assert(p:set_flow_control(rs232.RS232_FLOW_OFF)  == rs232.RS232_ERR_NOERROR)
  err, len_written = p:write(string.char(n2)..string.char(n1))
  assert(p:close() == rs232.RS232_ERR_NOERROR)-- this closes the COM
end

note = oapi.create_annotation()
note:set_pos (0.35,0.1,0.8,0.95)
note:set_colour ({r=0.9,g=0.5,b=0.2})


intro = "Machmètre sur 2 displays 7 segments"

note:set_text(intro)
proc.wait_sysdt(1.0)
v=vessel.get_interface('XR2-01')
rs232=require("luars232")
port_name="COM4"



repeat
  alt=oapi.get_altitude()
  mach = v:get_machnumber()
  mach_display=math.floor(10*mach)
  centaine=math.floor(mach_display/100)
  mach_display=mach_display-100*centaine
  dizaine=math.floor(mach_display/10)
  mach_display=mach_display-10*dizaine
  unite=mach_display
  char1=unite+16*dizaine
  char2=centaine
  note:set_text(char2.."  "..char1.."  "..centaine..dizaine..unite)
  if unite~=old_unite then
    send_data(char1, char2)
    old_unite=unite
  end
  proc.skip()
until alt>300000 -- sortie dès qu'on atteint 300 kms d'altitude

note:set_text('Longueur écrite='..len_written)
proc.wait_sysdt(1)


note:set_text('End of program')
proc.wait_sysdt(1)
note:set_text('  ')
proc.wait_sysdt(1)


Le sketch Arduino reçoit les données en boucle, envoyées par le script Lua, et les envoie dans les
registres à décalage pilotant les displays 7 segments par l'intermédiaire des 4553:
Code: [Select]
int dataPin=2;
int latchPin=3;
int clockPin=4;
int data=255;
byte octet1;
byte octet2;



void setup()
  {
    pinMode (dataPin,OUTPUT);
    pinMode (latchPin,OUTPUT);
    pinMode (clockPin,OUTPUT);
    Serial.begin(9600);
  }
 
void loop()
{

    if (Serial.available()>0)
    {
    octet1=Serial.read();
    octet2=Serial.read();
    digitalWrite(latchPin,LOW);
    shiftOut(dataPin, clockPin, LSBFIRST, octet1);
    shiftOut(dataPin, clockPin, LSBFIRST, octet2);
    digitalWrite(latchPin, HIGH);

    }

   delay(150);
}

On lance un SCN contenant l'ordre d'accès au script LUA, et on peut suivre la valeur des Machs
sur les deux afficheurs numériques 7 segments.

Envoyer des données d'Orbiter aux registres de sortie m'est désormais possible. Cela signifie aussi
que je peux synchroniser le simpit avec l'état du Xr2 au lancement du scénario.

Tout ceci est encore assez provisoire et expérimental. Dans mon prochain post, je vous exposerai comment j'ai optimisé ces bouts de code de façon simple.



 

« Last Edit: 27 January 2017, 15:26:14 by Mars Bleu »

Offline Bibi Uncle

  • Legend
  • ******
  • Posts: 2258
  • Country: Canada ca
  • Karma: 15
    • Site web perso
Reply #27 - 28 January 2017, 04:38:15
Continue Mars Bleu! Ton projet avance bien et j'adore lire les avancements au fur et à mesure!

Je suis plus ou moins sûr de comprendre la mise en forme que tu fais avant de l'envoyer à l'Arduino. Tu n'aurais pas pu faire un printf (ou la fonction équivalente en Lua, je n'ai jamais programmé avec ce langage) afin d'obtenir un string déjà formatée ? Ensuite, il suffirait d'envoyer les caractères les uns après les autres par UART et voilà. Dans ton code, tu sembles essayer de calculer manuellement les caractères nécessaires alors qu'une fonction standard aurait pu faire le travail à ta place.

Bibi Uncle
"Can't keep my eyes from the circling sky
Tongue-tied and twisted just an earth-bound misfit"

- Learning to Fly, Pink Floyd

Mireille :love:

Offline Mars Bleu

  • Hero Member
  • *****
  • Posts: 513
  • Karma: 18
Reply #28 - 28 January 2017, 19:44:38
 :salut: Merci, Bibi Uncle pour tes encouragements. :salut:
 J'ai encore plein d'avancements en réserve, tout en continuant à souder, découper, assembler, programmer.

Pour la mise en forme, j'ai fait en mode "artillerie lourde". Pour être plus subtil, j'ai demandé à un de mes fistons de me faire quelque chose, et il m'a pondu un bout de code que je n'ai pas encore eu le temps de
décortiquer. Je te le poste dès que je rentre à la maison (je ne l'ai pas pour l'instant).


Offline Mars Bleu

  • Hero Member
  • *****
  • Posts: 513
  • Karma: 18
Reply #29 - 28 January 2017, 19:58:37
Complément d'information:
Le principe que je mets en oeuvre pour la mise en forme est le suivant: on a besoin de quatre bits
pour piloter un 4553. Quatre bits, c'est un demi-octet, donc, avec un octet, je peux piloter deux
4553. C'est pour ça que je mets les dizaines avec un coeff de 16 (décalage des bits de quatre rangs).
Ainsi, je transmets dizaine et unités dans un seul registre à décalage 74HC595.

Je ne sais pas si je suis bien clair....


Offline Bibi Uncle

  • Legend
  • ******
  • Posts: 2258
  • Country: Canada ca
  • Karma: 15
    • Site web perso
Reply #30 - 30 January 2017, 19:22:25
Ah d'accord je comprends. Ça me semble légitime comme technique :top:

Bibi Uncle
"Can't keep my eyes from the circling sky
Tongue-tied and twisted just an earth-bound misfit"

- Learning to Fly, Pink Floyd

Mireille :love:

Offline Mars Bleu

  • Hero Member
  • *****
  • Posts: 513
  • Karma: 18
Reply #31 - 11 February 2017, 19:04:29
J'envoie un dernier chapitre sur la transmission des données entre l'ordinateur et la carte Arduino.
A mes yeux, c'est assez important, c'est pour ça que j'insiste un peu.

En fait, j'ai repris ce qui figure dans mes précédents posts, mais avec un peu de mises en forme,
et de séparation des éléments principaux.

En Lua, on doit en premier lieu ouvrir le port. J'ai appelé cette fonction: widdernixopen()
Code: [Select]
function widdernixopen()
     e,p=rs232.open(port_name)
      if e ~= rs232.RS232_ERR_NOERROR then
  note:set_text('Problème...')
  proc.wait_sysdt(1.41)
              return
      end
  -- set port settings
    assert(p:set_baud_rate(rs232.RS232_BAUD_9600) == rs232.RS232_ERR_NOERROR)
    assert(p:set_data_bits(rs232.RS232_DATA_8) == rs232.RS232_ERR_NOERROR)
    assert(p:set_parity(rs232.RS232_PARITY_NONE) == rs232.RS232_ERR_NOERROR)
    assert(p:set_stop_bits(rs232.RS232_STOP_1) == rs232.RS232_ERR_NOERROR)
    assert(p:set_flow_control(rs232.RS232_FLOW_OFF)  == rs232.RS232_ERR_NOERROR)
end

En deuxième lieu, la fermeture du port:
Code: [Select]
function widdernixclose()
    assert(p:close() == rs232.RS232_ERR_NOERROR)-- this closes the COM
end

Entre les deux (ouverture et fermeture) Lua doit envoyer les données vers Arduino. Elles soit sont contenues dans un tableau, ou bien en valeurs discrètes. Dans le cas d'un tableau, j'ai mis au point:

Code: [Select]
function init_data(data,start,width)
for i=start,width do
high_byte=math.floor(data[i]/256)
low_byte=data[i]-high_byte*256
err,len_written=p:write(string.char(high_byte)..string.char(low_byte))
end
end
J'ai mis dans ce tableau les valeurs donnant la position des auxiliaires dits APU (gear, airbrake,
baydoors, etc...). Cela permet de transmettre leur état, que ce soit en position déployée, rétractée ou intermédiaire, en vue de synchroniser les lumineux pilotés par l'Arduino et l'état logique de  l'engin chargé dans le fichier .scn.

Il y a bien sûr des données additionnelles (de plus en plus, à mesure que le cockpit s'amplifie). Pour les transmettre, j'ai écrit la fonction: init_status()
Pour l'instant, il y a trois valeurs int à transmettre, soit 6 octets supplémentaires:
Code: [Select]
function init_status()

        --envoi sur deux octets de la valeur SIPOstatus contenant l'état des 12 switches APU (dont l'APU lui même)
high_byte=math.floor(SIPOstatus/256)
low_byte=SIPOstatus-high_byte*256
err, len_written = p:write(string.char(high_byte)..string.char(low_byte))

        --envoi sur deux octets de la valeur SIPOtransit_status contenant l'état de position des 12 auxiliaires APU
high_byte=math.floor(SIPOtransit_status/256)
low_byte=SIPOtransit_status-high_byte*256
err, len_written = p:write(string.char(high_byte)..string.char(low_byte))

        --envoi sur deux octets de la valeur Miscstatus contenant l'état logique des RCS (OFF/ROT/LIN), ainsi que l'état
        Docké ou non du XR2. Il reste encore 13 bits dispos dans cette valeur

high_byte=math.floor(Miscstatus/256)
low_byte=Miscstatus-high_byte*256
err, len_written = p:write(string.char(high_byte)..string.char(low_byte))
end

Dans le script Lua, toutes ces fonctions sont appelées par:   
   widdernixopen()
   init_data(position,3,18)—transmission du tableau position[]
   init_status()
   widdernixclose()


Du côté de l'Arduino, , on doit recevoir ce qui est en provenance du sketch Lua. Un tableau de valeurs:

Code: [Select]
for (byte i=3;i<19;i++)
   {
//réception des données envoyées par Orbiter/Lua-----2*16=32 octets--------
      data=get_data(data);
      Pos[i]=data;
   }

Les données additionnelles:
Code: [Select]
//réception des données envoyées par Orbiter/Lua-----2 octets+les 32 déjà extraits---34 octets
    data=get_data(data);
    Switch_status=data;
 
//réception des données envoyées par Orbiter/Lua-----2 octets+les 34 déjà extraits---36 octets
    data=get_data(data);
    Transit_status=data;

//réception des données envoyées par Orbiter/Lua-----2 octets+les 36 déjà extraits---38 octets
    data=get_data(data);
    Miscstatus=data;

La fonction de réception et de transformation en int est:

Code: [Select]
  unsigned get_data(unsigned int value)
  {

    // réception des deux octets:
    byte high_byte=Serial.read();
    byte low_byte=Serial.read();

    //reconstitution de la valeur sur 2 octets
    value=256*high_byte+low_byte;
    return value;
  }//fin get_data()


J'ai dû me pencher sur la fonction réciproque: c'est-à-dire faire envoyer des données de l'Arduino vers Lua. En effet, le buffer d'entrée de la Leonardo ou de la Uno n'est que de 64 octets, ce qui n'est pas beaucoup. Si on a des paquets de données supérieurs à 64 octets, l'idée est de pouvoir envoyer un premier paquet, attendre une réponse, puis d'envoyer la suite.

Pour ce faire, dans le script Lua, j'initialise une variable: data_read="Z", et j'ai écrit une nouvelle fonction, la fonction copythat()
Code: [Select]
function copythat()
read_len=1
err, data_read, size=p:read(read_len)
end


Cette fonction va lire un octet en provenance de la carte Arduino. La valeur data_read ne va plus contenir "Z", mais la valeur envoyée par la carte Arduino, donnant un signal pour remplir de nouveau le petit buffer de l'Arduino avec de nouvelles données.

Voilà, voilà, j'espère que vous n'êtes pas trop :sick: :sick: :sick: :sick: J'ai essayé de faire léger!! :badsmile:

Dans un prochain post, je vous parlerai de l'assemblage de mes modules Parallel Input Serial Output. Il y aura plus d'illustrations!!


Offline jacquesmomo

  • Il parait que je suis une
  • Legend
  • ******
  • Posts: 5239
  • Country: France fr
  • Karma: 232
  • Plus on rate, plus on a de chances de réussir !..
Reply #32 - 12 February 2017, 23:22:19
Voilà, voilà, j'espère que vous n'êtes pas trop :sick: :sick: :sick: :sick: J'ai essayé de faire léger!! :badsmile:


Mes add-ons sont là !

Offline Mars Bleu

  • Hero Member
  • *****
  • Posts: 513
  • Karma: 18
Reply #33 - 12 February 2017, 23:49:13
 JacquesMomo:
Quote

J'aime bien la Coccinelle, même quand elle est vaseuse! :)
Et aussi les smiley "hors forum" que tu nous déniches