![]() |
Testeur NMEA WIFI et RS422 |
janvier 2020 |
Depuis quelques temps j'avais envie de réaliser un petit outil qui m'aide à identifier
les éventuelles problèmes sur la config nmea du voilier, vérifier la portée du wifi... Je me suis donc fixé un petit cahier des charges :
Ce type de projet est inenvisageable si l'on ne dispose pas d'un moyen de saisie pour paramétrer l'outil, j'ai cherché sur internet et je n'ai pas trouvé de librairie qui permette ceci avec un écran affichant suffisamment de ligne et qui consomme très peu. J'ai donc été obligé de concevoir deux objets logiciel pour créer une interface utilisateur simple sur écran Oled à l'aide d'un encodeur rotatif. Le code développé est donc disponible sur ce site. Je le mets à dispo dans le domaine public et j'espère que cela permettra à des amateurs comme moi la réalisation de nouveaux projets.
Le schéma est simple, l'utilisation de petits blocs "LEGO" facilite grandement le montage. L'interface utilisateur est composée
du bouton marche/arret (pas de bouton reset, il faut jouer avec ce bouton M/A), d'une LED qui indique la mise sous tension
(En cas d'utilisation sur batterie, il vaut mieux couper l'alim dès que l'on utilise plus le testeur).
Lorsque que l'on n'utilise pas le testeur il faut prendre l'habitude de placer les cellules solaire vers l'éclairage maxi. afin
de recharger la batterie. Les cellules solaires sont câblées en parallèle et fournisse du 5v. L'écran OLED est directement lié au
déplacement du bouton rotatif, la saisie de texte (mot de passe wifi, adresse IP, port, vitesse RS422 est possible grace à l'affichage
de trois types de caractères (Majuscules, minuscules et car. spéciaux).
L'interface vers le NMEA0183 du bateau est réalisée par un circuit bien pratique, celui-ci n'est pas relié à l'interface série classique
du microprocesseur mais vers une interface créée par logiciel grace à la librairie SoftwareSerial (grand merci à l'auteur), ceci libère
l'interface usb de l'esp (Wemos D1 mini pro) pour la programmation de nouvelles versions du testeur. J'ai rencontré des problèmes lorsque
j'utilisais l'usb pour modifier le programme alors que je recevais des trames nmea (RS422) sur cette même liaison, avec cette interface
logiciele ce problème est réglé !
Le Wemos D1 mini pro qui est le coeur du système regroupe pas mal de connexion et il faut jouer des coudes pour tout loger dans le petit boitier. J'ai précisé sur le schéma les deux notations les "Dx" gravés sur le boitier et les "Gpioxx" correspondant que l'on retrouve dans le code.
Cette petite vidéo montre l'avancée du projet. |
|
![]() |
Nous trouvons donc dans les entrailles du testeur : |
Tout montage commence par des tests en volant. On voit ici que le câblage est très simple, la difficulté était plutôt côté programmation !
Liste des éléments necessaires et les mots clés utilisablent pour trouver son bonheur sur GG :
Le Wemos D1 mini pro : "Wemos D1 mini pro" Prendre l'antenne wifi et son câble avec.
L'interface RS422 : "RS422 module MAX490"
L'écran LCD OLED : "OLED 128X64 I2C SSD1306 blanc" Attention ne pas prendre les modèles bi-color (voir remarques).
Le chargeur gestion alim TP4056 : "tp4056" prendre un modèle installé sur un petit circuit imprimé.
Le bouton rotatif sans fin : "Encodeur rotatif KY-040 arduino"
Les 3 cellules solaires 5v : "cellule solaire 5v 53x30"
La batterie : "batterie 18650 6800ma"
Le support de la batterie : "support batterie 18650 6800ma" Attention en trouver un en plastique rigide (plus chere) !
Le bouton M/A : "Interrupteur à 2 positions SPDT Mini Micro"
La LED : "mini led rouge avec support"
Un total de 25 euros max, mais 15 euros si vous pistez les bonnes affaires...
Pour le boitier, il faut l'imprimer et pour le logiciel, je vais le mettre à dispo...
Il faut aussi prévoir quelques bouts de fils, pas trop gros, il n'y a pas beaucoup de place dans le boitier et de la gaine thermo-retractable.
Le boitier brut à la sortie de l'impression, le couvercle viendra appuyer sur les circuits pour les maintenir au fond. Il faut agrandir un peu les deux trous des entrées usb, j'ai prévu un peu petit !
Chaque élément a sa petite place bien à lui ! Pour le moment c'est cool on a l'impression d'avoir de la place mais ca va se compliquer...
Sur le couvercle il n'y a que la led qui a son support. Perso j'ai trouvé une led avec sa petite résistance de limitation (160 ohms) directement intégrée sous une gaine thermo.
Les cellules solaires sont câblées en parallèle serrées sans excès de fil afin de pouvoir loger au dos du boitier.
Les deux premières cellules solaires sont insérées sous le boitier dans les glissières.
Les fils sont passés dans le trou avant l'insertion de la troisième cellule. La troisième cellule solaire est ensuite insérée sous le boitier dans les glissières.
Les fils des cellules sont regroupés à l'intérieur du boitier avec deux fils connectés vers In+ et In- du TP4056. Les deux fils de la batterie sont eux connectés sur B+ et B- du TP4056.
Perso j'ai préparé les différents éléments en ne laissant que le minimum de longueur de fil et en protégeant les connexions avec de la gaine thermo. Si le circuit est livré avec des broches, je les ai coupées de façon à garder le maximum de place dans le boitier.
Si vous choisissez d'utiliser l'antenne wifi externe vous devez déplacer le petit strap indiqué sur la photo (plus facile à dire qu’à faire !) . Vous trouverez des vidéos sur YouTube qui montrent comment faire cela proprement !
Avec tous mes essais de câblage j'ai fini pas casser les petits ergots de fixation de l'écran, comme on le voit sur l'image j'ai simplement utilisé des petites vis de maintien après avoir fait des avant trous. Si vous réalisez le montage vous ne devriez pas rencontrer ce problème, pour vous en principe, ce sera bon du premier coup !
Lors de la commande du matériel il faut veiller à prendre des écrans blanc, les modèles bi-colors sont jaune sur le haut, puis il y a un trou dans les lignes de l'écran et ensuite on passe à la deuxième couleur, c'est pas top avec ce montage je me suis fait avoir avec une commande et on voit des exemples sur certaines photos comme sur celle ci-dessous !
Le câblage est ici terminé, comme on peut le voir les fils sont finalement nombreux et il faut serrer tout ce petit monde dans la partie réservée sous l'écran.
Le couvercle va aussi compliquer les choses puisqu'il a pour mission de bloquer les circuits en fond de boitier, il faut bien regarder le meilleur circuit pour faire passer les fils.
Comme on le voit ici l'écran bi-colors ce n’est pas top, je l'ai changé dès que j'en ai reçu des nouveaux tout blanc !
Le code V2 (la V1 reste en téléchargement) du testeur, j'espère pouvoir encore rapidement ajouter de nouvelles fonctionnalités...
Pour fonctionner ce code doit être accompagné dans le même répertoire des deux objets "sai_oled" et "menu_oled" présentés dans la partie électronique du site.
Vous devez donc avoir dans le même répertoire :
menu_oled.h et menu_oled.cpp (une mise à jour de menu_oled.cpp est disponible depuis le 20/02/2020)
sais_oled.h et sai_oled.cpp
et le fichier ci-dessous.
L'IDE Arduino doit avoir été mis à jour avec les librairies mentionnées dans le programme ci-dessous et la liste "type de carte" doit comporter celles de l'Esp-8266.
Attention pour la librairie de l'écran Oled "SSD1306Wire.h", c'est celle-ci
qu'il faut télécharger sur le site "github", elle est différente de celle qui est disponible dans l'environnement "Arduino".
On la reconnait au "w" de Wire qui doit dans notre cas être une majuscule. (Merci à Guy, pour cette précision qui peut éviter pas mal de galère !).
Merci également à tous les contributeurs(46) de ce petit package qui contient plusieurs librairies. Vous devez charger le zip complet comme montré sur
l'image ci-contre.
// Testeur NMEA V2
// Reception de trames nmea soit par wifi soit par liaison nmea0183 RS422
// Emission trame profondeur et position GPS sur liaison RS422 nmea0183
// V2 Ajout de la lecture d'un fichier kml
// Castoo
// Janvier 2020
#include "Wire.h"
#include "SSD1306Wire.h"
#include "sai_oled.h"
#include "menu_oled.h"
#include "ESP8266WiFi.h"
#include "ESP_EEPROM.h"
#include "SoftwareSerial.h"
SSD1306Wire display(0x3c, 4, 5); // I2C sur un esp8266 donc gpio 4 > SDA & gpio 5 > SCL
menu_oled mes_menus_oled(16, 13, 0, display); // init saisie (Gpio vclk, Gpio vdt, Gpio vsw et nom de l'objet LCD)
sai_oled mon_sai_oled(16, 13, 0, display); // init saisie (Gpio vclk, Gpio vdt, Gpio vsw et nom de l'objet LCD)
uint8_t PinSW = 0; // Gpio x sur sortie SW du selecteur rotatif
#define sl_rx 14 // Reception interface serie logiciele
#define sl_tx 12 // Emission interface serie logiciele
SoftwareSerial swSer; // Déclaration du port serie RS422 (port logiciel)
int B_menu; // Choix selectionné dans menu
char ch_result[25]; // Chaine résultat du menu
const int nb_ch_menuA = 5; // choix du menu Principal
const String ch_menuA[] = {"Modif. parametres", "Lecture flux NMEA", "Compteur trames NMEA", "Affiche trames RMC", "Emission trame RMC RS422"};
String _ch_menuA = * ch_menuA;
const int nb_ch_menuB = 5; // choix du menu Parametres
const String ch_menuB[] = {"Type de liaison active", "Parametres Wifi", "Parametres série", "Afficher Parametres", "Retour"};
String _ch_menuB = * ch_menuB;
const int nb_ch_menuC = 4; // choix du menu Parametres type de réseau
const String ch_menuC[] = {"Réseau Wifi", "Réseau Série", "Lecture kml sur usb", "Retour"};
String _ch_menuC = * ch_menuC;
const int nb_ch_menuD = 5; // choix du menu Parametres wifi
const String ch_menuD[] = {"Selection du réseau Wifi", "Adresse IP réseau Wifi", "Port réseau Wifi", "Connexion Wifi", "Retour"};
String _ch_menuD = * ch_menuD;
const int nb_ch_menuE = 5; // choix du menu Parametres Liaison série RS422
const String ch_menuE[] = {"Vitesse 4800", "Vitesse 38400", "Vitesse choisie", "Schéma de connexion", "Retour"};
String _ch_menuE = * ch_menuE;
String trame_nmea = ""; // Trame nmea
// gestion de l'eeprom
struct s_memo_rom {
char verif_data_w[9];
char verif_data_s[3];
char wifi_ssid[50];
char wifi_passe[25];
char wifi_ip[15];
uint16_t wifi_port;
uint16_t rs422_vit;
bool v_wifi;
char position[51][26]; // Max 50 paires latitudes et longitudes
uint8_t nb_position; // Nb de position mémorisées
} memo_rom, memo_ram;
int adr_deb_memo_rom = 0;
WiFiClient client;
// Initialise des valeurs standard pour le LCD
void prep_aff_LCD(uint8_t typ_pol) {
display.clear();
if(typ_pol == 16) display.setFont(ArialMT_Plain_16);
else if(typ_pol == 24) display.setFont(ArialMT_Plain_16);
else display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_LEFT);
}
// Attente donnees en lecture
void oups() {
int delai = 1000;
prep_aff_LCD(10);
display.drawString(0, 10, ">> Oups Rien en ligne ! <<");
display.drawString(0, 30, ">> Un peu de patience.. <<");
display.drawString(0, 50, "> Si vraiment rien >reset<");
display.display();
while(delai > 0){delai--; if (!(digitalRead(PinSW))) return;}
}
// Mise à jour des verifications memo eeprom
void maj_verif(char v1 = 'z', char v2 = 'z', char v3 = 'z', char v4 = 'z', char v5 = 'z') {
if( v1 != 'z' ){ memo_ram.verif_data_w[0]='1'; memo_ram.verif_data_w[1]='x';}
if( v2 != 'z' ){ memo_ram.verif_data_w[2]='2'; memo_ram.verif_data_w[3]='x';}
if( v3 != 'z' ){ memo_ram.verif_data_w[4]='3'; memo_ram.verif_data_w[5]='x';}
if( v4 != 'z' ){ memo_ram.verif_data_w[6]='4'; memo_ram.verif_data_w[7]='x';}
if( v5 != 'z' ){ memo_ram.verif_data_s[0]='1'; memo_ram.verif_data_s[1]='x';}
memo_ram.verif_data_w[8]=0;
memo_ram.verif_data_s[2]=0;
}
// Affichage des parametres
void aff_param(){
prep_aff_LCD(10);
display.drawString(0, 0, "Parametres :");
display.drawString(0, 10, memo_ram.wifi_ssid);
display.drawString(0, 20, memo_ram.wifi_passe);
display.drawString(0, 30, memo_ram.wifi_ip);
display.drawString(0, 40, String(memo_ram.wifi_port));
display.drawString(0, 50, String(memo_ram.rs422_vit));
display.display();
delay(200);
while(digitalRead(PinSW)){delay(10);}
}
// Emission de trame
void envoi_rmc_rs422(){
int chksum = 0;
float profondeur = 1;
String s_profond;
String s_chksum;
String trame_SDDBT = "$SDDBT,7.8,f,?,M,1.3,F*";
String trame_RMC = "$GPRMC,123030.00,A,?,0.009,,270219,,,A*";
String p1_trame_SDDBT = trame_SDDBT.substring(0, 13);
String p2_trame_SDDBT = trame_SDDBT.substring(14);
String p1_trame_RMC = trame_RMC.substring(0, 19);
String p2_trame_RMC = trame_RMC.substring(20);
String trame_em_SDDBT;
String trame_em_RMC;
delay(400);
for(int i = 0; i <= 100; i++){
profondeur += 0.1;
s_profond = String(profondeur);
s_profond = s_profond.substring(0, s_profond.length()-1);
trame_em_SDDBT = p1_trame_SDDBT + s_profond + p2_trame_SDDBT;
trame_em_RMC = p1_trame_RMC + memo_ram.position[i%memo_ram.nb_position] + p2_trame_RMC;
chksum = 0;
for(int i = 1; i < trame_em_SDDBT.length()-1; i++) chksum ^= trame_em_SDDBT[i];
s_chksum = String(chksum, HEX);
if(s_chksum.length() < 2) s_chksum = '0' + s_chksum;
s_chksum.toUpperCase();
trame_em_SDDBT += s_chksum;
swSer.println(trame_em_SDDBT);
prep_aff_LCD(10);
display.drawString(10, 0, "<< Emission RS422 >>");
display.drawString(10, 10, "Trame DBT N° : " + String(i));
display.drawStringMaxWidth(0, 25, 120, String(trame_em_SDDBT));
display.display();
delay(200);
chksum = 0;
for(int i = 1; i < trame_em_RMC.length()-1; i++)chksum ^= trame_em_RMC[i];
s_chksum = String(chksum, HEX);
if(s_chksum.length() < 2) s_chksum = '0' + s_chksum;
s_chksum.toUpperCase();
trame_em_RMC += s_chksum;
swSer.println(trame_em_RMC);
prep_aff_LCD(10);
display.drawString(10, 0, "<< Emission RS422 >>");
display.drawString(10, 10, "Trame RMC N° : " + String(i));
display.drawStringMaxWidth(0, 25, 120, String(trame_em_RMC));
display.display();
if(!digitalRead(PinSW)) break;
delay(200);
}
}
// Affichage trames RMC
void aff_rmc() {
char heur_format[12];
int cpt = 0;
char* tbuf[12] = { 0 };
char buf[trame_nmea.length() + 1];
trame_nmea.toCharArray(buf, trame_nmea.length());
char *valeur = strtok(buf, ",");
while (valeur != NULL) {
if (cpt < 12) { tbuf[cpt++] = valeur; } else { break; }
valeur = strtok(NULL, ",");
}
prep_aff_LCD(16);
display.drawString(30, 0, "$GPRMC");
cpt = 1;
while (cpt < 7){
if(cpt == 1){
heur_format[0] = tbuf[cpt][0];heur_format[1] = tbuf[cpt][1];heur_format[2] = ':';
heur_format[3] = tbuf[cpt][2];heur_format[4] = tbuf[cpt][3];heur_format[5] = ':';
heur_format[6] = tbuf[cpt][4];heur_format[7] = tbuf[cpt][5];heur_format[8] = ' ';
heur_format[9] = 'U';heur_format[10] = 'T';heur_format[11] = 'C';heur_format[12] = 0;
display.drawString(15, 15, String(heur_format));
}else if(cpt == 3){
display.drawString(10, 30, String(tbuf[cpt]));
display.drawString(110, 30, String(tbuf[cpt+1]));
}else if(cpt == 5){
display.drawString(10, 45, String(tbuf[cpt]));
display.drawString(110, 45, String(tbuf[cpt+1]));
}
cpt++;
}
display.display();
}
// Affichage trames RMC WIFI
void aff_nmea_rmc() {
String type_trame = ""; // Type de trame nmea
unsigned long timeout;
while(true){
timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) oups();
if (!(digitalRead(PinSW))) return;
}
while (client.available()) {
char ch = static_cast(client.read());
if (ch == '$' || ch == '!'){
type_trame = trame_nmea.substring(0,6);
if(String(type_trame) == "$GPRMC") aff_rmc();
trame_nmea = "";
}
trame_nmea += ch;
if (!(digitalRead(PinSW))) return;
}
}
}
// Affichage trames RMC liaison série
void aff_nmea_rmc_serie() {
String type_trame = ""; // Type de trame nmea
unsigned long timeout;
while(true){
timeout = millis();
while (swSer.available() == 0) {
if (millis() - timeout > 5000) oups();
if (!(digitalRead(PinSW))) return;
}
while (swSer.available()) {
char ch = static_cast(swSer.read());
if (ch == '$' || ch == '!'){
type_trame = trame_nmea.substring(0,6);
if(String(type_trame) == "$GPRMC") aff_rmc();
trame_nmea = "";
}
trame_nmea += ch;
if (!(digitalRead(PinSW))) return;
}
}
}
// Attente donnees en lecture
void aff_trame() {
prep_aff_LCD(24);
display.drawString(0, 0, trame_nmea.substring(0,6));
display.setFont(ArialMT_Plain_10);
display.drawStringMaxWidth(0, 25, 120, String(trame_nmea));
display.display();
trame_nmea = "";
}
// Lecture trames NMEA WIFI
void li_trame_nmea() {
unsigned long timeout;
while(true){
timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) oups();
if (!(digitalRead(PinSW))) return;
}
while (client.available()) {
char ch = static_cast(client.read());
if (ch == '$' || ch == '!') aff_trame();
trame_nmea += ch;
if (!(digitalRead(PinSW))) return;
}
}
}
// Lecture trames NMEA sur liaison serie
void li_trame_nmea_serie() {
unsigned long timeout;
while(true){
timeout = millis();
while (swSer.available() == 0) {
if (millis() - timeout > 5000) oups();
if (!(digitalRead(PinSW))) return;
}
while( swSer.available() ){
char ch = static_cast(swSer.read());
if (ch == '$' || ch == '!') aff_trame();
trame_nmea += ch;
if (!(digitalRead(PinSW))) return;
}
if (!(digitalRead(PinSW))) return;
}
}
// Comptage des trames NMEA par type sur liaison wifi
void cpt_trame_nmea() {
String type_trame = ""; // Type de trame nmea
int cpt[10];
String trame[10];
uint8_t max_type = 12;
uint8_t nb_type = 0;
unsigned long timeout;
while(true){
timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) oups();
if (!(digitalRead(PinSW))) return;
}
while (client.available()) {
char ch = static_cast(client.read());
if (ch == '$' || ch == '!'){
if(trame_nmea.length() > 6){
type_trame = trame_nmea.substring(1,6);
for (uint8_t i = 0; i <= max_type; i++){
if( String(type_trame) == String(trame[i])){
cpt[i] ++;
break;
}else if( i >= nb_type ){
trame[i] = String(type_trame);
cpt[i] = 1;
nb_type ++;
break;
}
}
prep_aff_LCD(10);
for (uint8_t i = 0; i < nb_type; i++){
if (i<6){
display.drawString(0, 10*i, trame[i]);
snprintf (ch_result, 12, " %d", cpt[i]);
display.drawString(38, 10*i, ch_result);
}else{
display.drawString(65, 10*(i-6), String(trame[i]));
snprintf (ch_result, 12, " %d", cpt[i]);
display.drawString(102, 10*(i-6), ch_result);
}
}
display.display();
trame_nmea = "";
type_trame = "";
}
}
trame_nmea += ch;
if (!(digitalRead(PinSW))) return;
}
}
}
// Comptage des trames NMEA par type sur liaison serie
void cpt_trame_nmea_serie(){
String type_trame = ""; // Type de trame nmea
int cpt[10];
String trame[10];
uint8_t max_type = 12;
uint8_t nb_type = 0;
unsigned long timeout;
while(true){
timeout = millis();
while (swSer.available() == 0) {
if (millis() - timeout > 5000) oups();
if (!(digitalRead(PinSW))) return;
}
while (swSer.available()) {
char ch = static_cast(swSer.read());
if (ch == '$' || ch == '!'){
if(trame_nmea.length() > 6){
type_trame = trame_nmea.substring(1,6);
for (uint8_t i = 0; i <= max_type; i++){
if( String(type_trame) == String(trame[i])){
cpt[i] ++;
break;
}else if( i >= nb_type ){
trame[i] = String(type_trame);
cpt[i] = 1;
nb_type ++;
break;
}
}
prep_aff_LCD(10);
for (uint8_t i = 0; i < nb_type; i++){
if (i<6){
display.drawString(0, 10*i, trame[i]);
snprintf (ch_result, 12, " %d", cpt[i]);
display.drawString(38, 10*i, ch_result);
}else{
display.drawString(65, 10*(i-6), String(trame[i]));
snprintf (ch_result, 12, " %d", cpt[i]);
display.drawString(102, 10*(i-6), ch_result);
}
}
display.display();
trame_nmea = "";
type_trame = "";
}
}
trame_nmea += ch;
if (!(digitalRead(PinSW))) return;
}
}
}
// Découverte des réseaux wifi accessibles
void decouverte_wifi() {
String ssid_wifi[5];
WiFi.disconnect(true);
prep_aff_LCD(10);
display.drawString(2, 0, "RAZ wifi en cours...");
display.drawString(2, 10, ">>> Patience ... <<<");
display.drawString(8, 30, "Attention !");
display.drawString(0, 40, "Max 5 réseaux affichés");
display.display();
delay(400);
uint8_t n = WiFi.scanNetworks();
if( n == 0 ){
prep_aff_LCD(16);
display.drawString(0, 20, "Aucun réseau Wifi en vue !");
display.drawString(0, 40, "Relancez la recherche...");
display.display();
while(digitalRead(PinSW)){delay(10);}
}else{
if(n > 5) n = 5;
uint8_t plus_23 = 0;
uint8_t nb_val = 0;
for (uint8_t i = 0; i < n+1; ++i) {
if(WiFi.SSID(i).length() <= 48 && WiFi.SSID(i).length() > 1){
ssid_wifi[i-plus_23] = WiFi.SSID(i);
nb_val ++;
}else{
plus_23++;
i++;
}
delay(20);
}
B_menu = 0;
B_menu = mes_menus_oled.init(nb_val-1, *ssid_wifi);
ssid_wifi[B_menu+1].toCharArray(memo_ram.wifi_ssid, ssid_wifi[B_menu+1].length()+1);
if(memo_ram.wifi_ssid != ""){
prep_aff_LCD(10);
display.drawString(0, 10, "Saisir le mot de passe");
display.drawString(0, 20, "du réseau :");
display.drawString(0, 35, memo_ram.wifi_ssid);
display.display();
delay(200);
while(digitalRead(PinSW)){delay(10);}
String ch_result;
mon_sai_oled.init(); // Initialisation ecran OLED pour saisie
ch_result = mon_sai_oled.aff_menu_Alpha();
if(ch_result.length() > 2){
ch_result.toCharArray(memo_ram.wifi_passe, ch_result.length()+1);
maj_verif('x','x');
ecriture_eeprom();
}
}
}
}
// Ecriture des positions fichier kml dans eeprom
void ecriture_eeprom_positions(){
if(memo_ram.nb_position > 0){
EEPROM.begin(sizeof(s_memo_rom));
Serial.println("Ecriture EEPROM");
Serial.println("Qte Memmoire EEPROM utilisee : " + String(sizeof(s_memo_rom)));
delay(500);
memo_rom.nb_position = memo_ram.nb_position;
Serial.println("Nb de position memorisee : " + String(memo_rom.nb_position));
for(uint8_t i = 0; i <= memo_ram.nb_position; i++){
strcpy(memo_rom.position[i], memo_ram.position[i]);
delay(5);
Serial.println(memo_rom.position[i]);
}
EEPROM.put(adr_deb_memo_rom, memo_rom);
delay(2000);
bool ok = EEPROM.commit();
delay(2000);
EEPROM.end();
delay(500);
}
}
// Lecture de fichier kml et traduction en coordonées nmea trames RMC
void lire_kml() {
memo_ram.nb_position = 0;
char char_lu; // Caractère lu sur interface série/usb
unsigned long timeout; // Pour gérer attente début de transmission
String longitude = "";
String latitude = "";
String profondeur = ""; // Au moment du dev. l'info profondeur est toujours égale à 0
char v_long ='E'; // Est par défaut passe à Ouest si la longitude est négative
bool lect_lat = false; // Passe à vrai quand la longitude est ok
bool lect_prof = false; // Passe à vrai quand la latitude est ok
uint8_t pos_pt_long = 0; // Repérage du point décimal en longitude
uint8_t pos_pt_lat = 0; // Repérage du point décimal en latitude
bool fin_reception = false; // On est arrivé à la fin des mesures
String v_tmp; // Tempo pour calcul lat et long
prep_aff_LCD(24);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(64, 10, "En attente...");
display.display();
while(!fin_reception){
// Attente début d'émission...
timeout = millis();
while (Serial.available() == 0){
if (millis() - timeout > 5000){
oups();
prep_aff_LCD(24);
display.setTextAlignment(TEXT_ALIGN_CENTER);
}
if (!(digitalRead(PinSW))) break;
}
// Attente début de la liste des coordonées
String ligne = "";
while(ligne.indexOf(" 0){
char_lu = Serial.read();
if(char_lu == '>'){
ligne = "";
} else {
ligne += char_lu;
}
delay(2);
}
}
// Supression des "rdinate>\l\t\t\t\t"
int i = 0;
while(i < 13){
if(Serial.available() > 0){
char_lu = Serial.read();
i++;
delay(2);
}
}
// Lecture des coordonées
while(true){
longitude = "";
latitude = "";
profondeur = "";
v_long ='E'; // Est
lect_lat = false;
lect_prof = false;
// Lecture d'une coordonée
while (true){
if(Serial.available() > 0){
char_lu = Serial.read();
delay(2);
if(! lect_lat){
// lecture Longitude
if(char_lu == '-') v_long ='W'; // Ouest
else{
if(char_lu == ','){
lect_lat = true;
}else{
if(char_lu != '.'){
if(char_lu != '\t') longitude += char_lu;
} else pos_pt_long = longitude.length();
}
}
}else if(lect_lat){
// Lecture Latitude
if(char_lu == ','){
lect_prof = true;
}else{
if(char_lu != '.'){
latitude += char_lu;
}else pos_pt_lat = latitude.length();
}
}else if(lect_prof){
// Lecture de la profondeur
profondeur += char_lu;
if(char_lu == ' '){
break;
}
}
if (!(digitalRead(PinSW))) break;
if (char_lu == ' ') break;
}
}
// Si chaine de longitude vide on sort
if(longitude.length() < 3){ fin_reception = true; memo_ram.nb_position--; break;}
// Mise en forme des coordonées au format nmea avec gestion du point décimal
if(pos_pt_long == 1){
v_tmp = "0." + longitude.substring(1, 7);
v_tmp = String( v_tmp.toFloat() * 60 );
if(v_tmp.toInt() < 10)
longitude = "00" + longitude.substring(0,1) + 0 + String( longitude.substring(1, 7).toInt() * 6 );
else
longitude = "00" + longitude.substring(0,1) + String( longitude.substring(1, 7).toInt() * 6 );
longitude = longitude.substring(0,5) + "." + longitude.substring(5, longitude.length());
}else if(pos_pt_long == 2){
v_tmp = "0." + longitude.substring(2, 7);
v_tmp = String( v_tmp.toFloat() * 60 );
if(v_tmp.toInt() < 10)
longitude = "0" + longitude.substring(0, 2) + 0 + String( longitude.substring(2, 7).toInt() * 6 );
else
longitude = "0" + longitude.substring(0, 2) + String( longitude.substring(2, 7).toInt() * 6 );
longitude = longitude.substring(0,5) + "." + longitude.substring(5, longitude.length());
}
if(pos_pt_lat == 1){
v_tmp = "0." + latitude.substring(1, 7);
v_tmp = String( v_tmp.toFloat() * 60 );
if(v_tmp.toInt() < 10)
latitude = latitude.substring(0, 1) + 0 + String( latitude.substring(2, 7).toInt() * 6 );
else
latitude = latitude.substring(0, 1) + String( latitude.substring(2, 7).toInt() * 6 );
latitude = latitude.substring(0,4) + "." + latitude.substring(4, latitude.length());
}else if(pos_pt_lat == 2){
v_tmp = "0." + latitude.substring(2, 7);
v_tmp = String( v_tmp.toFloat() * 60 );
if(v_tmp.toInt() < 10)
latitude = latitude.substring(0, 2) + 0 + String( latitude.substring(2, 7).toInt() * 6 );
else
latitude = latitude.substring(0, 2) + String( latitude.substring(2, 7).toInt() * 6 );
latitude = latitude.substring(0,4) + "." + latitude.substring(4, latitude.length());
}
latitude = latitude + ",N," + longitude + "," + v_long;
latitude.toCharArray(memo_ram.position[memo_ram.nb_position], latitude.length()+2);
// Affichage flèches sur LCD Oled pour montrer qu'on avance
display.clear();
if (memo_ram.nb_position % 2 == 1 ) display.drawString(64, 20, "<<<<---"); else display.drawString(64, 20, "---<<<<");
display.display();
memo_ram.nb_position ++;
if(!(Serial.available())) break;
if(fin_reception) break;
}
if(Serial.available() == 0 || memo_ram.nb_position >= 50 || fin_reception) break;
}
// On va à la fin du fichier
while(Serial.available()){ Serial.read(); }
}
// Menu Parametre wifi
void param_wifi() {
B_menu = 0;
B_menu = mes_menus_oled.init(nb_ch_menuD, _ch_menuD);
delay(400);
if (B_menu == 0) decouverte_wifi();
else if (B_menu == 1){
String ch_result;
mon_sai_oled.init();
ch_result = mon_sai_oled.aff_menu_Alpha();
if(ch_result.length() > 2){
ch_result.toCharArray(memo_ram.wifi_ip, ch_result.length()+1);
maj_verif('z','z','x');
ecriture_eeprom();
}
}
else if (B_menu == 2){
String ch_result;
mon_sai_oled.init();
ch_result = mon_sai_oled.aff_menu_Alpha();
if(ch_result.length() > 2){
memo_ram.wifi_port = ch_result.toDouble();
maj_verif('z','z','z','x');
ecriture_eeprom();
}
}
else if (B_menu == 3){
if( String(memo_ram.verif_data_w) == "1x2x3x4x"){
cnx_wifi();
}else{
prep_aff_LCD(10);
display.drawString(0, 0, "*********************");
display.drawString(0, 10, "Parametres incomplets");
display.drawString(0, 20, "*********************");
display.display();
}
}
delay(400);
}
// Menu Parametre serie
void param_serie() {
B_menu = 0;
B_menu = mes_menus_oled.init(nb_ch_menuE, _ch_menuE);
delay(400);
if (B_menu == 0) memo_ram.rs422_vit = 4800;
else if (B_menu == 1) memo_ram.rs422_vit = 38400;
else if (B_menu == 2){
String ch_result;
mon_sai_oled.init();
ch_result = mon_sai_oled.aff_menu_Alpha();
if(ch_result.length() > 2){
memo_ram.rs422_vit = ch_result.toDouble();
}
}
else if (B_menu == 3){
prep_aff_LCD(10);
display.drawString(0, 0, "SCHEMA LIAISON NMEA");
display.drawString(0, 10, " NMEA RS422");
display.drawString(0, 20, "Out(+)------------A");
display.drawString(0, 30, "Out(-)------------B");
display.drawString(0, 40, "IN(-)----------------Z");
display.drawString(0, 50, "IN(+)----------------Y");
display.display();
delay(400);
while(digitalRead(PinSW)){delay(10);}
}
if (B_menu != 4){
maj_verif('z','z','z','z','x');
ecriture_eeprom();
swSer.begin(memo_ram.rs422_vit, SWSERIAL_8N1, sl_rx, sl_tx, false, 256);
}
}
// Menu Parametre type réseau
void param_type_reseau() {
B_menu = 0;
B_menu = mes_menus_oled.init(nb_ch_menuC, _ch_menuC);
delay(400);
if (B_menu == 0) memo_ram.v_wifi = true;
else if (B_menu == 1) memo_ram.v_wifi = false;
else if (B_menu == 2){
lire_kml();
// Affichage du nombre de positions trouvées
prep_aff_LCD(24);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(64, 10, String(memo_ram.nb_position) + " positions");
display.drawString(64, 30, "trouvées");
display.display();
while(digitalRead(PinSW)){delay(10);}
if(memo_ram.nb_position > 0){
prep_aff_LCD(24);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(64, 10, "Sauvegarde");
display.drawString(64, 30, "en cours");
display.display();
ecriture_eeprom_positions();
}
}
if (B_menu < 2) ecriture_eeprom();
delay(400);
}
// Menu Parametre
void parametre() {
B_menu = 0;
B_menu = mes_menus_oled.init(nb_ch_menuB, _ch_menuB);
delay(400);
if (B_menu == 0) param_type_reseau();
else if (B_menu == 1) param_wifi();
else if (B_menu == 2) param_serie();
else if (B_menu == 3) aff_param();
delay(200);
}
// Affiche compte-rendu enregistrement eeprom
void aff_eeprom(bool lect = true, bool memo_ok = false){
delay(200);
prep_aff_LCD(10);
if(lect) display.drawString(20, 0, "Lecture EEPROM"); else display.drawString(20, 0, "Ecriture EEPROM");
display.drawString(0, 10, "ssid : " + String(memo_ram.wifi_ssid));
display.drawString(0, 20, "@ IP : " + String(memo_ram.wifi_ip));
display.drawString(0, 30, "Port : " + String(memo_ram.wifi_port));
if(memo_ok) display.drawString(40, 30, "< MEMO OK >");
display.drawString(0, 40, "Vit S: " + String(memo_ram.rs422_vit));
if(memo_ram.v_wifi) display.drawString(0, 50, "Liaison Wifi Active"); else display.drawString(0, 50, "Liaison RS422 Active");
display.display();
delay(4000);
}
// Lecture eeprom
void lecture_eeprom(){
EEPROM.begin(sizeof(s_memo_rom));
delay(500);
EEPROM.get(adr_deb_memo_rom, memo_rom);
delay(500);
strcpy(memo_ram.verif_data_w, memo_rom.verif_data_w);
strcpy(memo_ram.verif_data_s, memo_rom.verif_data_s);
if(memo_ram.verif_data_w[0] == '1' && memo_ram.verif_data_w[2] == '2'){
if(memo_ram.verif_data_w[1] == 'x') strcpy(memo_ram.wifi_ssid, memo_rom.wifi_ssid); else strcpy(memo_ram.wifi_ssid, "");
if(memo_ram.verif_data_w[3] == 'x') strcpy(memo_ram.wifi_passe, memo_rom.wifi_passe); else strcpy(memo_ram.wifi_passe, "");
if(memo_ram.verif_data_w[5] == 'x') strcpy(memo_ram.wifi_ip, memo_rom.wifi_ip); else strcpy(memo_ram.wifi_ip, "");
if(memo_ram.verif_data_w[7] == 'x') memo_ram.wifi_port = memo_rom.wifi_port; else memo_ram.wifi_port = 0;
}
if(memo_ram.verif_data_s[0] == '1'){
if(memo_ram.verif_data_s[1] == 'x') memo_ram.rs422_vit = memo_rom.rs422_vit; else memo_ram.rs422_vit = 0;
}
Serial.println();
Serial.println("Lecture des positions kml de l'EEPROM");
memo_ram.nb_position = memo_rom.nb_position;
Serial.println("Nb Pos : " + String(memo_ram.nb_position));
Serial.println();
for(uint8_t i = 0; i <= memo_ram.nb_position; i++){
strcpy(memo_ram.position[i], memo_rom.position[i]);
delay(5);
Serial.println(memo_rom.position[i]);
}
memo_ram.v_wifi = memo_rom.v_wifi;
EEPROM.end();
delay(500);
aff_eeprom();
}
// Ecriture eeprom
void ecriture_eeprom(){
EEPROM.begin(sizeof(s_memo_rom));
delay(500);
if(memo_ram.wifi_ssid != ""){ strcpy(memo_rom.wifi_ssid, memo_ram.wifi_ssid); memo_rom.verif_data_w[0] = '1'; memo_rom.verif_data_w[1] = 'x';}
if(memo_ram.wifi_passe != ""){ strcpy(memo_rom.wifi_passe, memo_ram.wifi_passe); memo_rom.verif_data_w[2] = '2';; memo_rom.verif_data_w[3] = 'x';}
if(memo_ram.wifi_ip != ""){ strcpy(memo_rom.wifi_ip, memo_ram.wifi_ip); memo_rom.verif_data_w[4] = '3'; memo_rom.verif_data_w[5] = 'x';}
if(memo_ram.wifi_port != 0){ memo_rom.wifi_port = memo_ram.wifi_port; memo_rom.verif_data_w[6] = '4'; memo_rom.verif_data_w[7] = 'x';}else{memo_rom.wifi_port = 0;}
if(memo_ram.rs422_vit != 0){ memo_rom.rs422_vit = memo_ram.rs422_vit; memo_rom.verif_data_s[0] = '1'; memo_rom.verif_data_s[1] = 'x';}else{memo_rom.rs422_vit = 0;}
memo_rom.v_wifi = memo_ram.v_wifi;
EEPROM.put(adr_deb_memo_rom, memo_rom);
delay(500);
bool ok = EEPROM.commit();
delay(500);
aff_eeprom(false, ok);
EEPROM.end();
delay(500);
}
// connexion wifi
void cnx_wifi() {
int progress;
int cpt=1;
prep_aff_LCD(10);
display.drawString(0, 0, "Tentative de connexion sur :");
display.drawString(10, 10, memo_ram.wifi_ssid);
display.display();
WiFi.disconnect(true);
delay(400);
WiFi.begin(memo_ram.wifi_ssid, memo_ram.wifi_passe);
while (WiFi.status() != WL_CONNECTED){
progress = (cpt / 5) % 100;
display.drawProgressBar(0, 30, 120, 10, progress);
display.display();
cpt++;
if(cpt>=501) break;
delay(10);
}
display.setTextAlignment(TEXT_ALIGN_LEFT);
if (!client.connect(memo_ram.wifi_ip, memo_ram.wifi_port)) {
prep_aff_LCD(10);
display.drawString(0, 0, "Problème de connexion !");
display.drawString(0, 10, "Parametres :");
display.drawString(0, 20, memo_ram.wifi_ssid);
display.drawString(0, 30, memo_ram.wifi_passe);
display.drawString(0, 40, memo_ram.wifi_ip);
display.drawString(0, 50, String(memo_ram.wifi_port));
}else{
display.drawString(0, 52, ">> Connexion réussie <<");
}
display.display();
delay(200);
while(digitalRead(PinSW)){delay(10);}
}
// Initialisation
void setup() {
Serial.begin(4800); // Interface utilisée pour chargement des fichiers de coordonnées kml
pinMode(PinSW, INPUT);
display.init();
display.flipScreenVertically();
prep_aff_LCD(10);
Wire.begin();
// Init saisie OLED
mon_sai_oled.init();
mon_sai_oled.sortie_deb(false);
lecture_eeprom();
WiFi.mode(WIFI_STA);
if( String(memo_ram.verif_data_w) == "1x2x3x4x") cnx_wifi();
if( String(memo_ram.verif_data_s) == "1x") swSer.begin(memo_ram.rs422_vit, SWSERIAL_8N1, sl_rx, sl_tx, false, 256);
}
// Boucle principale
void loop() {
delay(200);
B_menu = 0;
B_menu = mes_menus_oled.init(nb_ch_menuA, _ch_menuA);
delay(300);
if (B_menu == 0) parametre();
else if (B_menu == 1) {if(memo_ram.v_wifi) li_trame_nmea(); else li_trame_nmea_serie(); delay(400); while(digitalRead(PinSW)){delay(10);}}
else if (B_menu == 2) {if(memo_ram.v_wifi) cpt_trame_nmea(); else cpt_trame_nmea_serie(); delay(400);}
else if (B_menu == 3) {if(memo_ram.v_wifi) aff_nmea_rmc(); else aff_nmea_rmc_serie(); delay(400); while(digitalRead(PinSW)){delay(10);}}
else if (B_menu == 4) {envoi_rmc_rs422();}
delay(400);
}
Téléchargement du fichier testeur nmea V1 (vous pouvez le nommer comme vous voulez) : lect_nmea.ino
ou
Téléchargement du fichier testeur nmea V2 (vous pouvez le nommer comme vous voulez) : lect_nmea_v2.ino
Vous pouvez commencer avec la V2 ou la V1 ou inversement, pour changer de version, il suffit de la téléverser dans le testeur nmea avec l'IDE Arduino.
Téléchargement du fichier : sai_oled.h (à installer dans le même rep que lect_nmea)
Téléchargement du fichier : sai_oled.cpp (à installer dans le même rep que lect_nmea)
Téléchargement du fichier : menu_oled.h (à installer dans le même rep que lect_nmea)
Téléchargement du fichier : menu_oled.cpp (à installer dans le même rep que lect_nmea)
J’ai réalisé ce montage pour mes propres besoins, mais si quelqu’un est intéressé par ce projet, je vais mettre à dispo sur ce site les informations nécessaires
à sa réalisation par domaine :
- Les infos pour l'impression du boitier, du couvercle et du bouton dans "Imp 3D"
- Les infos sur les fonctionnalités dans "Navigation"
La premiere évolution V2 :
- Lecture de fichier kml et émission des trames sur la liaison RS422"
Le projet étant évolutif, je vais numéroter les versions (impressions 3D, logiciel).