Le port Série

Créer un objet serial

Pour commencer il faut créer un objet (structure) serial et lui affecter un port physique:
id = serial('COM4');
Le port ainsi créé sera accessible par son identificateur id.
ici nous n'avons précisé que le nom du port, tous les autres paramètres sont pris par défaut. Pour consulter leurs valeurs, taper la commande get(id)

    ByteOrder = littleEndian
    BytesAvailable = 0
    BytesAvailableFcn =
    BytesAvailableFcnCount = 48
    BytesAvailableFcnMode = terminator
    BytesToOutput = 0
    ErrorFcn =
    InputBufferSize = 512
    Name = Serial-COM4
    ObjectVisibility = on
    OutputBufferSize = 512
    OutputEmptyFcn =
    RecordDetail = compact
    RecordMode = overwrite
    RecordName = record.txt
    RecordStatus = off
    Status = closed
    Tag =
    Timeout = 10
    TimerFcn =
    TimerPeriod = 1
    TransferStatus = idle
    Type = serial
    UserData = []
    ValuesReceived = 0
    ValuesSent = 0

    SERIAL specific properties:
    BaudRate = 9600
    BreakInterruptFcn =
    DataBits = 8
    DataTerminalReady = on
    FlowControl = none
    Parity = none
    PinStatus = [1x1 struct]
    PinStatusFcn =
    Port = COM4
    ReadAsyncMode = continuous
    RequestToSend = on
    StopBits = 1
    Terminator = LF

pour modifier la valeur d'une propriété, on peut le faire de deux façons:

Ouvrir un port

Avant d'utiliser un objet serial, il faut l'ouvrir: fopen(id)
sp = serial('COM4');
fopen(sp)


Fermer un port

Quand on a fini avec un port, il faut le fermer: fclose(id)

sp = serial('COM4');
fopen(sp)
...
...
...

fclose(sp)

Supprimer l'objet serial

Après la fermeture d'un port à l'aide la fonction fclose(), l'objet port série continue d'exister. On peut le réouvrir et le réutiliser. Quand on a vraiment terminé avec un port,  il faut le supprimer.

sp = serial('COM4');
fopen(sp);
...
...
...
fclose(sp);
delete(sp);
clear sp;

Pour éviter les problèmes

Il arrive (en cas de plantage de programme par exemple) qu'un objet serial ne soit pas supprimé. A chaque ré-exécution du programme, un nouvel objet serial est crée sur le même port physique. Le problème est que après, le plantage d'un programme, ses variables n'existe plus, mais les objets de type serial continuent d'exister. On se retrouve avec des objets serial dont les identificateurs n'existent plus, comment faire pour les supprimer ?
la fonction instrfind() permet de déterminer tous les identificateurs des objets liés à un port. Ceci permet de les supprimer.

id = instrfind('port', 'COM4') ;
delete(id);
clear id;

un bon programme contiendra les deux blocs suivants:
id = instrfind('port', 'COM4') ;
delete(id);
clear id;
sp = serial('COM4');
fopen(sp);
...
...
...
fclose(sp);
delete(sp);
clear sp;


Transmettre une Chaîne

fwrite(sp, 'ABç');
les octets transmis sont 65, 66, 231 ce qui signifie que Matlab utilise le codage par défaut de la machine utilisée. Pour moi, c'est  windows-1252 qui une adapatation pour Windows de  iso8859_1 souvent appelé Latin-1. Matlab ne produit pas de message d'erreur si on transmet des caractères qui ne peuvent être codés en latin-1 comme ω ou µ, les codes sont tout simplement tronqués et on reçoit des caractères erronés.

Pour déterminer le codage par défaut de votre machine: feature('DefaultCharacterSet')

Encodage d'une chaîne
Pour effectuer le codage d'une chaîne dans un code donné on peut utiliser la fonction:
bytes = unicode2native(unicodestr, encoding)
exemple:

B = unicode2native('AçéB', 'latin-1')  
B =   65  231  233   66

B = unicode2native('AçéB','utf-8')
B =   65  195  167  195  169   66
on remarque avec le codage utf-8,  ç est codé par deux octets 195, 167 ainsi que é qui est codé par 195, 169

pour avoir les code en hexadécimal, il suffit de demander à Matlab d'afficher en hexadécimal:
format hex
B = unicode2native('AçéB','utf-8')

B =   41   c3   a7   c3   a9   42
pour revenir au format décimal, il suffit de taper format

En résumé si on veut transmettre la chaîne AçéB en utilisant le codage utf-8, il suffit de transmettre la suite d'octet [65  195  167  195  169   66]. Il va sans dire que la machine qui reçoit doit les décoder conformément au tableau utf-8
fwrite(sp, [65  195  167  195  169   66], 'uint8')


Recevoir une Chaîne

Les informations sont reçues octet par octet. A priori on n'a aucun moyen de savoir si les octets reçus font partie d'une chaîne, d'un entier ou d'un réel.

Pour lire une suite de 6 octets dans le port ouvert avec l'identificateur rx,
inp = fread(rx, 6);

On obtient quelque chose de ce genre:
inp =
    65
   195
   167
   195
   169
    66

Si on utilise l'instruction whos, on peut vérifier que par défaut, Matlab stocke les éléments dans la variable inp au format double (32 bits = 8 octet pour chaque élément)

  Name      Size            Bytes    Class     Attributes
  inp          6x1                48     double             
 
Il est conseillé d'utiliser le format uint8 (unsigned integer 8) pour les octet reçu. On va aussi transposer (' ) le vecteur reçu afin d'obtenir un vecteur ligne.
inp = uint8(fread(rx, 6))';
on obtient:
inp =
   65  195  167  195  169   66

si on utilise l'instruction whos, on vérifie que chaque élément de inp est un byte et non un double:
  Name      Size            Bytes       Class          Attributes
  inp          1x6                 6          uint8              

Décodage de la chaîne
Si on sait que les octets reçus sont issus de l'encodage utf-8 d'une chaîne, on peut reconstituer cette chaîne à l'aide de la fonction  native2unicode(bytes, encoding)

s = native2unicode(inp, 'utf-8')
On obtient:
s = AçéB


Un bon moyen pour faire des essais, est de créer une paire de ports virtuels connectés entre eux à l'aide d'un logiciel du genre VSPD ou similaire. Ce qu'on écrit dans un port on le reçoit dans l'autre.
Voici un petit programme qui transmet une chaine sur COM4 et la récupère sur COM5
txp ='COM4';
id = instrfind('port',txp); 
delete(id);
clear id;
tx = serial(txp);
fopen(tx);

rxp ='COM5';
id = instrfind('port',rxp); 
delete(id);
clear id;
rx = serial(rxp);
fopen(rx);

st = 'Comment ça va ?';
fwrite(tx, st, 'uint8');
inp = uint8(fread(rx, length(st)))';
sr = char(inp)

fclose(tx);
delete(tx);
clear tx;

fclose(rx);
delete(rx);
clear rx;

l'exemple ci-dessous est le même mais utilise le codage utf-8
txp ='COM4';
id = instrfind('port',txp); 
delete(id);
clear id;
tx = serial(txp);
fopen(tx);

rxp ='COM5';
id = instrfind('port',rxp); 
delete(id);
clear id;
rx = serial(rxp);
fopen(rx);

st = 'ça va merci';
bt = unicode2native(st,'utf-8')
fwrite(tx, bt, 'uint8');

br = uint8(fread(rx, length(bt)))';
sr = native2unicode(br,'utf-8')

fclose(tx);
delete(tx);
clear tx;

fclose(rx);
delete(rx);
clear rx;


Transmettre un Entier

Un entier peut être constitué de 1, 2, 4 ou 8 octets ( et même plus). il peut être signé ou non signé.
Avec un octet, on peut représenter 256 entiers non signés allant de 0 à 255
Avec un octet, on peut représenter 256 entiers signés allant de 0 à +127 et de -1 à -128

Pour transmettre un entier N non signé de 32 bits (uint32) dans le port ouvert avec l'identificateur tx
fwrite(tx, N, 'uint32');

A titre d'exemple, examinons le nombre 888666444, si on le représente en binaire, on voit qu'il faut quatre octets pour le représenter.
dec2bin(888666444)
ans = 110100111101111111100101001100

typecast(uint32(888666444),'uint8')
ans  =    76  249  247   52

donc 888666444 =  110100 11110111 11111001 01001100
                               =      52             247             249            76

l'instruction fwrite(tx, 888666444, 'uint32'); va transmettre les 4 octets 76  249  247   52. La transmission commence par l'octet de poids le plus faible car la valeur par défaut, de la propriété ByteOrder de l'objet serial est égale à littleEndian. Si on veut commencer par le poids le plus fort, il faut donner la valeur bigEndian à cette propriété.
set(tx, 'ByteOrder', 'bigEndian')


Recevoir un Entier

La réception de nombres entiers de plusieurs octets chacun doit se faire avec soin. Il faut veiller à une bonne synchronisation entre l'émetteur et le récepteur. Par exemple, si l'émetteur transmet des entiers de 32bits (4 octets)
[23 45 66 100][56 34 2 22][100 50 44 123][200 100 23 8]...
si pour une raison ou pour une autre le récepteur rate le premier octet et commence à lire à partir du 2ème octet:
[45 66 100 56][34 2 22 100][50 44 123200] ...
Il est évident qu'après reconstitution, les entiers reçus seront différents des entiers transmis.

Ce problème mis à part, les choses sont assez simples. On lit n octets à l'aide de la fonction fread(), et on reconstitue l'entier à l'aide de la fonction typecast()

rb = uint8(fread(rx, 2))
ri = typecast(rb,'uint16')


rb = uint8(fread(rx, 4))
ri = typecast(rb,'uint32')


Si l'émetteur utilise l'ordre bigEndian, il faut retourner l'entier reconstitué à l'aide de la fonction swapbytes()
ri = swapbytes(ri)


Voici un exemple qui transmet un nombre 32 bits sur COM4 et le récupère sur COM5
txp ='COM4';
id = instrfind('port',txp); 
delete(id);
clear id;
tx = serial(txp);
fopen(tx);

rxp ='COM5';
id = instrfind('port',rxp); 
delete(id);
clear id;
rx = serial(rxp);
fopen(rx);

ti = 888666444;
fwrite(tx,ti,'uint32');
rb = fread(rx, 4)
ri = typecast(uint8(rb),'uint32')

%=== fermer le port et supprimer l'objet serial
fclose(tx);
delete(tx);
clear tx;

fclose(rx);
delete(rx);
clear rx;


Transmettre et Recevoir un Réel


Tout ce qu'on a dit sur les entiers s'applique au réels.
Les réels de type single (simple précision) sont codé sur 4 octets chacun. Les double (double précision) sont codé sur 8 octet chacun. Par défaut, Matlab code tous les nombres avec le type double. Pour changer de type, il faut utiliser les fonction de conversion single(), double(),  int16 (), int32(), int64(), int8(), intmax(), intmin(),  uint16(), uint32(), uint64

n = 123;
m = single(234);
k = uint8(24);
whos
  Name      Size            Bytes     Class     Attributes
  k             1x1                 1        uint8              
  m            1x1                 4       single             
  n             1x1                 8       double  

Voici un exemple qui transmet un nombre réel simple précision sur COM4 et le récupère sur COM5. Pour un réel double précision, il suffit de remplace single par double et 4 par 8
txp ='COM4';
id = instrfind('port',txp); 
delete(id);
clear id;
tx = serial(txp);
fopen(tx);

rxp ='COM5';
id = instrfind('port',rxp); 
delete(id);
clear id;
rx = serial(rxp);
fopen(rx);

ti = 345.6789;
fwrite(tx,ti,'single');
rb = uint8(fread(rx, 4))
ri = typecast(rb,'single')

%=== fermer le port et supprimer l'objet serial
fclose(tx);
delete(tx);
clear tx;

fclose(rx);
delete(rx);
clear rx;



Lecture Ecriture Périodique

L'objet serial est associé à un timer facile à utiliser. Il est géré par les propriétés TimerPeriod et TimerFcn
TimerPeriod: Sa valeur détermine la période du timer en seconde
TimerFcn: Sa valeur détermine la fonction qui sera exécutée chaque fois que la période est écoulée

Le programme suivant transmet un entier (16bits) toutes les secondes. Ce programme utilise getAvailableComPort qui permet de déterminer les ports COM disponibles sur la machine. Cette fonction doit être placée dans le même dossier.
function serial_tx_timer()
    tx = [];
    fh = figure('Units', 'Normalized', 'Position',[0.6 0.6 0.15 0.3],...
        'MenuBar','none', 'NumberTitle', 'off','name', 'Déconnecté');
    set(0, 'DefaultUicontrolUnits',  'normalized') ;
    h = 0.12;  % hauteur des boutons
    w = 0.6;   % largeur des boutons
    x = 0.2;
    y = 0.05;
    g = h + 0.05;
    poph = uicontrol(fh, 'style','popup','position',[x y+4*g w h],...
        'back', 'c', 'fore', 'r','call',@pop);
    avcp = getAvailableComPort;
    set(poph, 'string', avcp);
    stst = uicontrol(fh, 'style', 'push', 'pos',[x y+g w h],...
       'str', 'Stop ', 'call', @start_stop);
    visu = uicontrol(fh, 'style', 'Edit', 'pos',[x y+2*g w h]);
    exit = uicontrol(fh, 'style', 'push', 'pos',[x y w h], 'str', 'EXIT', 'call', @quitter);
    set(fh,'CloseRequestFcn',@quitter);

    function pop(obj,event)
        s = get(obj,'string');
        i = get(obj, 'value');
        com = s{i};
        id = instrfind('port',com); 
        delete(id);
        clear id;
        tx = serial(com);
        set(tx, 'TimerPeriod',1);
        set(tx, 'Timerfcn', @transmit)
        fopen(tx);
        set(fh, 'name', com);
    end

    function transmit(tx, event)
        m = uint16(fix(rand * 1023));
        set(visu,'string',m);
        fwrite(tx, m, 'uint16');
    end

    function start_stop(obj,event)
        if get(stst,'string') == 'Stop '
            set(tx, 'Timerfcn', '')
            set(stst, 'string','Start')
        else
            set(tx, 'Timerfcn', @transmit)
            set(stst, 'string','Stop ')
        end
    end

    function quitter(obj,event)
        delete(gcf);
        try
            if tx.status =='open'
                 fclose(tx);
            end
            delete(tx);
            clear tx;
        catch me
            disp(me.message)
        end
    end

end

La fonction getAvailableComPort()
function lCOM_Port = getAvailableComPort()
% function lCOM_Port = getAvailableComPort()
% Return a Cell Array of COM port names available on your computer

try
    s=serial('IMPOSSIBLE_NAME_ON_PORT');fopen(s);
catch
    lErrMsg = lasterr;
end
delete(s);
%Start of the COM available port
lIndex1 = findstr(lErrMsg,'COM');
%End of COM available port
lIndex2 = findstr(lErrMsg,'Use')-3;

lComStr = lErrMsg(lIndex1:lIndex2);

%Parse the resulting string
lIndexDot = findstr(lComStr,',');

% If no Port are available
if isempty(lIndex1)
    lCOM_Port{1}='';
    return;
end

% If only one Port is available
if isempty(lIndexDot)
    lCOM_Port{1}=lComStr;
    return;
end

lCOM_Port{1} = lComStr(1:lIndexDot(1)-1);

for i=1:numel(lIndexDot)+1
    % First One
    if (i==1)
        lCOM_Port{1,1} = lComStr(1:lIndexDot(i)-1);
    % Last One
    elseif (i==numel(lIndexDot)+1)
        lCOM_Port{i,1} = lComStr(lIndexDot(i-1)+2:end);      
    % Others
    else
        lCOM_Port{i,1} = lComStr(lIndexDot(i-1)+2:lIndexDot(i)-1);
    end
end   

Le programme ci-dessous lit un entier 16bits toutes les 1/2 secondes
function serial_rx_timer()
    rx = [];
    fh = figure('Units', 'Normalized', 'Position',[0.44 0.6 0.15 0.3],...
        'MenuBar','none', 'NumberTitle', 'off','name', 'Déconnecté');
    set(0, 'DefaultUicontrolUnits',  'normalized') ;
    h = 0.12;  % hauteur des boutons
    w = 0.6;   % largeur des boutons
    x = 0.2;
    y = 0.05;
    g = h + 0.05;
    poph = uicontrol(fh, 'style','popup','position',[x y+4*g w h],...
        'back', 'c', 'fore', 'r','call',@pop);
    avcp = getAvailableComPort;
    set(poph, 'string', avcp);
    volt = uicontrol(fh, 'style', 'edit', 'pos',[x y+g w h]);
    nadc = uicontrol(fh, 'style', 'edit', 'pos',[x y+2*g w h]);
    exit = uicontrol(fh, 'style', 'push', 'pos',[x y w h], 'str', 'EXIT', 'call', @quitter);
    set(fh,'CloseRequestFcn',@quitter);

    function pop(obj,event)
        s = get(obj,'string');
        i = get(obj, 'value');
        com = s{i};
        id = instrfind('port',com); 
        delete(id);
        clear id;
        rx = serial(com);
        set(rx, 'TimerPeriod',0.5);
        set(rx, 'Timerfcn', @lire)
        fopen(rx);
        set(fh, 'name', com);
    end

    function lire(rx, event)
        a = get(rx,'BytesAvailable');
        if a > 1
            rb = fread(rx, 2);
            ri = typecast(uint8(rb),'uint16');
            set(nadc,'string', ri);
            v = single(ri) * 5 / 1024;
            vs = num2str(v,'%4.2f');
            set(volt, 'string', vs);
        end  
    end

    function quitter(obj,event)
        delete(gcf);
        try
            if rx.status =='open'
                 fclose(rx);
            end
            delete(rx);
            clear rx;
        catch me
            disp(me.message)
        end
    end

end



Lecture sur événement

L'objet serial peut déclencher un événement suite à la réception d'un nombre définit d'octet. Ceci est géré par les propriétés suivantes:

BytesAvailableFcnMode: Sa valeur précise la nature des donnée à compter. Elle peut prendre les valeurs {terminator} ou byte. Le plus simple et de choisir 'byte' pour compter les octets reçus. Si on choisit 'terminator' ( valeur par défaut), on va compter les lignes reçue puisque la valeur par défaut de la propriété Terminator est 'LF'

BytesAvailableFcnCoun: Sa valeur précise le nombre de données à compter avant  d'appeller la fonction précisée par la propriété ci-dessous

BytesAvailableFcn: Non de la fonction à appeler quand le compte est bon. Son nom doit être précisé parle caractère @. Si on veut passer des paramètre à la fonction il faut utiliser la syntaxe suivante {@fonction x y z } ou {@fonction, x,  y, z}

Le programme ci-dessous lit un entier 16bits chaque fois que le port à reçu 2 octets.
function serial_rx_onevent()
    rx = [];
    fh = figure('Units', 'Normalized', 'Position',[0.44 0.6 0.15 0.3],...
        'MenuBar','none', 'NumberTitle', 'off','name', 'Déconnecté');
    set(0, 'DefaultUicontrolUnits',  'normalized') ;
    h = 0.12;  % hauteur des boutons
    w = 0.6;   % largeur des boutons
    x = 0.2;
    y = 0.05;
    g = h + 0.05;
    poph = uicontrol(fh, 'style','popup','position',[x y+4*g w h],...
        'back', 'c', 'fore', 'r','call',@pop);
    avcp = getAvailableComPort;
    set(poph, 'string', avcp);
    volt = uicontrol(fh, 'style', 'edit', 'pos',[x y+g w h]);
    nadc = uicontrol(fh, 'style', 'edit', 'pos',[x y+2*g w h]);
    exit = uicontrol(fh, 'style', 'push', 'pos',[x y w h], 'str', 'EXIT', 'call', @quitter);
    set(fh,'CloseRequestFcn',@quitter);

    function pop(obj,event)
        s = get(obj,'string');
        i = get(obj, 'value');
        com = s{i};
        id = instrfind('port',com); 
        delete(id);
        clear id;
        rx = serial(com);
        fopen(rx);
        set(fh, 'name', com);
        rx.BytesAvailableFcnMode = 'byte';
        rx.BytesAvailableFcnCoun = 2;
        rx.BytesAvailableFcn = @lire;
    end

    function lire(rx, event)
        rb = fread(rx, 2);
        ri = typecast(uint8(rb),'uint16');
        set(nadc,'string', ri);
        v = single(ri) * 5 / 1024;
        vs = num2str(v,'%4.2f');
        set(volt, 'string', vs);
    end

    function quitter(obj,event)
        delete(fh);
        try
            if strcmp(rx.status,'open')
                 fclose(rx);
            end
            delete(rx);
            clear rx;
        catch me
            disp('Tentative de fermer un port inexistant')
        end
    end
end


Un programme très utile

Le programme ci-dessous est très utile pour déboguer une liaison entre un PC et une carte microcontroleur type ARDUINO ou PIC
Il permet de transmettre du texte et des nombres introduit en décimal ou en Hexadécimal.
Les octets reçus sont affichés en Hexadécimal dans la fenêtre de gauche et en format texte dans la fenêtre de droite

function Serial_monitor()
    sp = serial('BIDON');
    lin = 1;
    col = 1;
    HF=400;
    WF=600;
    fh=figure('pos',[600 180 WF HF],'menubar','none',...
        'NumberTitle','off','CloseRequestFcn',@quitter);
    gap = 5;
    hpop = 30;
    wpop = 100;
    popy = HF-hpop-gap;
    avcp = getvalidports;
    %avcp = {'COM1','COM2','COM3','COM4','COM5','COM6','COM7','COM8','COM9','COM10',...
    %    'COM11','COM12','COM13','COM14','COM15','COM16','COM17','COM18','COM19'};
    COM_POP = uicontrol(fh, 'style','popup','position',[10 popy wpop hpop],...
        'back', 'c', 'fore', 'r','string',avcp,'call',@pop);

    he1=20;
    e1y = popy - he1 -gap;
    we1 = 370;
    e1 = uicontrol(fh, 'style', 'EDIT', 'Posit',[10 e1y we1 20],...
        'horizon','left','back',[0.8 1 0.8]);
   
    btx = uicontrol(fh, 'style', 'pushbutton', 'position',[10+we1+gap e1y 50 20],...
        'str', 'Tx', 'call', @transmit,'back',[0.8 0.8 0.4]);
    bCLR1 = uicontrol(fh, 'style', 'pushbutton', 'position',[10+we1+gap+70 e1y 50 20],...
        'str', 'CLEAR', 'call', @cleare1,'back',[0.8 0.8 0.4]);

    hlpmsg = {'Le champs de transmission permet de transmettre un mellange de texte et de nombres en décimal ou en Hexadécimal';...
        'Si on tape W 35 ABC ''123'' 37568 0x41A2 ''0x4253''';...
        'On transmet le caractère W, le nombre décimal 35, la chaine ABC,';...
        'la chaine 123, le nombre décimal 37568, le nombre hexadécimal 41A2,';...
        'et enfin la chaine 0x4253';...
        'Les nombres sont cassés en octets et transmis dans l''ordre Big Endian';'';...
        'Les caractères reçus sont affichés en hexadecimal dans la fenetre de gauche et en texte dans la fenetre de droite'};

    bhlp = uicontrol(fh, 'style', 'pushbutton', 'position',[10+wpop+10 popy+2*gap 50 20],...
        'str', 'Help', 'call', {@(hObject,callbackdata)msgbox(hlpmsg)},'back',[0.8 0.8 0.4]);
   
    hthex=300;
    thexy = e1y - hthex - gap;
    wthex = 430;
    thex = uicontrol(fh, 'style', 'TEXT', 'Posit',[10 thexy wthex hthex],...
        'horizon','left','fontname','Courier','foreg','r','fontsize',11);
   
    htchar=hthex;
    wtchar = WF-wthex-20;
    tchary = thexy;
    tchar = uicontrol(fh, 'style', 'TEXT', 'Posit',[10+wthex+gap tchary wtchar htchar],...
        'horizon','left','fontname','Courier','fontsize',11,'foreg','b');
   
    bCLR = uicontrol(fh, 'style', 'pushbutton', 'position',[100 5 50 20],...
        'str', 'CLEAR', 'call', @cleare,'back',[0.8 0.8 0.4]);

    bexit = uicontrol(fh, 'style', 'pushbutton', 'position',[10 5 40 20],...
        'str', 'EXIT', 'call', @quitter,'back',[0.8 0.8 0.4]);
    set(e1, 'string','Tapez ici les données à transmettre et cliquez sur le bouton Tx');
    set(thex, 'string','Les octets reçus sont affichés ici en Hexadécimal');
    set(tchar,'string','Les octets reçus sont affichés ici en format texte');
    set(fh, 'name', 'Aucun port Ouvert, Choisir un port dans la liste');
%============================================================================ 
    function pop(obj,event)
        s = get(obj,'string');
        i = get(obj, 'value');
        com = s{i};
        if exist('sp','var')
            %disp(['un handle est affécté à ' sp.Port])
            if isvalid(sp)
            %disp([sp.Port '  Valide'])
                if strcmp(sp.status,'open')
                     %disp([sp.Port '  Ouvert'])
                     fclose(sp);
                     %disp([sp.Port  '  Fermé']);
                else
                     %disp([sp.Port '  N''est pas Ouvert'])

                end
            disp(['>>>>>  Supression de  ' sp.Port])
            delete(sp);
            clear sp;
            end
        end
        sp = serial(com);
        sp.timeout = 5;
        sp.BytesAvailableFcnMode = 'byte';
        sp.BytesAvailableFcnCoun =1;
        sp.BytesAvailableFcn = @lire;
        fopen(sp);
        if sp.BytesAvailable > 0
            bidon = fread(rx,rx.BytesAvailable);
        end
        set(fh, 'name', [com '  9600']);
        if sp.Status
            disp(['>>>>>  ' com ' Ouvert']);
            set(e1,'string','');
        else
            disp([com ' Fermé']);
        end           
    end

    function lire(rx, event)
        rb = fread(rx, 1);
        sh = get(thex,'string');
        sh = [sh sprintf('%.2X ',rb)];
        set(thex, 'string', sh);
        viewchar(rb);
    end

%============================================================================ 
    function transmit(obj,event)
      if exist('sp','var') & isvalid(sp) & strcmp(sp.status,'open')
        s = get(e1,'string');
        while s
            [a, s]=strtok(s); %a=premiere chaine,  s=reste de la chiaine
            n = str2num(a);
            if isempty(n)
                if (length(a) > 2) & (a(1:2) == '0x' | a(1:2) == '0X')
                    a = a(3:length(a));
                    n = hex2dec(a);
                    n = swapbytes(uint32(n)); % à l'origine  n est un double
                    B = typecast(n, 'uint8'); % casser n en bytes
                    ix = B == 0;
                    B(ix) = [];
                else
                    if a(1) == ''''
                        a = a(2:length(a)-1);
                    end
                    B = uint8(a);
                end
            else 
                n = swapbytes(uint32(n)); % à l'origine  n est un double
                B = typecast(n, 'uint8'); % casser n en bytes
                ix = B == 0;
                B(ix) = [];
            end  
            fwrite(sp,B);
        end
      else
          set(e1,'str','Aucun port COMM ouvert, Veuillez choisir un port');
      end
    end


%=============================================================
    function viewchar(c)
        sc = get(tchar,'string');
        if c == 13
            c = 182;
        end
        if c == 10
            c = 167;
        end
        sc(lin, col) = c;
        set(tchar, 'string', sc);
        col = col + 1;
        if col == 17
            col = 1;
            lin = lin + 1;
        end
    end
%======================================================================
    function cleare(obj, event)
        set(thex, 'string','');
        set(tchar, 'string','');
        lin = 1;
        col = 1;
    end

    function cleare1(obj, event)
        set(e1, 'string','');
    end

%======================================================================

    function quitter(obj,event)
       
        if exist('sp','var')
            if isvalid(sp)
                if strcmp(sp.status,'open')
                     fclose(sp);
                     disp(['>>>>>  ' sp.Port  ' Fermé']);
                end
            delete(sp);
            clear sp;
            end
        end
        delete(gcf);
        disp('By')
    end

    function valid_ports = getvalidports()
        % function lCOM_Port = getAvailableComPort()
        % Return a Cell Array of COM port names available on your computer
       
        try
            s=serial('IMPOSSIBLE_NAME_ON_PORT');fopen(s);
        catch
            lErrMsg = lasterr;
        end
        delete(s);
        %Start of the COM available port
        lIndex1 = findstr(lErrMsg,'COM');
        %End of COM available port
        lIndex2 = findstr(lErrMsg,'Use')-3;
       
        lComStr = lErrMsg(lIndex1:lIndex2);
       
        %Parse the resulting string
        lIndexDot = findstr(lComStr,',');
       
        % If no Port are available
        if isempty(lIndex1)
            valid_ports{1}='';
            return;
        end
       
        % If only one Port is available
        if isempty(lIndexDot)
            valid_ports{1}=lComStr;
            return;
        end
       
        valid_ports{1} = lComStr(1:lIndexDot(1)-1);
       
        for i=1:numel(lIndexDot)+1
            % First One
            if (i==1)
                valid_ports{1,1} = lComStr(1:lIndexDot(i)-1);
                % Last One
            elseif (i==numel(lIndexDot)+1)
                valid_ports{i,1} = lComStr(lIndexDot(i-1)+2:end);
                % Others
            else
                valid_ports{i,1} = lComStr(lIndexDot(i-1)+2:lIndexDot(i)-1);
            end
        end
    end
end

.