Mise en oeuvre de la sation météo CV7

La sation météo CV7

Le CV7 est un capteur de vent à ultrason (anémomètre/girouette) moderne issu de la série CV3F. Il mesure aussi la température de l’air. Le capteur peut être exploité par un PC, utilisant une entrée normalisée NMEA 183.

Fil Nom
Fil Rouge + Alimentation
Fil Bleu - Alimentation
Fil Jaune + NMEA
Fil Vert - NMEA

Caractéristiques :

  • Sensibilité du module du vent : 0,25 nœud
  • Résolution : 0,1 nœud
  • Dynamique du module du vent : 0,25 à 80 nœuds
  • Sensibilité de la direction : +/- 1 degrés
  • Résolution : 1 degré
  • Gamme de température : -10 à 55°C
  • Liaison NMEA 183 : 4800 Bauds, Pas de parité, 1 Bit d’arrêt
  • Flux constant cadencé à environ 500 millisecondes (2 fois par seconde)
  • Fin de phrase NMEA 183 : CR,LF

Exemples de phrases NMEA 183 :

$IIMWV,225.0,R,000.0,N,A*38
$WIXDR,C,022.0,C,,*52
$PLCJ,5801,5F01,AA,4253,3341
$PLCJEA870,6D98,C500,0056,AC,

Phrase Vent : $IIMWV,226.0,R,000.0,N,A*0B

  • 226.0 : Direction du vent en degrés
  • R : Référence
  • 000.0 : Vitesse du vent
  • N : Unité de mesure du vent (N = Nœud)
  • A : Etat du CV7 (A : Mesures correctes / V : Mesures incorrectes)
  • 0B : Checksum

Phrase température du vent : $WIXDR,C,022.0,C,,*52

  • 022.0 : Valeur avec une décimale
  • C : Unité °C
  • 52 : Checksum

Lire : LCJ-Capteurs_ManuelUtilisateur-CV7_FRA_web.pdf

Prise en charge sous Raspberry Pi

Brancher le CV7 sur un port USB de la raspberry

Vérifier la détection du périphérique :

$ dmesg
...

$ lsusb
...

Modifier les droits d’accès manuellement au fichier device :

$ sudo chmod 666 /dev/ttyUSB0

Pour obtenir une modification permanente et automatique, éditer le fichier :

$ sudo vim /etc/udev/rules.d/51.ttyusb.rules

Ajouter :

# adaptateur FT232RL pour CV7 USB
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", NAME="ttyUSB1", SYMLINK+="cv7"

Sauvegarder et quitter avec :wq

La norme NMEA 0183

La norme NMEA 0183 est une spécification pour la communication entre équipements marins, dont les équipements GPS. Elle est définie et contrôlée par la National Marine Electronics Association (NMEA), association américaine de fabricants d’appareils électroniques maritimes.

La norme 0183 utilise une simple communication série pour transmettre une “phrase” (sentence) à un ou plusieurs écoutants. Une trame NMEA utilise tous les caractères ASCII.

Exemple : Waypoint Arrival Alarm

$GPAAM,A,A,0.10,N,WPTNME*32

Le type d’équipement est défini par les deux caractères qui suivent le $. Le type de trame est défini par les caractères suivants jusqu’à la virgule.

Chaque trame a sa syntaxe propre, mais selon le cas elles peuvent ou doivent se terminer, après le *, par un octet formant une somme de contrôle (checksum) qui permet de détecter une erreur dans la transmission.

La somme de contrôle à la fin de chaque phrase est le OU EXCLUSIF (XOR) de tous les octets de la phrase à l’exclusion du premier caractère ($) et jusqu’au caractère avant l’étoile (*). Cf. C implementation of checksum generation.

Site officielle www.nmea.org

Réception de phrases NMEA 0183

Actuellement lorsque l’on raccorde la station météo CV7 USB à un ordinateur, cela revient à gérer un port série virtuel.

On peut tester la réception de phrases NMEA 0183 avec la commande screen (Ctrl-a k pour sortir) ou avec picocom (Ctrl-a Ctrl-x pour sortir) :

$ screen /dev/ttyUSB0 4800
$IIMWV,226.0,R,000.00,N,A*0B
$WIXDR,C,036.5,C,,*52
$PLCJ,75FA,7DEA,03,,,,6D7C,837E
$PLCJEAC90,D35D,3F00,0056,FF
...

$ picocom -b 4800 /dev/ttyUSB0
picocom v1.4

port is        : /dev/ttyUSB
flowcontrol    : none
baudrate is    : 4800
parity is      : none
databits are   : 8
escape is      : C-a
noinit is      : no
noreset is     : no
nolock is      : no
send_cmd is    : ascii_xfr -s -v -l10
receive_cmd is : rz -vv

Terminal ready
$IIMWV,226.0,R,000.00,N,A*0B
$WIXDR,C,036.5,C,,*52
$PLCJ,75FA,7DEA,03,,,,6D7C,837E
$PLCJEAC90,D35D,3F00,0056,FF
...

Remarque : 4800 est le débit de la “ligne” en bits/s.

Ou avec le logiciel cutecom.

Traitement

Les phrases NMEA 183 devront être réceptionnées puis traitées par l’application. Celles-ci étant composées de caractères ASCII, cela revient à traitert des chaînes de caractères.

Attention : la tâche d’acquisition de phrases NMEA 183 doit s’assurer de fournir des phrases “complètes”. C’est-à-dire qu’elles commencent par le délimiteur ‘$’ et se terminent par un ‘\r’.

Sous Qt, la classe QString contient de nombreuses méthodes pour manipuler des chaînes de caractère. Il vous faudra consulter très souvent sa documentation : doc.qt.io/qt-4.8/qstring.html.

Voici quelques exemples d’utilisation de la classe QString dans le cadre d’un traitement d’une trame NMEA 0183 :

#include <QDebug>
#include <QString>

int verifierPhrase(const QString &phrase, const QString &typeTrame)
{
    QString checksum;
    const QString debutTrame = "$";    
    const QString debutChecksum = "*";
    QString type = phrase.mid(3, 3);
    
    // phrase vide ?
    if(phrase.length() != 0)
    {    
        // est-ce une phrase NMEA 0183 ?
        if(phrase.startsWith(debutTrame))
        {    
            // est-ce la bonne phrase ?
            if(type == typeTrame)
            {
                // y-a-t-il un checksum ?
                if(phrase.contains(debutChecksum))
                {
                    checksum = phrase.section(debutChecksum, 1, 1);
                    qDebug() << "checksum : 0x" << checksum;
                }
                else
                {
                    qDebug() << "Attention : il n'y a pas de checksum dans cette phrase !";
                    return 0;
                }
            }
            else
            {
                qDebug() << "Erreur : ce n'est pas une trame MWV !";
                return 1;
            }
        }
        else
        {
            qDebug() << "Erreur : ce n'est pas une trame NMEA 0183 !";
            return 2;
        }
    }
    else
    {        
        qDebug() << "Erreur : phrase vide !";
        return 3;
    }
    return 0; // Ok
}

int main(int argc, char *argv[])
{
    QString phrase;
    
    // Faire des essais :
    phrase = "$IIMWV,226.0,R,000.0,N,A*0B";
    //phrase = "";
    //phrase = "IIMWV,226.0,R,000.0,N,A*0B";
    //phrase = "$IIMWA,226.0,R,000.0,N,A*0B";
    //phrase = "$IIMWV,226.0,R,000.0,N,A";
    
    verifierPhrase(phrase, "MWV");
   
    return 0;
}

L’autre utilisation fréquente de la classe QString est la conversion des données numériques (int, double, …) :

~ {.cpp} #include #include

int main(int argc, char argv[]) { / Exemple de base */ int i = 2; double d = 3.14;

// Du numérique -> chaîne de caractères
QString entier = QString::number(i); // int -> QString
QString reel = QString::number(d); // double -> QString

qDebug() << "L'entier i : " << entier;
qDebug() << "Le réel d : " << reel;

// De chaîne de caractères -> numérique
entier = "100";
reel = "2.71828";
i = entier.toInt(); // QString -> int
d = reel.toDouble(); // QString -> double

qDebug() << "L'entier i : " << i;
qDebug() << "Le réel d : " << d;

/* Exemple appliqué */
QString phrase = "$WIXDR,C,036.5,C,,*52";
QString strTemperature;
QString strUnite;
double temperature;

// découpe la trame avec le délimiteur ',' et récupère le deuxième champ
strTemperature = phrase.section(',', 2, 2); 
// découpe une chaine à partir d'une position et un nombre de caractères
temperature = strTemperature.toDouble();

qDebug() << "Température : " << temperature;

strUnite = phrase.section(',', 3, 3); 
if(strUnite.contains("C"))
    strTemperature = QString::number(temperature) + " °Celcius";

qDebug() << "Température : " << strTemperature;    

return 0;

} ~ {.cpp}

On doit maintenant s’assurer d’extraire des phrases “complètes” des données reçues du capteur. Une phrase “complète” commence par le délimiteur ‘$’ et se terminent par un ‘\r’ :

void extrairePhrases(const QString &donneesRecues)
{
    QString phrase = "";
    bool debutPhrase = false;
    bool finPhrase = false;

    for(int i = 0; i < donneesRecues.length(); i++)
    {
        // début d'une phrase NMEA183 ?
        if(donneesRecues.at(i) == '$')
        {
            debutPhrase = true;
        }
        // fin d'une phrase NMEA183 ?
        if(donneesRecues.at(i) == '\r' && debutPhrase == true)
        {
            finPhrase = true;
        }
        if(debutPhrase == true && finPhrase == false)
        {
            phrase += donneesRecues.at(i);
        }
        if(finPhrase == true)
        {
            qDebug() << "phrase NMEA183 : " << phrase;

            // on continue ...
            phrase.clear();
            finPhrase = false;
            debutPhrase = false;
        }
    }
}

int main()
{
    QString donneesRecues;
    
    // Simule des données reçues
    donneesRecues = "MWV,226.0,R,000.0,N,A*0B\r$WIXDR,C,036.5,C,,*52\r$IIMWV,226.0,R,000.0,N,A*0B\r$WIXDR,C,036.5";

    extrairePhrases(donneesRecues);
    
    return 0;
}

Code source : test-mo-nmea.zip

Code source : test-mo-port.zip

Retour au sommaire